Skill Tracer
Skills › HyperFrames › hyperframes › runtimes.md

runtimes.md

69 lines · skills/hyperframes/references/runtimes.md at a595755

Open on GitHub
1# Animation runtimes
2
3HyperFrames seeks each frame on its own, so motion must be a function of time, never of the previous frame. Full GSAP reference: `npx hyperframes docs gsap`.
4

Animation runtimes

HyperFrames seeks each frame on its own, so motion must be a function of time, never of the previous frame. Full GSAP reference: npx hyperframes docs gsap.

Raw lines 1 to 4
5## GSAP facts that bite
6
7- Register the timeline last. Building inside `document.fonts.ready.then(…)` or another async callback is fine if you register at the end of it; registering first renders blank (`gsap_timeline_registered_before_async_build`).
8- Place tweens at absolute seconds (`tl.to(el, {…}, 2.4)`). Set start states in the tween (`fromTo`), not in CSS.
9- Measure layout once before building tweens, never inside `onUpdate`.
10- Finite repeats only: `repeat: Math.max(0, Math.floor(duration / cycle) - 1)`.
11- Seed randomness with a small PRNG (mulberry32).
12- A scene's timeline can't reach elements outside its file, and two timelines must not animate the same property of one element.
13- Transforms on a `<span>` need `display: inline-block` or `block`.
14- Text that must fit a width: `window.__hyperframes.fitTextFontSize(text, { fontFamily, fontWeight, maxWidth })` returns `{ fontSize }`; `window.__hyperframes.pretext` measures text without a reflow.

GSAP facts that bite

  • Register the timeline last. Building inside document.fonts.ready.then(…) or another async callback is fine if you register at the end of it; registering first renders blank (gsap_timeline_registered_before_async_build).
  • Place tweens at absolute seconds (tl.to(el, {…}, 2.4)). Set start states in the tween (fromTo), not in CSS.
  • Measure layout once before building tweens, never inside onUpdate.
  • Finite repeats only: repeat: Math.max(0, Math.floor(duration / cycle) - 1).
  • Seed randomness with a small PRNG (mulberry32).
  • A scene's timeline can't reach elements outside its file, and two timelines must not animate the same property of one element.
  • Transforms on a <span> need display: inline-block or block.
  • Text that must fit a width: window.__hyperframes.fitTextFontSize(text, { fontFamily, fontWeight, maxWidth }) returns { fontSize }; window.__hyperframes.pretext measures text without a reflow.
Raw lines 5 to 15
16## Other runtimes
18| Runtime | Use for | How HyperFrames drives it |
19| ---------------------------- | --------------------------------- | ------------------------------------------------------------------------------------------------ |
20| CSS `@keyframes` | Simple loops and decoration | Found automatically; finite iterations, `animation-fill-mode: both`, `data-start` on the element |
21| WAAPI | Native keyframes | `element.animate(…, { fill: "both" })` then `pause()`; times are document time, so use `delay` |
22| Lottie / dotLottie | After Effects exports, characters | `autoplay: false`, always set `loop`, push the instance to `window.__hfLottie` |
23| anime.js v4 | Existing anime code | `autoplay: false`, push to `window.__hfAnime`, `anime.createSeededRandom` |
24| Three.js, WebGPU, p5, canvas | 3D and generative | Draw from `e.detail.time` in an `hf-seek` listener; set `data-duration` on the root |
26Charts and map overlays: drive them from a GSAP tween's `onUpdate`, not the library's own transitions. A real place is shown on baked imagery, not drawn (`film.md` "Maps and real places"). Heavy setup (big meshes, shader compiles) registers a `window.__hf.buildReady["<key>"]` promise. Setup awaits library promises such as `renderer.compileAsync(...)`, never `requestAnimationFrame`, the clock, its own timers or `scheduler.yield()` (`path-tracing.md`).

Other runtimes

