HTML scenes and canvas scenes, the meta, the frame rule, and why every frame renders the same.
A scene is one shot: an id, a duration, an entry, cues, and something that draws as a pure function of time. Any frame re-renders exactly, frames render in any order, and one scene renders in parallel chunks across Chrome pages. Authoring contract: writing scenes.
Two kinds
- HTML scenes: one
.htmlfile of HTML, CSS, and pinned libraries, run in headless Chrome under rapid's virtual-time runtime. Everyscenes/<id>.htmlin a project is one; the planner builds only these. - Canvas scenes: a
.js,.mjs, or.tsmodule whose default export has ametaand adraw(g, t, d)that paints the whole frame fromt.rapid rendertakes one by its extension.
The meta
SceneMeta and HtmlSceneMeta in contracts/src/scene.ts:
| Field | Type | Notes |
|---|---|---|
id | id | a-z, 0-9, -, up to 64 |
kind | html or canvas | html by default in an HTML scene |
dur | seconds | from the scene's own timings, never padded |
enter | { kind, dur, params? } | cut, flood, mark, blob, slide, or fade; default { "kind": "cut", "dur": 0 } |
place, clock | strings, up to 80 and 20 | drawn over the scene, never by it |
who | string, up to 80 | optional |
duck | array of [from, to] | spans where the scene's full-frame type owns the clock's corner; default [] |
width, height, fps | HTML only | the frame it draws, from its format; required |
fonts | HTML only, array of strings | families drawn only on a <canvas>, which must load; default [] |
key | HTML only, seconds | the key frame: the hero, built and approved before the motion |
holds | HTML only, array of [from, to] | spans held still on purpose; the dead check skips them; default [] |
room | HTML only, 0–1 | the share of the key frame that may be empty field; the empty check allows up to it (default 0.4) |
The frame rule
One function (contracts/src/frames.ts) says how many frames a duration has and which frame a time is in; every renderer, check, and sheet uses it.
- Frame
kstarts atk / fps. dseconds isceil(d × fps)frames, at least one.- A time falls in the frame it is in (rounding down), clamped to the scene.
- The poster is the start of the last frame: the node's thumbnail.
At 30 fps a 3.2 s scene has 96 frames; its poster is at 3.1667 s.
Determinism
The runtime loads before the scene's scripts and owns every clock the page can read: Date, performance.now, requestAnimationFrame, setTimeout, setInterval, Math.random, crypto.getRandomValues. The clock starts at 2026-01-01T00:00:00Z and moves only when the renderer seeks.
Same in any seek order:
- code in
rapid.frame(fn)that computes from itst; - GSAP timelines built at load;
- CSS animations and transitions;
- Web Animations made at load;
rapid.random()inside a frame callback (reseeded from the frame's time);- the pinned libraries.
Same only in a straight play: state a script keeps (a timer chain, a counter bumped per frame). Timers fire in virtual-time order as seeks move forward; a backwards seek rewinds the clocks but never un-fires a timer. A render seeks out of order across chunks, so compute from t.
Problems fail the render
The runtime collects uncaught errors, unhandled rejections, a throwing frame callback or timer, assets that fail to load, images still loading after 10 s, more than 10,000 callbacks in one seek, and fonts that didn't load, each with the time it first happened. A scene with any problem renders nothing; rapid render exits 1 naming it:
rapid: callback at 1.033s: frame: Error: oops
No network
A scene reaches nothing outside its folder (a project's scene: the project). An absolute address in its HTML or CSS is refused before the page loads; the page's content security policy blocks anything a script builds at run time. Relative addresses resolve against the scene's folder; rapid:style/<path> is the linked style's file.
What a render writes
Beside each MP4:
<name>.boxes.json: everydata-elelement on every frame: id, box in frame pixels, role, text, and for type its line height and font size. The checks read it.<name>.cues.json: every cue the scene emitted, deduplicated by key. The mix plays them; review times are picked at them.