Files
cycle-atlas/.claude/skills/hyperframes/references/runtimes.md
T

3.0 KiB

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.

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.

Other runtimes

Runtime Use for How HyperFrames drives it
CSS @keyframes Simple loops and decoration Found automatically; finite iterations, animation-fill-mode: both, data-start on the element
WAAPI Native keyframes element.animate(…, { fill: "both" }) then pause(); times are document time, so use delay
Lottie / dotLottie After Effects exports, characters autoplay: false, always set loop, push the instance to window.__hfLottie
anime.js v4 Existing anime code autoplay: false, push to window.__hfAnime, anime.createSeededRandom
Three.js, WebGPU, p5, canvas 3D and generative Draw 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.

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.