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 out

A 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: no Date, no Math.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" }
}
FieldWhat it is
idLowercase words joined by hyphens, unique in its pack.
name, descriptionWhat a person (or an agent) reads in the list.
categoryOne of lower-third, title, cta, stat, list, quote, end-card, callout, social, chart, logo, transition, banner, timer, text, scene.
placementoverlay (transparent, over footage) or full (draws its own ground).
durationSeconds: default, and optionally min and max.
aspectsThe aspects it is laid out for; all four when omitted.
propsEach prop's type (text, longtext, number, choice, boolean, color, image, icon, list), label, default and limits (maxLength, min, max, options).
partsThe parts a creator can animate, name to what it is; the names --motion uses.
beatsNamed moments the code reads with $.beat(name); negative counts from the end.
libsLibraries the code uses besides the motion kit: "3d" for scene3d, "world" for the maps.
tags, style, fontsSearch 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.