Page API

What the renderer gives a page and what it reads from a page, from window.vawe to meta tags and data attributes.

This page lists the page APIThe page API is the window.vawe object that the renderer sets before your scripts run: aspect, width, height, fps, duration, onFrame and more.: what the renderer sets before your scripts run, and what it reads from your markup. The sources are harness/media/render-page.mjs, core/engine/page-seek.js, core/engine/page-api.js and harness/media/page-audio.mjs. For the idea behind it, read The page and Time and seek.

window.seek

Define window.seek(t) when the page paints in script. t is the film time in seconds and can be fractional. Paint frame t as a pure function of t. A promise that you return is awaited, so a texture can decode before the screenshot (core/engine/page-seek.js).

For each seek, the renderer does these steps in order:

  1. Wait for the page to be ready: no image decode, fetch or font load is pending.
  2. Set the virtual clockThe virtual clock is the code in core/engine/page-clock.js that controls Date, requestAnimationFrame, timers and Math.random during a render. to t.
  3. Call window.seek(t) if the page defines it.
  4. Pause every CSS, Web Animation and SVG animation and set its time to t.
  5. Call every vawe.onFrame hook.

window.vawe

The renderer sets these before any page script runs (installPageFrame, core/engine/page-seek.js).

NameTypeWhat it is
vawe.aspectstringThe aspect of this render, for example "16:9"
vawe.width, vawe.heightnumberThe CSS viewport in px. Always the full aspect size, also in a draft
vawe.fpsnumber or undefinedThe value of <meta name="fps">. It is the rate of your frame table, not the render rate
vawe.durationnumber or undefinedThe value of <meta name="duration">. For auto, the length that the render measured
vawe.onFrame(fn)functionRegister a hook fn(t) that runs after each seek. It may return a promise
vawe.animationEnd()function{ endS, infinite }: the end of the last animation. The renderer uses it to size an auto page

One module adds more names. Import it from films/<name>/page.html as a module script:

<script type="module">
import '../../core/engine/page-api.js';
</script>

It adds these names to window.vawe (core/engine/page-api.js):

NameWhat it does
vawe.value(row, t)Sample one timing row { at, dur, from, to, ease } as a number. ease is a CSS keyword or a name in core/motion/easings.js. An unknown name throws and lists the valid ones
vawe.assert(t, fn, message)The renderer runs fn after it seeks to t, at dev and ship. A falsy or throwing fn stops the render with exit 2, the time and message
vawe.clockThe virtual clock, window.__pageClock
vawe.threeThe three.js helpers studio, extrude and material from core/three/index.js

CSS variables and the html element

NameSet onWhat it is
data-aspect<html>The aspect of this render. Use it in selectors, for example [data-aspect="9:16"] h1
--vw:rootThe viewport width as a length, such as 1920px
--vh:rootThe viewport height as a length, such as 1080px
--ease-<name>:rootThe named eases as CSS linear() curves

--vw and --vh hold a unit. Write calc(var(--vh) * 0.11), never * 0.11px. The named eases are land, land-soft, settle, swap, glide, carry, leave, launch, pop and nudge (EASE_HANDLES in core/motion/presets.js). A plain browser does not set them, so a page that uses them shows its end state there.

The canvas sizes are 16:9 1920x1080, 9:16 1080x1920, 1:1 1080x1080, 4:5 1080x1350 and 4:3 1440x1080. Any other ratio fits the long edge to 1920 (core/layout/aspects.js). See Aspect ratios.

Meta tags

The renderer reads these before the page loads, from the file. harness/lib/page-meta.mjs finds <meta name="..." content="...">.

MetaValueWhat it does
durationseconds, or autoThe lengthThe duration is the film length in seconds, set by <meta name="duration" content="12.4">.. auto is the end of the last finite move plus a hold. See harness/media/page-duration.mjs
aspectW:HThe aspect when no --aspect flag is given. Default 16:9
fpsnumberThe rate of your frame table. The render rate is separate: 30 for a draft, 60 for a final
messagetextThe one thing to rememberThe message is the one thing that a viewer must remember, set by <meta name="message">.. The text checks read it
spectaclesecondsThe second of the one big moment, the spectacleThe spectacle is the second of the one big moment in a film, set by <meta name="spectacle" content="6.2">.. Put quiet before it
signatureband=...; ease=...; stagger=...; seam=...; palette=...; thread=...The six dials of the film. The motion presets use band, ease and stagger as defaults, and dev reads all six (harness/lib/signature.mjs)
loudnessLUFS numberNormalise the mix to this target. Without it, the mix is as written (harness/media/page-audio.mjs)
looptrueThe loop-seam check compares the last frame with the first (quality/gates/page-check.mjs:378)
blank0-0.4, 4.8-5Declare the seconds where an empty frame is intended. data-blank on <html> does the same (render-page.mjs:665)

data attributes

On the page

AttributeOnWhat it does
data-world="<id>"an elementMarks one worldA world is the one element that holds one beat of the film, marked with data-world="<id>".. Lowercase id. frames, strip --cuts, the checks and data-on read it
data-chromean elementText inside is product UI shown as texture. The cap floor is 2.5% and it has no read hold
data-uian elementText inside is UI that is the subject. The cap floor is 3%
data-texture="<why>"an elementReal texture, with a reason. The reason needs at least 12 characters (taste/rules/readable-text-size.md)
data-look="product"<html>The motion lint treats the page like data-chrome: uniform motion (harness/lib/motion-lint.mjs:53)

Text inside aria-hidden="true" is also texture, and the text checks skip it.

On an audio tag

page-audio.mjs reads these from every <audio> element. The mix is offline and as written. See Audio and Fonts and sounds.

AttributeWhat it does
srcThe file, relative to the page. A <source> child also works
loopMarks the music bedA bed is a music track that plays under the whole film, marked by the loop attribute on an <audio> tag. (role music)
data-synth="<name>"A built-in voiceA voice is one of the built-in synth sounds, such as pluck or whoosh, chosen with data-synth="<voice>". instead of a file
data-at="<s>"The second where the cueA cue is one sound at one second, written as an <audio> tag with data-at. starts. Default 0
data-on="world:<id>"data-at counts from the first second world <id> shows. It needs the page seek
data-gain="<dB>"The absolute level. It replaces the default of a voice
data-fade-in, data-fade-outFade seconds. Default 0
data-trim, data-trim-endPlay the seconds data-trim to data-trim-end of the file
data-role="vo"A voice-over. It ducksTo duck is to lower the music while a voice speaks. the music by 18 dB while it plays
data-length="<s>"The seconds of the move, for the voices air and swoosh-long
data-duck="<dB>"Duck the music by this many dB while this track plays

What a page may not do

The virtual clock owns Date, performance.now, requestAnimationFrame, setTimeout, setInterval and Math.random. Each one is a function of the seek time. Math.random restarts from the seek time on every seek, so the same t gives the same numbers (core/engine/page-clock.js). Keep no state between frames. See Determinism.

A module-level await of anything but an image decode, a fetch or document.fonts.ready is not tracked. Frame 0 can paint before your vawe.onFrame exists. Await those three, or register the hook first and await inside it.

A rule that you break on purpose goes into <script type="application/json" id="authoring">. See Waivers.

From the blogHTML to video format: the page contract · GSAP to MP4: export a timeline as video · Seed Math.random and freeze Date.now in JavaScript

On this page