Write your own
A graphic is function frame(t, $): given a time in seconds and the runtime, it returns the SVG of that frame. Pure, seekable, diffable, and something an agent can write and fix.
volito new promo-card # item.json + frame.js from a template
volito sheet ./promo-card # frames across time in one PNG: look at it
volito check ./promo-card # errors and warnings at every aspect
volito render ./promo-card --out promo.mov
volito api # the whole $ runtime
volito guide # how items and packs are laid outA single .js file works anywhere an item does too, so volito sheet my.js and volito render my.js just work. Give it a length with --duration.
A program
function frame(t, $) {
const p = $.data, k = $.k; // props, and the canvas scale (1 at 1080 px)
const tin = $.beat("in", 0.2);
return $.g($.enter(t, tin, { dy: 60 * k, ox: $.W / 2, oy: $.H / 2 }),
$.card($.W / 2 - 320 * k, $.H / 2 - 110 * k, 640 * k, 220 * k),
$.text($.case(p.title ?? "Hello"), { ...$.font("display"), size: 80 * k, x: $.W / 2, y: $.H / 2 - 44 * k, anchor: "middle" }));
}$ is the runtime: the canvas ($.W, $.H, $.k, $.dur), the brand ($.c for colours, $.font(role) for faces), drawing helpers ($.g, $.card, $.text, charts, icons, $.pattern), timing ($.beat, $.enter, springs and eases), and the libraries: the motion kit, scene3d for real 3D and worldMap / worldGlobe for maps. volito api prints all of it.
Rules that keep it renderable
- A pure function of
t: noDate, noMath.random(use$.hash), no timers, network or DOM. - Size everything by
$.k, so a 9:16 render and a 4K render both come out right. - Colours from
$.c(bg,fg,accent,accent2,surface,mute) and faces from$.font(role), so a DESIGN.md re-skins it. - Escape text you put into raw markup with
$.esc(). - Draw moving parts through the motion kit (Motion), and they take every move, mood and seed from the command line.
3D and maps
The 3D items are real geometry: meshes are lit, faces turned away are culled, everything is sorted far to near and projected through a perspective camera, then drawn as SVG paths. 3D text is extruded from the outlines of the bundled faces, so a logo turns in the brand's own type.
function frame(t, $) {
const S = scene3d($, { camera: { yaw: -20 + 40 * (t / $.dur), pitch: -8 } });
S.add(S.text("VOLITO", { face: $.font("display"), size: 220 * $.k, depth: 60 * $.k, color: $.c.fg, side: $.c.accent }));
return S.render();
}Maps use Natural Earth's countries, 177 of them packed into 30 KB: worldMap($, { center, zoom }) projects them flat (Equal Earth), worldGlobe(S, …) puts them on a 3D globe, and worldFind("UK") finds one by name, short name or ISO number. An item that uses them lists the library in its libs.
An item
An item is a folder with item.json and frame.js. volito new writes both from a template.
{
"id": "promo-card",
"name": "Promo card",
"description": "A card with a title and a price that counts up.",
"category": "cta",
"placement": "overlay",
"duration": { "default": 4, "min": 2, "max": 12 },
"aspects": ["16:9", "9:16", "1:1", "4:5"],
"props": {
"title": { "type": "text", "label": "Title", "default": "Hello", "maxLength": 24 },
"price": { "type": "number", "label": "Price", "default": 49, "min": 0, "max": 99999 }
},
"parts": { "card": "the card", "title": "the title", "price": "the price" }
}| Field | What it is |
|---|---|
id | Lowercase words joined by hyphens, unique in its pack. |
name, description | What a person (or an agent) reads in the list. |
category | One of lower-third, title, cta, stat, list, quote, end-card, callout, social, chart, logo, transition, banner, timer, text, scene. |
placement | overlay (transparent, over footage) or full (draws its own ground). |
duration | Seconds: default, and optionally min and max. |
aspects | The aspects it is laid out for; all four when omitted. |
props | Each prop's type (text, longtext, number, choice, boolean, color, image, icon, list), label, default and limits (maxLength, min, max, options). |
parts | The parts a creator can animate, name to what it is; the names --motion uses. |
beats | Named moments the code reads with $.beat(name); negative counts from the end. |
libs | Libraries the code uses besides the motion kit: "3d" for scene3d, "world" for the maps. |
tags, style, fonts | Search words, the style it is shown in by default, and faces it names besides the brand's. |
A pack
A pack is a folder with pack.json, an optional shared.js of helpers that is prepended to every item's code, and one folder per item. Load it with --packs <dir> or VOLITO_PACKS, and it shows up in list, the Node API and the MCP tools like the built-in packs.
{
"format": 1,
"id": "acme",
"name": "Acme graphics",
"version": "1.0.0",
"description": "Our lower thirds and end cards.",
"author": "Acme",
"license": "Proprietary",
"style": "cobalt",
"shared": "shared.js",
"items": ["promo-card"]
}Items stay small: the motion kit, the 3D kit and the map data are added when a program is built, and the 60 000-character limit applies only to what you (or an agent) wrote.