Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FXVsD1DgdzZLiPuwtKzMSY
3.0 KiB
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>needdisplay: inline-blockorblock. - Text that must fit a width:
window.__hyperframes.fitTextFontSize(text, { fontFamily, fontWeight, maxWidth })returns{ fontSize };window.__hyperframes.pretextmeasures 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.