RuntimeUse forHow HyperFrames drives it
CSS @keyframesSimple loops and decorationFound automatically; finite iterations, animation-fill-mode: both, data-start on the element
WAAPINative keyframeselement.animate(…, { fill: "both" }) then pause(); times are document time, so use delay
Lottie / dotLottieAfter Effects exports, charactersautoplay: false, always set loop, push the instance to window.__hfLottie
anime.js v4Existing anime codeautoplay: false, push to window.__hfAnime, anime.createSeededRandom
Three.js, WebGPU, p5, canvas3D and generativeDraw from e.detail.time in an hf-seek listener; set data-duration on the root

Charts and map overlays: drive them from a GSAP tween's onUpdate, not the library's own transitions. A real place is shown on baked imagery, not drawn (film.md "Maps and real places"). Heavy setup (big meshes, shader compiles) registers a window.__hf.buildReady["<key>"] promise. Setup awaits library promises such as renderer.compileAsync(...), never requestAnimationFrame, the clock, its own timers or scheduler.yield() (path-tracing.md).

Raw lines 16 to 27
28## dotLottie browser setup
30Copy `dist/index.js` and `dist/dotlottie-player.wasm` from the same pinned
31`@lottiefiles/dotlottie-web` release (for example, 0.81.0) into the project's
32`lib/dotlottie-web/` directory. Its CommonJS entry cannot run as a classic
33browser script. Import the ESM entry and set the local WASM URL before creating
34the player, so rendering loads the SDK, WASM and animation from project files.
36```html
37<canvas id="anim" style="width:100%;height:100%"></canvas>
38<script type="module">
39 import { DotLottie } from "./lib/dotlottie-web/index.js";
41 DotLottie.setWasmUrl(new URL("./lib/dotlottie-web/dotlottie-player.wasm", import.meta.url).href);
42 const player = new DotLottie({
43 canvas: document.getElementById("anim"),
44 src: "assets/hello.lottie",
45 autoplay: false,
46 loop: false,
47 renderConfig: { autoResize: false, freezeOnOffscreen: false },
48 });
49 window.__hfLottie = window.__hfLottie || [];
50 window.__hfLottie.push(player);
51</script>
52```

dotLottie browser setup

Copy dist/index.js and dist/dotlottie-player.wasm from the same pinned @lottiefiles/dotlottie-web release (for example, 0.81.0) into the project's lib/dotlottie-web/ directory. Its CommonJS entry cannot run as a classic browser script. Import the ESM entry and set the local WASM URL before creating the player, so rendering loads the SDK, WASM and animation from project files.

<canvas id="anim" style="width:100%;height:100%"></canvas>
<script type="module">
  import { DotLottie } from "./lib/dotlottie-web/index.js";

  DotLottie.setWasmUrl(new URL("./lib/dotlottie-web/dotlottie-player.wasm", import.meta.url).href);
  const player = new DotLottie({
    canvas: document.getElementById("anim"),
    src: "assets/hello.lottie",
    autoplay: false,
    loop: false,
    renderConfig: { autoResize: false, freezeOnOffscreen: false },
  });
  window.__hfLottie = window.__hfLottie || [];
  window.__hfLottie.push(player);
</script>
Raw lines 28 to 53
54## The seek protocol
56```html
57<script>
58 function renderAt(t) {
59 /* draw the frame for t seconds from state derived from t alone */
60 }
61 window.addEventListener("hf-seek", (e) => {
62 renderAt(e.detail.time);
63 // async work (GPU submit, image decode): e.detail.waitUntil(promise), called synchronously
64 });
65 renderAt(0);
66</script>
67```
69No `requestAnimationFrame` loop or timer as the clock.

The seek protocol

<script>
  function renderAt(t) {
    /* draw the frame for t seconds from state derived from t alone */
  }
  window.addEventListener("hf-seek", (e) => {
    renderAt(e.detail.time);
    // async work (GPU submit, image decode): e.detail.waitUntil(promise), called synchronously
  });
  renderAt(0);
</script>

No requestAnimationFrame loop or timer as the clock.

Raw lines 54 to 69