{"id":"0a44097e-138c-48c2-8c6d-be3cea3a5835","entityType":"agent","slug":"clawhub-heygen-com-hyperframes-animation","name":"hyperframes-animation","canonicalUrl":"https://www.xpersona.co/agent/clawhub-heygen-com-hyperframes-animation","canonicalPath":"/agent/clawhub-heygen-com-hyperframes-animation","generatedAt":"2026-10-09T23:49:31.307Z","source":"CLAWHUB","claimStatus":"UNCLAIMED","verificationTier":"NONE","summary":{"evidence":{"source":"editorial-content","verified":true,"confidence":"high","updatedAt":"2026-10-09T09:32:49.859Z","emptyReason":null},"description":"All animation knowledge for HyperFrames — atomic motion rules, multi-phase scene blueprints, scene transitions, broader motion-design techniques, AND the seven runtime adapters (GSAP default, plus Lottie, Three.js, Anime.js, CSS keyframes, Web Animations API, TypeGPU). Use for any motion or animation task: pick 2-4 rules and compose, or load a blueprint, or look up runtime-specific API (e.g. GSAP eases / Lottie player / Three.js mixer). Also covers auditing an existing composition's choreography (animation map) and 24 named text-animation effects. HyperFrames-native: single paused timeline, seek-safe, deterministic. Skill: hyperframes-animation Owner: heygen-com Summary: All animation knowledge for HyperFrames — atomic motion rules, multi-phase scene blueprints, scene transitions, broader motion-design techniques, AND the seven runtime adapters (GSAP default, plus Lottie, Three.js, Anime.js, CSS keyframes, Web Animations API, TypeGPU). Use for any motion or animation task: pick 2-4 rules and compose, or load a blueprint, or look","descriptionLabel":"Technical summary","evidenceSummary":"Capability contract not published. No trust telemetry is available yet. 3.1K downloads reported by the source. Last updated 10/9/2026.","installCommand":"clawhub skill install s17fpgb0p797dzkbtbrxw5x1hh89qs64:hyperframes-animation","sourceUrl":"https://clawhub.ai/heygen-com/hyperframes-animation","homepage":"https://clawhub.ai/heygen-com/skills/hyperframes-animation","primaryLinks":[{"label":"View on ClawHub","url":"https://clawhub.ai/heygen-com/hyperframes-animation","kind":"source"},{"label":"Homepage","url":"https://clawhub.ai/heygen-com/skills/hyperframes-animation","kind":"homepage"}],"safetyScore":84,"overallRank":62,"popularityScore":70,"trustScore":null,"claimedByName":null,"isOwner":false,"seoDescription":"All animation knowledge for HyperFrames — atomic motion rules, multi-phase scene blueprints, scene transitions, broader motion-design techniques, AND the seven "},"coverage":{"evidence":{"source":"public-profile","verified":false,"confidence":"medium","updatedAt":"2026-10-09T09:32:49.859Z","emptyReason":null},"protocols":[{"protocol":"OPENCLEW","label":"OpenClaw","status":"self-declared","notes":"Declared in the public agent profile."}],"capabilities":[],"verifiedCount":0,"selfDeclaredCount":1,"capabilityMatrix":{"rows":[{"key":"OPENCLEW","type":"protocol","support":"unknown","confidenceSource":"profile","notes":"Listed on profile"}],"flattenedTokens":"protocol:OPENCLEW|unknown|profile"}},"adoption":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-09T09:32:49.859Z","emptyReason":null},"stars":null,"forks":null,"downloads":3145,"packageName":null,"latestVersion":"1.0.36","tractionLabel":"3.1K downloads"},"release":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-09T09:32:49.850Z","emptyReason":null},"lastUpdatedAt":"2026-10-09T09:32:49.859Z","lastCrawledAt":"2026-10-09T09:32:49.850Z","lastIndexedAt":null,"nextCrawlAt":"2026-10-10T09:32:49.850Z","lastVerifiedAt":null,"highlights":[{"version":"1.0.36","createdAt":"2026-10-08T20:31:23.510Z","changelog":"Synced from 29b901d (main)","fileCount":124,"zipByteSize":477411},{"version":"1.0.35","createdAt":"2026-10-07T01:44:00.483Z","changelog":"Synced from 4cf5cf9 (main)","fileCount":124,"zipByteSize":476517},{"version":"1.0.34","createdAt":"2026-10-06T21:38:25.960Z","changelog":"Synced from b50dead (main)","fileCount":124,"zipByteSize":476424},{"version":"1.0.33","createdAt":"2026-10-05T17:05:31.799Z","changelog":"Synced from 6c353d8 (main)","fileCount":124,"zipByteSize":476186},{"version":"1.0.32","createdAt":"2026-10-04T19:30:16.146Z","changelog":"Synced from 0c3e244 (main)","fileCount":124,"zipByteSize":475480},{"version":"1.0.31","createdAt":"2026-10-04T19:17:27.754Z","changelog":"Synced from 173103d (main)","fileCount":124,"zipByteSize":475467},{"version":"1.0.30","createdAt":"2026-10-02T14:31:35.744Z","changelog":"Synced from 9465048 (main)","fileCount":124,"zipByteSize":475346},{"version":"1.0.29","createdAt":"2026-10-02T14:17:55.513Z","changelog":"Synced from 0a04f80 (main)","fileCount":124,"zipByteSize":475481}]},"execution":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No published capability contract is available yet."},"installCommand":"clawhub skill install s17fpgb0p797dzkbtbrxw5x1hh89qs64:hyperframes-animation","setupComplexity":"low","setupSteps":["Setup complexity is classified as HIGH. You must provision dedicated cloud infrastructure or an isolated VM. Do not run this directly on your local workstation.","Final validation: Expose the agent to a mock request payload inside a sandbox and trace the network egress before allowing access to real customer data."],"contract":{"contractStatus":"missing","authModes":[],"requires":[],"forbidden":[],"supportsMcp":false,"supportsA2a":false,"supportsStreaming":false,"inputSchemaRef":null,"outputSchemaRef":null,"dataRegion":null,"contractUpdatedAt":null,"sourceUpdatedAt":null,"freshnessSeconds":null},"invocationGuide":{"preferredApi":{"snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-heygen-com-hyperframes-animation/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-heygen-com-hyperframes-animation/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-heygen-com-hyperframes-animation/trust"},"curlExamples":["curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-heygen-com-hyperframes-animation/snapshot\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-heygen-com-hyperframes-animation/contract\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-heygen-com-hyperframes-animation/trust\""],"jsonRequestTemplate":{"query":"summarize this repo","constraints":{"maxLatencyMs":2000,"protocolPreference":["OPENCLEW"]}},"jsonResponseTemplate":{"ok":true,"result":{"summary":"...","confidence":0.9},"meta":{"source":"CLAWHUB","generatedAt":"2026-10-09T23:49:31.304Z"}},"retryPolicy":{"maxAttempts":3,"backoffMs":[500,1500,3500],"retryableConditions":["HTTP_429","HTTP_503","NETWORK_TIMEOUT"]}},"endpoints":{"dossierUrl":"https://www.xpersona.co/api/v1/agents/clawhub-heygen-com-hyperframes-animation/dossier","snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-heygen-com-hyperframes-animation/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-heygen-com-hyperframes-animation/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-heygen-com-hyperframes-animation/trust"}},"reliability":{"evidence":{"source":"runtime-metrics","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No trust, reliability, or runtime telemetry is available."},"trust":{"status":"unavailable","handshakeStatus":"UNKNOWN","verificationFreshnessHours":null,"reputationScore":null,"p95LatencyMs":null,"successRate30d":null,"fallbackRate":null,"attempts30d":null,"trustUpdatedAt":null,"trustConfidence":"unknown","sourceUpdatedAt":null,"freshnessSeconds":null},"decisionGuardrails":{"doNotUseIf":["Contract metadata is missing or unavailable for deterministic execution."],"safeUseWhen":[],"riskFlags":["missing_or_unavailable_contract","trust_data_unavailable","schema_references_missing"],"operationalConfidence":"low"},"executionMetrics":{"observedLatencyMsP50":null,"observedLatencyMsP95":null,"estimatedCostUsd":null,"uptime30d":null,"rateLimitRpm":null,"rateLimitBurst":null,"lastVerifiedAt":null,"verificationSource":null},"runtimeMetrics":{"successRate":null,"avgLatencyMs":null,"avgCostUsd":null,"hallucinationRate":null,"retryRate":null,"disputeRate":null,"p50Latency":null,"p95Latency":null,"lastUpdated":null}},"benchmarks":{"evidence":{"source":"no-benchmark-data","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No benchmark suites or observed failure patterns are available."},"suites":[],"failurePatterns":[]},"artifacts":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"high","updatedAt":"2026-10-09T09:32:49.859Z","emptyReason":null},"readme":"Skill: hyperframes-animation\n\nOwner: heygen-com\n\nSummary: All animation knowledge for HyperFrames — atomic motion rules, multi-phase scene blueprints, scene transitions, broader motion-design techniques, AND the seven runtime adapters (GSAP default, plus Lottie, Three.js, Anime.js, CSS keyframes, Web Animations API, TypeGPU). Use for any motion or animation task: pick 2-4 rules and compose, or load a blueprint, or look up runtime-specific API (e.g. GSAP eases / Lottie player / Three.js mixer). Also covers auditing an existing composition's choreography (animation map) and 24 named text-animation effects. HyperFrames-native: single paused timeline, seek-safe, deterministic.\n\nTags: latest:1.0.36\n\nVersion history:\n\nv1.0.36 | 2026-10-08T20:31:23.510Z | user\n\nSynced from 29b901d (main)\n\nv1.0.35 | 2026-10-07T01:44:00.483Z | user\n\nSynced from 4cf5cf9 (main)\n\nv1.0.34 | 2026-10-06T21:38:25.960Z | user\n\nSynced from b50dead (main)\n\nv1.0.33 | 2026-10-05T17:05:31.799Z | user\n\nSynced from 6c353d8 (main)\n\nv1.0.32 | 2026-10-04T19:30:16.146Z | user\n\nSynced from 0c3e244 (main)\n\nv1.0.31 | 2026-10-04T19:17:27.754Z | user\n\nSynced from 173103d (main)\n\nv1.0.30 | 2026-10-02T14:31:35.744Z | user\n\nSynced from 9465048 (main)\n\nv1.0.29 | 2026-10-02T14:17:55.513Z | user\n\nSynced from 0a04f80 (main)\n\nv1.0.28 | 2026-10-02T13:01:25.335Z | user\n\nSynced from 6f799aa (main)\n\nv1.0.27 | 2026-10-02T00:11:45.645Z | user\n\nSynced from 37f30b1 (main)\n\nv1.0.26 | 2026-09-27T21:19:23.193Z | user\n\nSynced from ff6e210 (main)\n\nv1.0.25 | 2026-09-24T02:58:52.701Z | user\n\nSynced from ff88484 (main)\n\nv1.0.24 | 2026-09-23T13:22:22.494Z | user\n\nSynced from 8055961 (main)\n\nv1.0.23 | 2026-09-19T05:56:14.565Z | user\n\nSynced from 752b552 (main)\n\nv1.0.22 | 2026-09-19T03:21:24.019Z | user\n\nSynced from 2db126d (main)\n\nv1.0.21 | 2026-09-18T15:13:46.079Z | user\n\nSynced from e36a53d (main)\n\nv1.0.20 | 2026-09-13T03:20:33.857Z | user\n\nSynced from 4aa6e17 (main)\n\nv1.0.19 | 2026-09-08T18:10:23.399Z | user\n\nSynced from 131336f (main)\n\nv1.0.18 | 2026-08-24T22:06:03.964Z | user\n\nSynced from b2fc18b (main)\n\nv1.0.17 | 2026-08-20T06:22:54.825Z | user\n\nSynced from a6a9e2f (main)\n\nv1.0.16 | 2026-08-18T02:01:48.918Z | user\n\nSynced from 0d874ad (main)\n\nv1.0.15 | 2026-08-16T18:02:49.415Z | user\n\nSynced from 67edb01 (main)\n\nv1.0.14 | 2026-07-31T04:53:47.104Z | user\n\nSynced from 3a6b7f0 (main)\n\nv1.0.13 | 2026-07-30T12:11:06.203Z | user\n\nSynced from 2e4c2c4 (main)\n\nv1.0.12 | 2026-07-21T16:44:01.099Z | user\n\nSynced from 696cbdb (main)\n\nv1.0.11 | 2026-07-21T09:30:15.588Z | user\n\nSynced from 8532564 (main)\n\nv1.0.10 | 2026-07-20T15:20:12.522Z | user\n\nSynced from 6ad738b (main)\n\nv1.0.9 | 2026-07-15T14:23:43.216Z | user\n\nSynced from 7d21cc9 (main)\n\nv1.0.8 | 2026-07-15T13:21:41.852Z | user\n\nSynced from b9be0b2 (main)\n\nv1.0.7 | 2026-07-15T00:36:24.655Z | user\n\nSynced from 15ca6fd (main)\n\nv1.0.6 | 2026-07-14T22:05:37.371Z | user\n\nSynced from eb731b6 (main)\n\nv1.0.5 | 2026-07-10T22:48:49.768Z | user\n\nSynced from 00d059b (main)\n\nv1.0.4 | 2026-07-08T08:39:58.936Z | user\n\nSynced from 6192ed4 (main)\n\nv1.0.3 | 2026-07-07T20:27:30.258Z | user\n\nSynced from 7286b00 (main)\n\nv1.0.2 | 2026-07-07T19:00:13.302Z | user\n\nSynced from 5fe9573 (main)\n\nv1.0.1 | 2026-07-02T17:08:54.618Z | user\n\nSynced from a7c3cc7 (main)\n\nv1.0.0 | 2026-07-01T08:42:59.986Z | user\n\nOfficial HyperFrames skills from heygen-com/hyperframes\n\nArchive index:\n\nArchive v1.0.36: 124 files, 477411 bytes\n\nFiles: adapters/animate-text.md (4635b), adapters/animejs.md (6224b), adapters/css-animations.md (5407b), adapters/gsap-easing-and-stagger.md (13270b), adapters/gsap-timeline-and-labels.md (3791b), adapters/gsap-transforms-and-perf.md (8552b), adapters/gsap.md (7602b), adapters/html-in-canvas-patterns.md (17166b), adapters/lottie.md (6181b), adapters/three.md (8207b), adapters/typegpu.md (8230b), adapters/waapi.md (4248b), blueprints-index.md (26512b), blueprints/agent-progress-theater.md (16091b), blueprints/camera-journey.md (13648b), blueprints/comparison-split.md (3316b), blueprints/constellation-hub.md (7144b), blueprints/cta-morph-press.md (6407b), blueprints/cursor-ui-demo.md (20276b), blueprints/dataviz-countup.md (14611b), blueprints/device-surface-showcase.md (17151b), blueprints/fixed-anchor-cycle.md (9089b), blueprints/grid-card-assemble.md (15594b), blueprints/kinetic-type-beats.md (29151b), blueprints/logo-assemble-lockup.md (26609b), blueprints/overwhelm-surround.md (6140b), blueprints/panel-edit-live-sync.md (13394b), blueprints/prompt-type-submit-generate.md (18636b), blueprints/spatial-pan-stations.md (4868b), blueprints/ticker-takeover.md (3052b), blueprints/titlecard-reveal.md (7441b), blueprints/transcript-scroll-artifact-reveal.md (11666b), blueprints/typewriter-reveal.md (6276b), blueprints/video-text-pivot.md (3281b), blueprints/zoom-out-workspace-reveal.md (14714b), examples/brand-reveal-assemble-zoom.html (12285b), examples/comparison-split-cards.html (21754b), examples/concept-demo-decode-pan.html (18382b), examples/cta-morph-press.html (15192b), examples/cta-orbit-collapse.html (50646b), examples/demo-page-scroll-spotlight.html (25019b), examples/hook-counter-burst.html (23004b), examples/messaging-multi-phrase.html (12223b), examples/metric-video-text-pivot.html (25640b), examples/problem-mockup-overwhelm.html (44482b), examples/proof-logo-chain.html (28911b), examples/takeover-ticker-displace.html (10947b), examples/workflow-approve-press.html (20656b), references/motion-blur.md (14104b), rules-index.md (21484b), rules/3d-camera-flight.md (18137b), rules/3d-page-scroll.md (7406b), rules/3d-text-depth-layers.md (6708b), rules/ai-tracking-box.md (6562b), rules/ambient-glow-bloom.md (9840b), rules/anchored-layout-expand.md (10313b), rules/asr-keyword-glow.md (6762b), rules/avatar-cloud-network.md (6578b), rules/camera-cursor-tracking.md (7911b), rules/card-morph-anchor.md (7411b), rules/center-outward-expansion.md (4263b), rules/chart-scrub-readout.md (10348b), rules/chromatic-glitch.md (10339b), rules/context-sensitive-cursor.md (6938b), rules/control-target-sync.md (10276b), rules/coordinate-target-zoom.md (7607b), rules/counting-dynamic-scale.md (5977b), rules/css-marker-patterns.md (5702b), rules/cursor-click-ripple.md (6397b), rules/cursor-drag.md (10215b), rules/depth-of-field-blur.md (8876b), rules/depth-scatter-assemble.md (7054b), rules/discrete-text-sequence.md (6454b), rules/dynamic-content-sequencing.md (8558b), rules/gradient-text-sweep.md (8751b), rules/gsap-effects.md (6912b), rules/hacker-flip-3d.md (5586b), rules/kinetic-beat-slam.md (6445b), rules/motion-blur-streak.md (16190b), rules/multi-cursor-choreography.md (10520b)\n\nFile v1.0.36:SKILL.md\n\n---\nname: hyperframes-animation\ndescription: \"All animation knowledge for HyperFrames — atomic motion rules, multi-phase scene blueprints, scene transitions, broader motion-design techniques, AND the seven runtime adapters (GSAP default, plus Lottie, Three.js, Anime.js, CSS keyframes, Web Animations API, TypeGPU). Use for any motion or animation task: pick 2-4 rules and compose, or load a blueprint, or look up runtime-specific API (e.g. GSAP eases / Lottie player / Three.js mixer). Also covers auditing an existing composition's choreography (animation map) and 24 named text-animation effects. HyperFrames-native: single paused timeline, seek-safe, deterministic.\"\n---\n\n**Plugin installs:** Before setup or freshness commands, follow [plugin execution rules](../hyperframes/references/plugin-installation.md) when this skill is inside a HyperFrames plugin. Standalone installs keep the update instructions below.\n\n# HyperFrames Animation\n\nAll motion knowledge in one skill: **rules** (atomic recipes), **blueprints** (multi-phase scene templates), **transitions** (scene-to-scene), **techniques** (broader motion-design patterns), and **adapters** (per-runtime APIs).\n\nFor the composition contract (data attributes, sub-compositions, determinism) see `hyperframes-core`.\n\n## Default: compose atomic rules\n\nPick 2-4 rules from `rules-index.md`, glue them together with a single paused GSAP timeline, done. This is faster and produces less code than starting from a blueprint.\n\n## Load a blueprint when\n\n- The scene matches an existing pre-designed multi-phase template (brand-reveal, social-proof, etc.) and reusing its phase pipeline saves real authoring time\n- You want runnable ground-truth code for a complex 4-5 phase choreography\n\nBlueprints live in `blueprints-index.md`. Each entry points to `blueprints/<id>.md` (recipe). Do not read it speculatively; load it when you've already decided you need scene-level orchestration.\n\n## Routing\n\n| Want to…                                                                       | Read                                                |\n| ------------------------------------------------------------------------------ | --------------------------------------------------- |\n| Pick an atomic motion pattern by trigger / tag                                 | `rules-index.md`                                    |\n| Read one rule's full HTML / CSS / GSAP recipe                                  | `rules/<name>.md`                                   |\n| Pick a multi-phase scene template                                              | `blueprints-index.md`                               |\n| Read one blueprint's full recipe                                               | `blueprints/<id>.md`                                |\n| Author a scene transition (CSS-driven, between two clips)                      | `transitions/overview.md`, `transitions/catalog.md` |\n| Look up a broader motion-design technique                                      | `techniques.md`                                     |\n| Motion blur — shutter smear on an element, and when not to use it              | `references/motion-blur.md`                         |\n| Analyze an existing composition's animation map                                | `scripts/animation-map.mjs`                         |\n| GSAP API — timeline / tweens / position parameters                             | `adapters/gsap.md`                                  |\n| GSAP — drop-in effect recipes                                                  | `rules/gsap-effects.md`                             |\n| GSAP — transforms / perf                                                       | `adapters/gsap-transforms-and-perf.md`              |\n| GSAP — eases / stagger                                                         | `adapters/gsap-easing-and-stagger.md`               |\n| GSAP — timeline / labels                                                       | `adapters/gsap-timeline-and-labels.md`              |\n| Lottie / dotLottie (After Effects exports, `window.__hfLottie`)                | `adapters/lottie.md`                                |\n| Character animation (walk cycle, mascot, jointed puppet, gestures)             | `adapters/lottie.md` → Characters                   |\n| Three.js / WebGL (3D scenes, `AnimationMixer`, `hf-seek`)                      | `adapters/three.md`                                 |\n| Anime.js (`window.__hfAnime`)                                                  | `adapters/animejs.md`                               |\n| CSS keyframes (`animation-delay` / `play-state` / `fill-mode`)                 | `adapters/css-animations.md`                        |\n| Web Animations API (`element.animate()`, `currentTime` seek)                   | `adapters/waapi.md`                                 |\n| TypeGPU / WebGPU (`navigator.gpu`, WGSL, compute pipelines)                    | `adapters/typegpu.md`                               |\n| HTML-as-texture + WebGL/GLSL post-fx (capture live DOM via `drawElementImage`) | `adapters/html-in-canvas-patterns.md`               |\n| Named text-animation effects (24 IDs via external `animate-text` skill)        | `adapters/animate-text.md`                          |\n\n## Picking a runtime\n\n- **GSAP** is the default for 95% of motion work — covers timeline orchestration, transforms, easing, stagger. All atomic rules in this skill are GSAP-based.\n- **Lottie** when an asset has its own pre-baked timeline (typically After Effects exports), including characters that walk, gesture or react.\n- **Three.js** for 3D scenes, camera motion, shader-driven visuals.\n- **Anime.js** for lightweight tweening when GSAP is overkill.\n- **CSS** for simple repeated motifs, decoration, shimmer — no JavaScript animation cost.\n- **WAAPI** for native browser keyframes without a GSAP dependency.\n- **TypeGPU / WebGPU** for GPU-rendered canvases (particles, liquid glass, custom shaders).\n\nMultiple runtimes can coexist in one composition. Each registers its instances on the runtime-specific global so HyperFrames can seek all of them in one pass.\n\n## Critical Constraints\n\n**Prerequisite: `hyperframes-core` → One paused timeline + Non-negotiable rules** (single paused timeline, `data-duration` governs length, no `Math.random` / `Date.now` / `performance.now`, no `repeat: -1` without a finite root `data-duration`, no page-load `gsap.set` on later-scene clips, no `display` or raw `visibility` tweens, and register the timeline only after it is fully built, including when the build runs inside an async callback such as `document.fonts.ready`). GSAP `autoAlpha` and zero-duration visibility sets at explicit timeline boundaries remain allowed by core. Use those exceptions only on non-clip elements or wrappers inside a clip; the framework owns `.clip` lifecycle. Don't restate the full contract here.\n\nAnimation-craft additions on top of core's contract:\n\n- **Pre-calculated layout constants** — never derive positions from `getBoundingClientRect()` at tween time. Tween-time DOM measurements desync because the renderer samples in parallel; compute coordinates once at composition setup and reuse.\n- **Spatial motion uses GSAP transform aliases only** (`x`, `y`, `scale`, `rotation`). Core's allowlist also permits `opacity` / `color` / `backgroundColor` / `borderRadius` for non-spatial property tweens — but never `width` / `height` / `top` / `left` for layout changes.\n\n## Scripts\n\n```bash\nnode <SKILL_DIR>/scripts/animation-map.mjs <composition-dir> \\\n  --out <composition-dir>/.hyperframes/anim-map\n```\n\nReads every GSAP timeline registered on `window.__timelines`, enumerates tweens, samples bboxes, computes flags, outputs `animation-map.json`. Use it to audit choreography (dead zones, stagger consistency, lifecycle warnings) after authoring.\n\n`animation-map.mjs` resolves helper packages from the current project first, then can bootstrap the bundled HyperFrames package version. Set `HYPERFRAMES_SKILL_PKG_VERSION=<version>` only when running the skill outside the bundled CLI/skill install and you need to pin that bootstrap version explicitly.\n\n## See Also\n\n- `hyperframes-core` — composition structure, data attributes, sub-compositions, deterministic render contract\n- `hyperframes-creative` — palettes, typography, narration, beat planning (non-animation creative direction)\n- `hyperframes-cli` — `npx hyperframes lint / check / snapshot / preview / render`\n\nFile v1.0.36:_meta.json\n\n{\n  \"ownerId\": \"kn77d06grj6xqp3dqwkk4bavhn89pegt\",\n  \"slug\": \"hyperframes-animation\",\n  \"version\": \"1.0.36\",\n  \"publishedAt\": 1791491483510\n}\n\nFile v1.0.36:references/motion-blur.md\n\n# Motion blur — shutter smear on any animated element\n\n## Read this part before you blur anything\n\nBlur is not polish. It is the smear a real shutter leaves while the subject\ncrosses the frame, so it only reads as correct when the subject crosses enough\nof the frame to have smeared. Applied to motion that was never fast enough, it\nreads as a soft, cheap render: the eye sees mush where it expected an edge.\n\nDo not blur:\n\n| Case                                             | Why                                                                                                    | Do instead                          |\n| ------------------------------------------------ | ------------------------------------------------------------------------------------------------------ | ----------------------------------- |\n| Travel under about one element-width per frame   | The copies pile up inside the element's own silhouette, so the result is a softer element, not a smear | Leave it sharp                      |\n| A fade, a color change, a blur-in, a filter beat | Nothing reaches `transform`, so there is no trajectory to integrate and no smear to draw               | Leave it sharp                      |\n| Text meant to be read at that moment             | A smeared word is an unreadable word, which is usually the opposite of the brief                       | Blur the approach, land sharp, hold |\n| A slow drift, a parallax layer, a breathing loop | Below the half-pixel deadband it renders sharp anyway; above it, it looks like a mistake               | Leave it sharp                      |\n| The whole scene, or a container of many elements | Cost is the copies of an entire subtree, and a container that never moves smears nothing inside it     | Point it at the element that moves  |\n\nBlur the beats that snap: a slam, a whip, a hard cut in position, a spin, a\nscale punch. One to three of them in a composition, not every tween.\n\n## Two routes, and they are not interchangeable\n\n| Route                                   | What it is                                                                                                       | Use when                                                                                                             |\n| --------------------------------------- | ---------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |\n| The `motion-blur` registry component    | A DOM shutter synthesised in the page: N+1 additively blended copies of the element along its sampled trajectory | You want the smear visible in the preview, on selected elements, authored per element                                |\n| The engine's `motionBlur` render option | The renderer integrates real sub-frame samples of the whole frame                                                | You want every moving thing smeared, including canvas, video and transformed ancestors, and only in the final render |\n\nThe component smears what a `transform` can express, on the elements you mark.\nThe engine smears the frame. The component is the one an agent authors; the\nengine option is a render-time decision and shows nothing in a preview.\n\n## The contract: one attribute\n\nPaste the component's snippet into the composition, then mark the element:\n\n```html\n<div id=\"root\" data-composition-id=\"hero\" data-duration=\"4\" data-fps=\"30\">\n  <div id=\"slam\" data-hf-motion-blur></div>\n  <div id=\"title\" data-hf-motion-blur='{\"shutterAngle\": 360}'></div>\n</div>\n```\n\nNothing to call and no ordering to get right. A marked element is attached as\nsoon as its composition's timeline is registered, and the registry is also\npolled for about eight seconds after load, so a composition that mounts\nasynchronously is picked up too. An element takes the timeline registered under\nthe `data-composition-id` of its nearest ancestor carrying that attribute, so a\nsub-composition's targets follow that sub-composition.\n\nEmpty attribute means defaults. Any other value must parse as a JSON object, so\nit needs double quotes on the keys. A value that parses to something else,\n`null` or a bare number, warns and is skipped rather than quietly taken as\ndefaults.\n\n`attachMotionBlur(target, timeline, options)` is for an element created later\nthan that window. It has an ordering contract, after every tween so the\ntimeline's final duration is known, and getting it wrong is silent. For markup\nthat is already in the document, use the attribute.\n\n## Options\n\n| Option            | Default                                                      | Meaning                                                                                                                                                                                                                                                                                                         |\n| ----------------- | ------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `shutterAngle`    | 720                                                          | Degrees of the frame interval the shutter is open. 720 is two frames, measured off a real After Effects export. 360 is one frame. 0 disables the smear                                                                                                                                                          |\n| `shutterPhase`    | -360                                                         | Degrees the window start sits from the frame time. -360 centres the window on the frame                                                                                                                                                                                                                         |\n| `samplesPerFrame` | 16                                                           | Sub-intervals of the window, so this many plus one copies, each at 1 over this many opacity. Max 64                                                                                                                                                                                                             |\n| `fps`             | the `data-fps` of the target's own composition root, else 30 | Composition frame rate. Pass it explicitly when rendering with an fps override                                                                                                                                                                                                                                  |\n| `sharp`           | 1                                                            | 1 paints the element itself, crisp, over its smear. 0 hides it while it moves, leaving only the shutter average: a soft blur with no frame-time edge. It hides through a marker attribute, never the element's own visibility, so autoAlpha still works (an inline `visibility: visible !important` on it wins) |\n\nA key that is none of these five, and a value that is not a finite number, are\nboth refused by name rather than read as defaults. `{\"shutterAngle\": \"720deg\"}`\nis a refusal, not a 720 degree shutter.\n\nThere is no axis, no strength and no radius. The smear is the trajectory,\nintegrated; its extent is speed times shutter time and is not a free parameter.\nA template that passes `axis`, `blurMax` or `blurScale` carries an older fork of\nthis snippet and its options do nothing in the current one.\n\n## The failure modes are all silent\n\nEvery one of these renders a plausible-looking sharp element and reports\nnothing except where noted:\n\n| Symptom                                                                  | Cause                                                                                                               | Fix                                                                                  |\n| ------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ |\n| Console warns that no composition registered a timeline for an element   | The element is outside every `data-composition-id`, or that composition never registers a timeline                  | Put it inside the composition's root, or register the timeline                       |\n| Console warns the attribute is not JSON                                  | Single quotes, unquoted keys, a trailing comma                                                                      | Double-quoted JSON, or an empty attribute for defaults                               |\n| Console warns the attribute is not a JSON object                         | `null`, a bare number, a quoted string, an array                                                                    | An object, or an empty attribute for defaults                                        |\n| Console warns the attribute names no such option                         | A misspelled key, for example `samplesperframe`                                                                     | Use one of the five names above, case-sensitive                                      |\n| Console warns the attribute needs a number for an option                 | A quoted or unit-suffixed value, for example `\"720deg\"`                                                             | A bare JSON number                                                                   |\n| Console warns it cannot blur a target inside another target              | Both an element and one of its ancestors carry the attribute                                                        | Mark one of them, the one that moves                                                 |\n| Console warns one call cannot blur compositions at different frame rates | One `attachMotionBlur` call named elements in two compositions whose `data-fps` differ                              | One call per composition                                                             |\n| Console warns a second timeline registered for an element                | The same `data-composition-id` key was registered twice with different timelines; the copies still follow the first | Register once per composition                                                        |\n| No smear, no warning                                                     | The beat animates `left`, `top`, `width` or `height`; the snippet reads the resolved `transform`                    | Animate `x`, `y`, `scale`, `rotation`                                                |\n| No smear on a container's children                                       | The marked element does not move; its children do                                                                   | Mark the elements that move                                                          |\n| A smear that lags the element                                            | A transformed ancestor is doing the moving                                                                          | Move the element itself, or use the engine route                                     |\n| Blur only on the first frame, or never in a preview                      | The host never seeks the timeline                                                                                   | HyperFrames seeks every frame; a paused timeline nobody seeks shows nothing          |\n| A smear left behind in the old parent                                    | The target was reparented after attaching; the group stays where it was inserted and is never moved                 | Do not reparent a blurred element. Animate `x`/`y` instead, or attach after the move |\n| A selector stops matching after attaching                                | A copy keeps the element's classes, because a class rule is the only thing that can style a copy's pseudo-elements  | Address the element by id or by reference, never by a class a copy also carries      |\n\n## Cost\n\nEach target costs N+1 copies of its whole subtree. Those are restyled on attach\nand on resize, and re-transformed every frame, and the timeline is seeked N+1\ntimes per frame to sample the trajectory. At the default 16 that is 17 copies\nand 17 seeks per target per frame. Three marked elements is fine. Thirty is a\ndifferent render.\n\nDrop `samplesPerFrame` before you drop the effect: 8 halves the cost and the\nstaircase is still smooth on a fast beat.\n\n## The numbers, so you can argue with them\n\nThe defaults are measured against a 1920x1080 30 fps After Effects export of\ntranslating text, not chosen. In that export the outermost trailing copy sits\nexactly at the previous frame's position and the outermost leading copy exactly\nat the next frame's, with 8 evenly spaced copies between on each side: a window\nof two frames, phase minus one frame, 16 sub-intervals. The staircase across a\nstroke steps by 0.063 plus or minus 0.002 of the sharp text intensity, a flat\n1/16 per copy with no taper toward the window edges. Triangle weighting scores\n1.6 dB worse against the same export.\n\nPer-beat PSNR against that reference, sharp render versus this component:\ntranslate +3.38 dB, scale +0.62, rotate X +0.43, rotate Y +0.84, rotate Z\n+1.15. A beat that only scales or only rotates smears, which the earlier\nSVG-filter stage could not do.\n\n## See also\n\n`../../../registry/components/motion-blur/motion-blur.html` is the snippet and its\nfull header. `shutter-slam` is the same model as an installable component: the\nAfter Effects reference case, six beats, elastic to the container.\n\nFile v1.0.36:adapters/animate-text.md\n\n# Text Effects — Reference\n\nFor deterministic text-animation specs (e.g., `typewriter` at exact `240ms / 46ms stagger / steps(1, end) easing`), this skill defers to the separate **`animate-text`** skill maintained by Pixel Point at [github.com/pixel-point/animate-text](https://github.com/pixel-point/animate-text). It provides a catalog of 24 named text effects with portable contracts and per-library implementation recipes (GSAP, Anime.js, WAAPI).\n\n**We do NOT ship the catalog inside this repo.** Pixel Point's `animate-text` is the source of truth; vendoring its files here would violate the upstream's licensing (no explicit license declared upstream as of this writing). Loading the skill separately keeps the legal picture clean while giving you the same catalog.\n\n## How to use it\n\nWhen a beat needs a deterministic text animation, load the upstream skill alongside this one:\n\n```bash\n# In your project root, install the upstream skill into .agents/skills/\nnpx skills add pixel-point/animate-text\n```\n\nOr in a skill-aware agent runtime, the skill is invoked by name:\n\n```\n/animate-text\n```\n\nOnce installed, the specs live at:\n\n```\n.agents/skills/animate-text/assets/effects/<id>.json   # per-library implementation recipe\n.agents/skills/animate-text/assets/specs/<id>.json     # portable motion contract\n```\n\nSub-agents reading those files get exact GSAP timings, easing strings, DOM split rules, and stagger algorithms — no creative invention needed.\n\n## When you don't need the upstream skill\n\nIf a beat's text animation is simple enough to describe in prose (\"headline fades up word-by-word, 80ms stagger\"), implement it inline using the GSAP knowledge already in these skills (`hyperframes-creative` → `references/motion-principles.md` and `references/beat-direction.md`; `hyperframes-animation` → `techniques.md`, entry #4 \"Per-Word Kinetic Typography\"). The upstream catalog is most valuable when:\n\n- You want a specific NAMED effect across multiple beats (so they feel like one design system, not one-offs)\n- You're choosing between several similar effects (typewriter vs per-character-rise vs bottom-up-letters) and want to see all 24 in one place\n- You need layout-aware effects (`kinetic-center-build`, `short-slide-right`, `short-slide-down`) where parameters alone aren't enough — those ship with custom layout algorithms\n\n## Effect names — vocabulary (do NOT use this as the implementation source)\n\nFor convenience while writing storyboards: the upstream skill provides 24 effects. Their IDs are listed here so you can name them in `STORYBOARD.md` even before loading the upstream skill. **The implementation specs are in the upstream skill, not here.**\n\n- **Per-character (7):** soft-blur-in, per-character-rise, typewriter, bottom-up-letters, top-down-letters, stagger-from-center, stagger-from-edges\n- **Per-word (8):** per-word-crossfade, spring-scale-in, shared-axis-y, blur-out-up, kinetic-center-build, short-slide-right, short-slide-down, depth-parallax-words\n- **Per-line (2):** mask-reveal-up, line-by-line-slide\n- **Whole element (7):** micro-scale-fade, shimmer-sweep, fade-through, shared-axis-z, scale-down-fade, focus-blur-resolve, shared-axis-x\n\nFor descriptions, durations, easing curves, and the per-library recipes: load `/animate-text` and read its own catalog page.\n\nFor `mask-reveal-up` and other line-mask effects, leave room for the font's painted glyphs at rest. Tight line-height plus `overflow: hidden` can cut descenders and accents after the reveal finishes. Follow the [text mask guidance](../techniques.md#12-clip-path-reveal-masks): start with block padding of `0.2em` at the text's font size and a compensating negative margin, or release the clip on the timeline once the entrance settles. Inspect a settled snapshot using the actual font and text; `data-layout-allow-overflow` also waives checks in the resting state.\n\n## In the storyboard\n\nEvery text element in every beat can name an effect by ID, e.g.:\n\n```markdown\n**Text Animations:**\n\n- Main headline: `kinetic-center-build`\n- Eyebrow label: `soft-blur-in`\n- Body copy 3 lines: `mask-reveal-up`\n```\n\nSub-agents implementing the beat will load `/animate-text` if it's not already loaded, then read the spec for each named effect from the upstream skill's files.\n\nIf the upstream skill isn't available (offline build, network restrictions, agent runtime that doesn't support skill loading), sub-agents fall back to implementing the effect from the description alone — using GSAP knowledge plus the effect ID as a description of intent (e.g., \"typewriter\" = per-character stepped reveal with no interpolation).\n\nFile v1.0.36:adapters/animejs.md\n\n---\nname: hyperframes-animejs\ndescription: Anime.js adapter patterns for HyperFrames. Use when writing Anime.js animations or timelines inside HyperFrames compositions, registering animations on window.__hfAnime, making Anime.js seek-driven and deterministic, or translating Anime.js examples into render-safe HyperFrames HTML.\n---\n\n# Anime.js for HyperFrames\n\nHyperFrames can seek Anime.js instances through its `animejs` runtime adapter. The composition owns the animation objects; HyperFrames owns the clock.\n\n**This page targets v4 (examples pinned to 4.5.0, MIT).** v4 is a hard break from v3 — there is no callable `anime()`, `easing:` is now `ease:`, and ease names lost their `ease` prefix. Writing v3 from memory produces a composition that throws or silently animates nothing.\n\nThe repo's own producer fixtures pin `animejs@4.0.2/lib/anime.iife.min.js`, which still resolves — but that build predates `splitText` / `scrambleText` / `createSeededRandom` / `createLayout` used below, and 4.1+ moved the bundles to `dist/bundles/`, so a version bump needs the path changed too.\n\n## Contract\n\n- Create animations or timelines synchronously during composition initialization.\n- Set `autoplay: false` so Anime.js does not advance on its own clock.\n- Register every returned animation or timeline on `window.__hfAnime` — **explicitly. There is no working auto-discovery on v4** (see Avoid).\n- Use finite durations and loop counts.\n- Avoid callbacks that mutate DOM based on wall-clock time, network state, or unseeded randomness.\n\nThe adapter seeks every registered instance with `instance.seek(timeMs)`, where `timeMs` is HyperFrames time **in milliseconds** (`ctx.time` seconds × 1000). It also calls `pause()` and `play()` on each instance; anything exposing those three methods works, whatever created it.\n\n## Loading v4\n\n```html\n<!-- UMD: the global `anime` is a NAMESPACE OBJECT, not a function -->\n<script src=\"https://cdn.jsdelivr.net/npm/animejs@4.5.0/dist/bundles/anime.umd.min.js\"></script>\n```\n\n`anime.animate(...)`, `anime.createTimeline(...)`, `anime.utils.*`, `anime.svg.*`, `anime.stagger(...)`. **Calling `anime(...)` is a TypeError** — every v4 build (UMD and IIFE alike) assigns a namespace object to the global, so v3's `anime({ targets })` form cannot work no matter which v4 file you load.\n\n## Basic Pattern\n\n```html\n<script>\n  const anim = anime.animate(\".mark\", {\n    x: 280, // v4 shorthand for translateX\n    rotate: \"1turn\",\n    opacity: [0, 1],\n    duration: 1200,\n    ease: \"outExpo\", // NOT easing: \"easeOutExpo\"\n    autoplay: false,\n  });\n\n  window.__hfAnime = window.__hfAnime || [];\n  window.__hfAnime.push(anim);\n</script>\n```\n\n## Timeline Pattern\n\n`createTimeline` replaces `anime.timeline`, and `add()` takes **targets as its first argument** — `add(targets, parameters, position)`:\n\n```html\n<script>\n  const tl = anime.createTimeline({\n    autoplay: false,\n    defaults: { ease: \"outCubic\" }, // per-timeline defaults, not a bare `easing`\n  });\n\n  tl.add(\".title\", { y: [40, 0], opacity: [0, 1], duration: 650 });\n  tl.add(\".accent\", { scaleX: [0, 1], duration: 450 }, 250); // 250 = time position\n\n  window.__hfAnime = window.__hfAnime || [];\n  window.__hfAnime.push(tl);\n</script>\n```\n\nPosition accepts a number, a label, `\"+=250\"` / `\"-=100\"`, `\"<\"` (previous **end**) and `\"<<\"` (previous **start**).\n\n## Module Builds\n\nThe adapter does not care how the instance was created — only that it exposes `seek()`, `pause()`, and `play()`:\n\n```html\n<script type=\"module\">\n  import { animate } from \"https://cdn.jsdelivr.net/npm/animejs@4.5.0/+esm\";\n\n  const anim = animate(\".chip\", { x: \"18rem\", duration: 900, autoplay: false });\n\n  window.__hfAnime = window.__hfAnime || [];\n  window.__hfAnime.push(anim);\n</script>\n```\n\n## Determinism\n\nv4 ships `createSeededRandom(seed)` — use it instead of `Math.random()` when a composition needs scatter/jitter, so the same frame renders the same on every pass:\n\n```js\nconst rnd = anime.createSeededRandom(1337);\nanime.animate(\".dot\", { y: () => -40 * rnd(), duration: 800, autoplay: false });\n```\n\n`anime.utils.random()` / `randomPick()` / `shuffle()` are **not** seeded — they break frame-to-frame reproducibility.\n\n## Good Uses\n\n- Small SVG and DOM flourishes where Anime.js syntax is compact.\n- Free `splitText` / `scrambleText` (Motion puts these behind Motion+; GSAP SplitText is the other free option).\n- `svg.createDrawable` / `svg.morphTo` / `svg.createMotionPath` line-draw and path work.\n- Multiple independent micro-animations pushed into the same registry.\n\nUse GSAP for complex scene sequencing unless the user specifically asks for Anime.js. GSAP is still the primary HyperFrames authoring path.\n\n## Avoid\n\n- Leaving `autoplay` at the Anime.js default.\n- **Relying on the adapter's `anime.running` auto-discovery — it cannot work on v4.** `running` is not among v4.5.0's exports (verified against the published bundle), so `discover()` returns immediately and any instance you did not `push()` is never seeked. Explicit registration is mandatory, not a nicety.\n- `autoplay: onScroll(...)` — there is no scroll in a headless seek render, so the animation would never advance. Drive it off composition time instead.\n- `waapi.animate()` for anything the adapter must seek — the adapter seeks via `.seek()`, and whether WAAPI-backed instances honor it is **unverified**. Use the JS engine (`animate`) for rendered compositions; `waapi` is an off-main-thread optimization for live pages.\n- `createDraggable`, and any pointer-driven `createAnimatable` loop — input does not exist at render time.\n- Infinite loops. Compute a finite repeat count from the composition duration (v4 `loop` counts **repeats**: `loop: 1` plays twice).\n- Building animations in timers, promises, event handlers, or after async asset loads.\n\n## Validation\n\nAfter editing a composition that uses Anime.js:\n\n```bash\nnpx hyperframes lint\nnpx hyperframes validate\n```\n\n## Credits And References\n\n- HyperFrames adapter source: `packages/core/src/runtime/adapters/animejs.ts`.\n- Anime.js v4 docs: https://animejs.com/documentation/\n- v3 → v4 migration (not on animejs.com): https://github.com/juliangarnier/anime/wiki/Migrating-from-v3-to-v4\n\nFile v1.0.36:adapters/css-animations.md\n\n---\nname: hyperframes-css-animations\ndescription: CSS animation adapter patterns for HyperFrames. Use when authoring CSS keyframes, animation-delay based timing, animation-fill-mode, animation-play-state, or CSS-only motion that HyperFrames must seek deterministically during preview and rendering.\n---\n\n# CSS Animations for HyperFrames\n\nHyperFrames can seek CSS keyframe animations through its `css` runtime adapter. Use this for simple repeated motifs, background motion, shimmer, glow, masks, and non-sequenced decoration.\n\nFor scene choreography, GSAP is usually clearer. CSS animations work best when the motion belongs to one element and has a fixed duration.\n\n## Contract\n\n- Put the animated element in the DOM before runtime initialization finishes.\n- Give timed elements a `data-start` value so local animation time matches the clip.\n- Use finite `animation-duration` and `animation-iteration-count` because the negative-delay fallback cannot represent unbounded duration in environments without WAAPI-backed CSS animations.\n- Prefer `animation-fill-mode: both` so seeked states hold before and after active motion.\n- Avoid wall-clock JavaScript, hover-triggered state, and class toggles that depend on user events.\n\nThe adapter discovers elements with computed `animation-name`, seeks their browser `Animation` handles when available, and falls back to pausing with negative `animation-delay`.\n\n## Basic Pattern\n\n```html\n<div\n  id=\"pulse-ring\"\n  class=\"clip pulse-ring\"\n  data-start=\"0\"\n  data-duration=\"4\"\n  data-track-index=\"2\"\n></div>\n\n<style>\n  .pulse-ring {\n    width: 280px;\n    height: 280px;\n    border: 4px solid rgba(255, 255, 255, 0.7);\n    border-radius: 50%;\n    animation-name: pulse-ring;\n    animation-duration: 1200ms;\n    animation-timing-function: cubic-bezier(0.2, 0, 0, 1);\n    animation-iteration-count: 3;\n    animation-fill-mode: both;\n  }\n\n  @keyframes pulse-ring {\n    from {\n      opacity: 0;\n      transform: scale(0.82);\n    }\n    35% {\n      opacity: 1;\n    }\n    to {\n      opacity: 0;\n      transform: scale(1.18);\n    }\n  }\n</style>\n```\n\n## Stagger Pattern\n\nUse CSS custom properties to avoid duplicating keyframes:\n\n```html\n<div class=\"clip dots\" data-start=\"1\" data-duration=\"3\" data-track-index=\"3\">\n  <span style=\"--i: 0\"></span>\n  <span style=\"--i: 1\"></span>\n  <span style=\"--i: 2\"></span>\n</div>\n\n<style>\n  .dots span {\n    display: inline-block;\n    width: 18px;\n    height: 18px;\n    margin-right: 10px;\n    border-radius: 50%;\n    background: currentColor;\n    animation: dot-pop 900ms ease-out both;\n    animation-delay: calc(var(--i) * 120ms);\n  }\n\n  @keyframes dot-pop {\n    from {\n      opacity: 0;\n      transform: translateY(18px) scale(0.75);\n    }\n    to {\n      opacity: 1;\n      transform: translateY(0) scale(1);\n    }\n  }\n</style>\n```\n\n## Good Uses\n\n- Decorative loops with a known repeat count.\n- Mask, glow, shimmer, grain, and subtle parallax layers.\n- Simple one-element entrances where a full JS timeline would be excessive.\n\n## Avoid\n\n- Infinite CSS animations unless you have verified the browser exposes seekable WAAPI-backed CSS animation handles. Prefer a finite iteration count covering the visible duration. If you do use `infinite`, add `data-duration` to the root element — see Composition Duration below.\n- Animating layout properties like `top`, `left`, `width`, or `height` when transforms work.\n- Relying on hover, focus, scroll, or media queries to trigger render-critical motion.\n- Changing animation classes after startup unless another deterministic timeline controls that change.\n\n## Composition Duration\n\nThe render engine needs to know the composition's total length. GSAP timelines report this automatically; CSS-only compositions have no timeline object, so the runtime infers duration from the longest running animation's computed end time (`animation-delay` + `animation-duration` × finite `animation-iteration-count`, per element with `data-start` added as an offset). `data-duration` on the root element is optional whenever every CSS animation on the page is finite — you don't need to add it just because the composition is CSS-driven.\n\n`animation-iteration-count: infinite` (or any unresolved/unbounded animation) has no finite end time, so it cannot be auto-inferred. If the composition's only animation is infinite, you **must** add `data-duration=\"<seconds>\"` to the root `[data-composition-id]` element with your intended total length — `npx hyperframes lint` errors on this case (`root_composition_missing_duration_source`) precisely because there is nothing for the runtime to infer.\n\n```html\n<div\n  data-composition-id=\"root\"\n  data-start=\"0\"\n  data-duration=\"6\"\n  data-width=\"1920\"\n  data-height=\"1080\"\n>\n  <div class=\"clip spinner\" data-start=\"0\" style=\"animation: spin 1s linear infinite\"></div>\n</div>\n```\n\n## Validation\n\nAfter editing CSS animation compositions:\n\n```bash\nnpx hyperframes lint\nnpx hyperframes check\n```\n\n## Credits And References\n\n- HyperFrames adapter source: `packages/core/src/runtime/adapters/css.ts`.\n- Duration auto-inference: `packages/core/src/runtime/init.ts` (`resolveAdapterDurationFloorSeconds`), `getInferredDurationSeconds` in the adapter above.\n- MDN CSS animation documentation: https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/animation\n- MDN `animation-fill-mode`: https://developer.mozilla.org/en-US/docs/Web/CSS/animation-fill-mode\n\nFile v1.0.36:adapters/gsap-easing-and-stagger.md\n\n# Easing, Stagger, and Function-Based Values\n\n## Easing\n\nBuilt-in eases: `power1`, `power2`, `power3`, `power4`, `back`, `bounce`, `circ`, `elastic`, `expo`, `sine`, `none`.\n\nEach has `.in`, `.out`, `.inOut` variants.\n\n| Ease                                       | Use for                                                                                         |\n| ------------------------------------------ | ----------------------------------------------------------------------------------------------- |\n| `power1.out`, `power2.out`                 | Gentle motion for secondary elements (a caption fade, a small shift). NOT the entrance default. |\n| `power3.out` (house default), `power4.out` | The standard long-tail settle. Entrances, title cards, hero reveals.                            |\n| `sine.inOut`                               | Long, slow, calm motion. Crossfades, ambient drift.                                             |\n| `back.out(1.7)`                            | Overshoot then settle. RARE — explicitly-playful register only, never a default.                |\n| `elastic.out(1, 0.3)`                      | Springy bounce. Same playful-only rule; prefer a baked spring (see Spring Eases below).         |\n| `expo.inOut`                               | Snappy, dramatic. Quick transitions between hero scenes.                                        |\n| `none` (linear)                            | Camera moves with timed counterpoint, mechanical motion.                                        |\n\nPick `.out` for entrances, `.in` for exits, `.inOut` for symmetric moves and continuous motion.\n\n**Smooth beats bouncy** — the motion doctrine (`rules/spring-pop-entrance.md`, the workflows' `motion-language.md`): entrances default to `power3.out` or the baked critically-damped spring (see Spring Eases below); overshoot eases (`back` / `elastic` / `bounce`) are a rare, explicitly-playful register, never the house style.\n\n## Easing Vocabulary (character & mood)\n\nEasings are tone of voice: a video that only whispers is boring; one that varies between whisper, normal, and punch is engaging. A composition should draw on ~3 easing characters across its beats — but vary **within the smooth families by energy** (`sine` / `power1` calm → `power3` standard → `power4` / `expo` punch); don't reach for overshoot to add variety. Overshoot is a _register_ (explicitly playful), not a spice. One ease everywhere reads flat; bounce everywhere reads cheap — the second failure is worse.\n\nThe full palette by character (each family has `.in`, `.out`, `.inOut` variants):\n\n| Family               | Character                                                                    | Typical use                                                                                                                                  |\n| -------------------- | ---------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |\n| `power1`–`power4`    | Gentle (1) to aggressive (4) acceleration curves                             | General purpose. **power3 is the house workhorse**; power2 for gentle secondary motion, power4 for dramatic snaps                            |\n| `back(N)`            | Overshoot then settle. N controls how far past the target (1=subtle, 4=wild) | RARE — explicitly-playful register only, never a default. Keep N ≤ 2; prefer a baked spring at ζ 0.6–0.7 (physical settle, see Spring Eases) |\n| `elastic(amp, freq)` | Spring bounce. amp=magnitude, freq=oscillation speed                         | RARE — same playful-only rule; the baked spring (below) is the physical version                                                              |\n| `bounce`             | Ball-drop bouncing                                                           | RARE — physical-comedy register only (something literally dropping)                                                                          |\n| `expo`               | Extreme acceleration curve (much steeper than power4)                        | Premium/luxury reveals, dramatic entrances                                                                                                   |\n| `sine`               | Smooth, organic, no hard edges                                               | Ambient float, breathing, Ken Burns, anything that loops. `.inOut` for yoyo motion                                                           |\n| `circ`               | Circular acceleration (starts very fast, ends very gentle or vice versa)     | Camera moves, scene transitions, orbital motion                                                                                              |\n| `steps(N)`           | Discrete N-step jumps, no interpolation                                      | Typing effects, cursor blink, counter ticks, retro/digital aesthetics                                                                        |\n\n**Mood mapping:** Match easing character to the beat's emotional content. Smooth/organic easings (`sine`, `power1`) feel contemplative and drifting. Aggressive deceleration (`power4.out`, `expo.out`) feels snappy and confident. Spring overshoot (`back.out`) feels bouncy and physical — but bouncy is a register, not an emphasis tool; reach for it only on explicitly-playful beats. The storyboard's mood description should guide which character fits — not a formula.\n\n## Defaults\n\n```javascript\nconst tl = gsap.timeline({\n  paused: true,\n  defaults: { duration: 0.6, ease: \"power3.out\" }, // the house settle — smooth beats bouncy\n});\n```\n\nOr globally:\n\n```javascript\ngsap.defaults({ duration: 0.6, ease: \"power3.out\" });\n```\n\nSetting defaults at timeline scope is preferred — it documents the motion language of that composition in one place.\n\n## Spring Eases (baked physics, seek-safe)\n\nThe \"iOS feel\" is a **damped spring's velocity curve**, not a bounce: a fast launch into a long asymptotic settle. Well-made system animations are critically damped or close to it — they barely overshoot, or don't at all. `power3.out` / `expo.out` approximate that curve; when you want the exact one — or a _physical_ overshoot for the rare playful register — bake the spring's closed-form solution into a function ease.\n\nWhy not a real-time spring library: an interactive spring is a stateful integrator (velocity accumulates frame to frame), which cannot be seeked deterministically — you'd have to simulate frames 0…N−1 to render frame N. The closed form below is a **pure function of progress** — no state, nothing to desync, seek-safe by construction. This is also why interaction-lib spring solvers are banned in compositions.\n\n```javascript\n// springEase — a damped spring's exact position curve as a GSAP ease.\n// response         ≈ seconds one oscillation would take (0.3–0.6 for entrances)\n// dampingFraction  1.0       = critically damped — smooth settle, NO overshoot (house default)\n//                  0.80–0.85 ≈ the iOS system register — ~1–1.5% overshoot, felt not seen\n//                  0.60–0.70 = explicitly playful — ~5–10% overshoot (rare; replaces back.out)\nfunction springEase({ response = 0.5, dampingFraction = 1 } = {}) {\n  const w = (2 * Math.PI) / response; // undamped natural frequency\n  const z = dampingFraction;\n  let pos; // x(t): 0 → 1, starting at rest (v0 = 0)\n  if (z < 1) {\n    const wd = w * Math.sqrt(1 - z * z);\n    pos = (t) => 1 - Math.exp(-z * w * t) * (Math.cos(wd * t) + ((z * w) / wd) * Math.sin(wd * t));\n  } else if (z > 1) {\n    const wo = w * Math.sqrt(z * z - 1);\n    pos = (t) =>\n      1 - Math.exp(-z * w * t) * (Math.cosh(wo * t) + ((z * w) / wo) * Math.sinh(wo * t));\n  } else {\n    pos = (t) => 1 - Math.exp(-w * t) * (1 + w * t);\n  }\n  // Settle time: last moment the curve sits outside ±0.1% of target.\n  // Fixed-step scan, runs once at setup — deterministic (no Math.random / Date.now).\n  const EPS = 0.001;\n  const rate = z <= 1 ? z * w : (z - Math.sqrt(z * z - 1)) * w; // slowest decay mode\n  const SCAN = 12 / rate;\n  const N = 4800;\n  let T = SCAN;\n  for (let i = N; i >= 0; i--) {\n    const t = (i / N) * SCAN;\n    if (Math.abs(1 - pos(t)) > EPS) {\n      T = ((i + 1) / N) * SCAN;\n      break;\n    }\n  }\n  const xT = pos(T);\n  return {\n    duration: T, // use as the tween's duration — the settle time IS the physics\n    ease: (p) => pos(p * T) + p * (1 - xT), // normalized so ease(1) === 1 exactly\n  };\n}\n```\n\nUsage — take **both** the ease and the duration from the helper (the settle time is part of the physics; overriding the duration just re-times the same curve, so tune speed via `response` instead):\n\n```javascript\nconst settle = springEase({ response: 0.4 }); // critically damped → duration ≈ 0.59s\ntl.fromTo(\n  \"#hero\",\n  { scale: 0, opacity: 0 },\n  { scale: 1, opacity: 1, duration: settle.duration, ease: settle.ease },\n  0.2,\n);\n```\n\n| dampingFraction   | overshoot       | register                                                                                                                                           |\n| ----------------- | --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **1.0 (default)** | none (monotone) | The house settle — the exact curve `power3.out` approximates. Product / enterprise / serious tone.                                                 |\n| 0.80–0.85         | ~1–1.5%         | \"Alive, not bouncy\" — the iOS system default register. The overshoot is felt, not seen.                                                            |\n| 0.60–0.70         | ~5–10%          | Explicitly-playful ONLY (same rule as `back.out`, which this replaces — a spring's second-order settle reads physical where `back` reads cartoon). |\n| < 0.55            | > 12%           | Don't. Cartoon-wobble territory.                                                                                                                   |\n\n| response  | duration (ζ=1) | feel                                                         |\n| --------- | -------------- | ------------------------------------------------------------ |\n| 0.25–0.35 | 0.37–0.51s     | tight snap — chips, small UI                                 |\n| 0.35–0.50 | 0.51–0.74s     | standard entrance                                            |\n| 0.50–0.70 | 0.74–1.03s     | weighted hero landing — check the `t ≤ 0.5s` visibility rule |\n\nCraft notes:\n\n- **ζ=1 vs `power3.out`**: the true spring front-loads harder (~67% vs ~58% travelled at quarter-time) and settles on a longer asymptotic tail; max shape difference ~11%. That long tail is the \"premium\" read — use it when the settle IS the shot (a wordmark landing, a final lockup).\n- **At ζ<1, overshooting curves go on transforms only** — never on `opacity` (it would push past 1) or color. Split opacity onto its own `power2.out` tween at the same timeline position.\n- **Doctrine unchanged**: ζ below ~0.8 is still the rare, explicitly-playful exception (`rules/spring-pop-entrance.md`). The default of this section is ζ=1 — real spring physics is not a license for bounce.\n\n## Stagger\n\n```javascript\ngsap.fromTo(\".item\", { y: 24, opacity: 0 }, { y: 0, opacity: 1, duration: 0.5, stagger: 0.08 });\n```\n\nObject form:\n\n```javascript\ngsap.fromTo(\n  \".item\",\n  { y: 24, opacity: 0 },\n  {\n    y: 0,\n    opacity: 1,\n    stagger: {\n      each: 0.08, // delay between each\n      from: \"center\", // \"start\" | \"end\" | \"center\" | \"edges\" | \"random\" | index\n      amount: 0.6, // total stagger time (overrides each if both set)\n      grid: \"auto\", // for 2D stagger\n      axis: \"x\" | \"y\",\n    },\n  },\n);\n```\n\nPrefer `stagger` over N separate tweens with manual delays — it stays correct when the target count or order changes. Use `fromTo()` rather than `from()` so the start state is explicit (see `gsap-timeline-and-labels.md` → sub-composition entrances).\n\n## Function-Based Values\n\nAny var can be a function `(index, target, targets) => value`:\n\n```javascript\ngsap.to(\".item\", {\n  x: (i, target, targets) => i * 50,\n  rotation: (i) => (i % 2 === 0 ? 5 : -5),\n  stagger: 0.1,\n});\n```\n\nUse this for per-element values that depend on index, attributes, or measured size. Cheaper and more idiomatic than building tweens in a loop.\n\n## gsap.matchMedia (preview only)\n\n`matchMedia` runs setup only when a media query matches and auto-reverts when it stops matching. It is useful for **preview** in the browser at different viewport sizes, and for `prefers-reduced-motion`. It is **not** a substitute for rendering at the composition's actual `data-width`/`data-height` — HyperFrames renders at a fixed viewport.\n\n```javascript\nlet mm = gsap.matchMedia();\nmm.add(\n  {\n    isDesktop: \"(min-width: 800px)\",\n    reduceMotion: \"(prefers-reduced-motion: reduce)\",\n  },\n  (context) => {\n    const { isDesktop, reduceMotion } = context.conditions;\n    gsap.to(\".box\", {\n      rotation: isDesktop ? 360 : 180,\n      duration: reduceMotion ? 0 : 2,\n    });\n  },\n);\n```\n\nFile v1.0.36:adapters/gsap-timeline-and-labels.md\n\n# Timelines and Labels\n\nHyperFrames is a seek-driven runtime. Build one paused timeline per composition, attach it to `window.__timelines[\"<composition-id>\"]`, and let HyperFrames seek it. Never call `.play()` for render-critical motion.\n\n## Creating a Timeline\n\n```javascript\nconst tl = gsap.timeline({\n  paused: true,\n  defaults: { duration: 0.5, ease: \"power3.out\" },\n});\n\ntl.to(\".a\", { x: 100 }).to(\".b\", { y: 50 }).to(\".c\", { opacity: 0 });\n```\n\nTimeline options:\n\n- **paused: true** — required in HyperFrames. The framework drives the playhead.\n- **repeat**, **yoyo** — apply to the whole timeline. `repeat: -1` requires a finite root `data-duration` (export clips to it); otherwise use finite counts.\n- **defaults** — vars merged into every child tween. Use this instead of repeating `ease` and `duration` on every line.\n\n## Position Parameter\n\nThe third argument to `.to()`/`.from()`/`.fromTo()` controls placement on the timeline:\n\n| Form           | Meaning                              |\n| -------------- | ------------------------------------ |\n| `0`, `1.5`     | Absolute time in seconds             |\n| `\"+=0.5\"`      | 0.5s after the end of the timeline   |\n| `\"-=0.2\"`      | 0.2s before the end of the timeline  |\n| `\"intro\"`      | At the `intro` label                 |\n| `\"intro+=0.3\"` | 0.3s after the `intro` label         |\n| `\"<\"`          | Same start as the previous tween     |\n| `\">\"`          | Right after the previous tween ends  |\n| `\"<0.2\"`       | 0.2s after the previous tween starts |\n| `\">-0.1\"`      | 0.1s before the previous tween ends  |\n\n```javascript\ntl.to(\".a\", { x: 100 }, 0);\ntl.to(\".b\", { y: 50 }, \"<\"); // same start as .a\ntl.to(\".c\", { opacity: 0 }, \"<0.2\"); // 0.2s after .b starts\n```\n\nPrefer the position parameter over `delay:` — it composes naturally and survives refactors that re-order tweens.\n\n## Labels\n\n```javascript\ntl.addLabel(\"intro\", 0);\ntl.to(\".a\", { x: 100 }, \"intro\");\n\ntl.addLabel(\"outro\", \"+=0.5\");\ntl.to(\".a\", { opacity: 0 }, \"outro\");\n```\n\nLabels make a long timeline readable and let multiple tweens converge on the same beat without re-typing absolute times.\n\n## Nesting Timelines\n\n```javascript\nconst master = gsap.timeline({ paused: true });\n\nconst child = gsap.timeline();\nchild.to(\".a\", { x: 100 }).to(\".b\", { y: 50 });\n\nmaster.add(child, 0);\n```\n\nIn HyperFrames, **do not** nest sub-composition timelines into the host. Sub-compositions loaded via `data-composition-src` are seeked independently by HyperFrames from their own `data-start`. Nesting is only for grouping pieces of the _same_ composition's timeline.\n\n## Inside Sub-Compositions: prefer `fromTo` over `from`\n\nFor entrance tweens inside a sub-composition, prefer `gsap.fromTo()` over `gsap.from()`:\n\n```javascript\n// Sub-composition entrance — survives re-seek cleanly\ntl.fromTo(\".title\", { y: 60, opacity: 0 }, { y: 0, opacity: 1, duration: 0.6 }, 0.2);\n```\n\nWhy: HyperFrames re-seeks the sub-composition every time its host clip becomes visible. `gsap.from()` snapshots the starting state at **registration time** (page load); when the playhead jumps back past `data-start`, that snapshot can desync from the actual CSS state and the element renders in the wrong position. `gsap.fromTo()` declares both endpoints explicitly, so the seek-back always produces the same start state.\n\nIn top-level (standalone) compositions either form works — there's no re-seek-through-mount cycle.\n\n## Playback Control (debug / preview only)\n\n```javascript\ntl.play();\ntl.pause();\ntl.reverse();\ntl.restart();\ntl.time(2);\ntl.progress(0.5);\ntl.kill();\n```\n\nThese are useful when previewing in the browser. In rendered output HyperFrames calls `seek()` internally — your timeline must produce identical state for the same time value every time it is seeked.\n\nFile v1.0.36:adapters/gsap-transforms-and-perf.md\n\n# Transforms and Performance\n\n## Transform Aliases\n\nPrefer GSAP's transform aliases over raw `transform` strings:\n\n| GSAP property               | Equivalent            |\n| --------------------------- | --------------------- |\n| `x`, `y`, `z`               | `translateX/Y/Z` (px) |\n| `xPercent`, `yPercent`      | `translateX/Y` in `%` |\n| `scale`, `scaleX`, `scaleY` | `scale`               |\n| `rotation`                  | `rotate` (deg)        |\n| `rotationX`, `rotationY`    | 3D rotate             |\n| `skewX`, `skewY`            | `skew`                |\n| `transformOrigin`           | `transform-origin`    |\n\nAliases let GSAP track and interpolate each axis independently, which prevents accidental overwrites between separate tweens on the same element.\n\n## autoAlpha\n\nPrefer `autoAlpha` over `opacity` for show/hide:\n\n```javascript\ngsap.to(\".panel\", { autoAlpha: 0, duration: 0.4 });\n```\n\n`autoAlpha: 0` sets both `opacity: 0` and `visibility: hidden`, which removes the element from hit-testing and accessibility tree at zero alpha — closer to \"gone\" than plain `opacity: 0`. The registered seekable timeline still interpolates only opacity; visibility changes at the hidden endpoint. Use `autoAlpha` only on non-clip elements or wrappers inside a clip; HyperFrames owns `.clip` visibility. Never duration-tween raw `visibility` or `display`.\n\n## clearProps\n\nRemoves inline styles set by GSAP when the tween completes:\n\n```javascript\ngsap.to(\".item\", { x: 100, rotation: 45, clearProps: \"all\" });\ngsap.to(\".item\", { x: 100, rotation: 45, clearProps: \"rotation,x\" });\n```\n\nUseful at the end of an animation segment to hand the element back to CSS.\n\n## CSS Variables\n\n```javascript\ngsap.to(\".chart\", { \"--hue\": 180, duration: 1 });\n```\n\nAnimate any custom property. Works for color, length, number — anything CSS will interpolate.\n\n## Relative and Directional Values\n\n- Relative: `\"+=20\"`, `\"-=10\"`, `\"*=2\"`.\n- Directional rotation: `\"360_cw\"`, `\"-170_short\"`, `\"90_ccw\"` — controls which way the angle takes when going between two values.\n\n## SVG Specifics\n\n- `svgOrigin` sets transform origin in the SVG's global coordinate space (not the element's local box). **Do not** combine `svgOrigin` with `transformOrigin` on the same element — pick one.\n- Animate SVG transform attributes via the same alias names (`x`, `y`, `rotation`) — GSAP handles the SVG-specific quirks.\n- **Resolve SVG geometry before building center-based transforms.** `createElementNS` is supported, but a detached, hidden, or zero-size element may not expose usable geometry when GSAP resolves a percentage `transformOrigin`. Attach and size the SVG before constructing the timeline, use an explicit `svgOrigin` when you know the canvas coordinates, or draw animated geometry around local `(0,0)` inside a positioning `<g>` for a center pivot that does not depend on a measured bounding box.\n\n## Performance Rules\n\n### Animate transforms, not layout properties\n\nAnimate `x`, `y`, `scale`, `rotation`, `opacity`. Never animate `left`, `right`, `top`, `bottom`, `width`, `height`, `margin*`, the text-reflow props `letterSpacing` / `wordSpacing` / `fontSize` — and never `roundProps`.\n\nThis is a **render-correctness** rule in HyperFrames, not just a GPU-performance nicety. The renderer seeks frame-by-frame and screenshots each frame, and the browser compositor snaps layout properties to whole device pixels. On a fast tween the per-frame step is several pixels, so the snap is invisible; on a slow tween or a long ease-out tail the value moves less than a pixel per frame — it holds the same pixel for several frames, then jumps a whole one. The result is motion that looks smooth when fast but visibly stutters when slow. Transforms interpolate sub-pixel and stay smooth at any speed. `roundProps` forces the same integer snap onto a transform — don't use it.\n\n\"Layout property\" is broader than position: anything that triggers **reflow** snaps the same way. `letterSpacing` / `fontSize` are the common trap — a slow \"settle\" that crawls one of them by a fraction of a pixel per frame dwells on a handful of discrete glyph layouts (visible micro-stutter). The faithful smooth fix depends on which property — **do not reach for `scale` reflexively**:\n\n- **`fontSize`** → animate `scale`. Scaling text up/down is the same visual and stays sub-pixel smooth (no reflow).\n- **`letterSpacing` / `wordSpacing`** → uniform `scale` is **not** the same effect (it resizes the glyphs; it does not change the gaps between them). To animate spacing smoothly, split the text into per-character (or per-word) elements and animate each one's `x` — the glyph spread is a transform, sub-pixel smooth and visually identical to a letter-spacing tween. GSAP's `SplitText` does the split. If the spacing change is a minor flourish, hold the final value statically instead.\n\nUnlike positional props, reflow props snap during browser **layout** — upstream of the canvas raster — so they stutter even in html-in-canvas, and the exception below does **not** apply to them.\n\n#### Fixing a flagged animation — preserve the intent\n\nThe lint rule tells you a property will stutter; it does **not** tell you the fix, and a fix that merely passes lint can silently change the look. Swapping a `letterSpacing` tighten for a uniform `scale` lints clean but animates a _different thing_ (it resizes the glyphs instead of closing the gaps). Two rules:\n\n1. **Reproduce the same visual** — same start/end state, same trajectory, only sub-pixel-smooth. Use the faithful equivalent (per-glyph `x` for spacing, `scale` for `fontSize`, `x`/`y` for position), not whichever transform is the least code.\n2. **Verify against the original, not against the linter.** Render the original and the fixed version and compare the motion at its key moments — the fix should differ only by the removed stutter, not by _where things end up_. Lint-clean-and-smooth is not the bar; faithful-and-smooth is.\n\nIf the faithful fix is non-trivial (a per-glyph split, a measured offset), build it or surface the tradeoff — never downgrade to a cheaper, different effect just to satisfy the linter.\n\n**Convert a position animation to a transform** by leaving the element at its resting `left`/`top` in CSS and animating the _offset_ with `x`/`y`:\n\n```javascript\n// CSS: #card { left: 1340px; top: 540px }   ← resting position stays in CSS\ntl.to(\"#card\", { left: 1340, top: 540, duration: 1 }); // ✗ stutters\ntl.fromTo(\"#card\", { x: 640, y: 0 }, { x: 0, y: 0, duration: 1 }); // ✓ x/y = delta from CSS rest (640 = startLeft − 1340)\n```\n\nFor a parent-relative `left: \"100%\"` sweep, use `xPercent: 100` only when the element is the full width of its container; otherwise convert to pixels (`x: containerWidth`).\n\n**The one exception:** elements drawn through the html-in-canvas API — those under a `<canvas layoutsubtree>` ancestor, e.g. the `liquid-glass-*` blocks. The canvas rasterizes from sub-pixel `getComputedStyle`, so layout props don't snap there and those elements keep `left`/`top`. Everything the browser lays out (plain DOM) follows the rule.\n\nThe `gsap_non_transform_motion` lint rule is the backstop, not the teacher — reach for transforms from the start instead of animating layout props and waiting for lint to reject them.\n\n### will-change (sparingly)\n\n```css\n.title {\n  will-change: transform;\n}\n```\n\nOnly on elements that _actually_ animate. Applied everywhere it becomes useless and burns memory.\n\n### gsap.quickTo for frequent updates (preview-only)\n\nFor high-frequency updates driven by **events** — pointer move, scroll, audio scrub — `quickTo` reuses the same tween instead of creating a new one each frame:\n\n```javascript\nconst xTo = gsap.quickTo(\"#cursor\", \"x\", { duration: 0.4, ease: \"power3\" });\nconst yTo = gsap.quickTo(\"#cursor\", \"y\", { duration: 0.4, ease: \"power3\" });\n\ncontainer.addEventListener(\"mousemove\", (e) => {\n  xTo(e.pageX);\n  yTo(e.pageY);\n});\n```\n\n> **Render mode has no input events.** The renderer seeks frame-by-frame; `mousemove`, `scroll`, etc. never fire. `quickTo`'s main use case applies in **live preview** in the browser only. For audio-reactive motion in renders, pre-extract audio data and drive the timeline declaratively (see `../rules/gsap-effects.md`).\n\n### Stagger beats N tweens\n\nOne tween with `stagger` beats N tweens with manual delays for both readability and runtime cost.\n\n### Cleanup\n\nIn live preview, pause or `kill()` off-screen animations. Render mode is unaffected (the renderer drives time directly).\n\nFile v1.0.36:adapters/gsap.md\n\n---\nname: hyperframes-gsap-adapter\ndescription: GSAP animation API reference for HyperFrames. Use when writing seekable GSAP timelines in HyperFrames compositions, including gsap.to(), from(), fromTo(), set(), timeline position parameters, labels, easing, stagger, finite repeats, and transform performance.\n---\n\n# HyperFrames GSAP\n\nGSAP usage scoped to HyperFrames' seek-driven render model. This skill is the GSAP reference _as constrained by HyperFrames_ — for the framework's broader composition contract see `hyperframes-core`.\n\n## HyperFrames Contract\n\nHyperFrames controls GSAP through its `gsap` runtime adapter. Create a paused timeline synchronously, register it on `window.__timelines` with the exact `data-composition-id`, and let HyperFrames seek it.\n\n```html\n<script src=\"https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js\"></script>\n<script>\n  window.__timelines = window.__timelines || {};\n  const tl = gsap.timeline({ paused: true });\n\n  tl.from(\".title\", { y: 48, opacity: 0, duration: 0.6, ease: \"power3.out\" }, 0);\n  tl.to(\".accent\", { scaleX: 1, duration: 0.5, ease: \"power2.out\" }, 0.25);\n\n  window.__timelines[\"main\"] = tl; // key must equal data-composition-id on the composition root\n</script>\n```\n\n- The registry key must match the composition root's `data-composition-id`.\n- Bracket and dot syntax both register: `window.__timelines[\"main\"] = tl` and `window.__timelines.main = tl` are equivalent (the linter recognizes both). Bracket form is required when the id isn't a valid identifier (e.g. contains `-`).\n- Do not call `tl.play()` for render-critical motion.\n- Building inside an async callback such as `document.fonts.ready` is supported and common. What breaks is **registering the key before the build finishes**: an empty timeline registered early is treated as ready and nested empty, so it renders blank (`lint`: `gsap_timeline_registered_before_async_build`, error). Assign `window.__timelines[id] = tl` at the end of the callback. Do not drive render-critical motion from timers or event handlers.\n- Keep loops finite. HyperFrames renders finite video durations.\n- **Render duration comes from `data-duration` on the composition root, not from GSAP timeline length.** Do not pad the timeline with empty tweens like `tl.set({}, {}, 283)` to \"extend\" it. (Some external docs show this trick; in HyperFrames it conflicts with the seek-driven duration model — set `data-duration` instead.)\n\n## Core Tween Methods\n\n- **gsap.to(targets, vars)** — animate from current state to `vars`. Most common.\n- **gsap.from(targets, vars)** — animate from `vars` to current state (entrances).\n- **gsap.fromTo(targets, fromVars, toVars)** — explicit start and end.\n- **gsap.set(targets, vars)** — apply immediately (duration 0).\n\nAlways use **camelCase** property names (e.g. `backgroundColor`, `rotationX`).\n\n## Common vars (cheatsheet)\n\n- **duration** — seconds (default 0.5).\n- **delay** — seconds before start.\n- **ease** — `\"power1.out\"` (default), `\"power3.inOut\"`, `\"back.out(1.7)\"`, `\"elastic.out(1, 0.3)\"`, `\"none\"`. See `./gsap-easing-and-stagger.md`.\n- **stagger** — number or object. See `./gsap-easing-and-stagger.md`.\n- **repeat** — finite number; never `-1` in HyperFrames. Compute repeats from the visible duration.\n- **yoyo** — alternates direction with repeat.\n- **overwrite** — `false` (default), `true`, or `\"auto\"`.\n- **immediateRender** — default `true` for from()/fromTo(). Set `false` on later tweens targeting the same property+element.\n- **onComplete**, **onStart**, **onUpdate** — callbacks.\n\nFor transforms, autoAlpha, clearProps, and SVG specifics see `./gsap-transforms-and-perf.md`.\n\n## Animated Property Allowlist\n\nHyperFrames is stricter than vanilla GSAP. Animate only:\n\n- **Compositor-cheap**: `opacity`, `x`, `y`, `scale`, `scaleX`, `scaleY`, `rotation`, `rotationX`, `rotationY`, `skewX`, `skewY`, `transformOrigin`\n- **Visual fills**: `color`, `backgroundColor`, `borderColor`, `borderRadius`\n- **CSS variables**: `\"--hue\": 180` etc.\n- **Media `volume`** (on `<audio>` / `<video>`): do not tween it for fades or ducking; use the `data-automation` volume lane (`creator-editing-recipes.md` in `hyperframes-core`). A lane wins over a tween on the same element.\n- **DOM text `innerText`** (for numeric counters): tween it directly, e.g. `tl.to(el, { innerText: 100, snap: { innerText: 1 } })` — `snap` keeps it integer; the GSAP inspector recognizes it as a counter. Equivalent to the `onUpdate`-proxy form in `../rules/counting-dynamic-scale.md`; prefer that proxy form for locale formatting (`toLocaleString`) or suffix logic, and pair it with a separate transform-scale tween when the number should grow.\n\n**Avoid** (use the transform alias instead):\n\n- `width` / `height` / `top` / `left` / `right` / `bottom` / `margin*` / `padding*` — trigger layout reflows. Use `scaleX/Y` (with `transformOrigin`) or `x` / `y`.\n\n**Forbidden** (breaks the renderer or the clip lifecycle):\n\n- `display`, raw `visibility` **on a clip element**: never duration-tween these. HyperFrames owns a clip's visibility and `lint` rejects it. Use `autoAlpha` (opacity plus endpoint visibility) or a zero-duration timeline set at an explicit boundary. Animating a clip element's other visual properties is fine and the shipped catalog does it throughout; what is forbidden is taking over its visibility.\n- Anything driven by `Math.random()`, `Date.now()`, `performance.now()`, or event handlers — animation state must be deterministic from time alone.\n\n> **Note**: the list above is a **denylist**, not an allowlist. Properties outside it, including `width`, `height`, `filter`, `clipPath` and `strokeDashoffset`, are legitimate targets; prefer transforms and opacity where you have the choice, for performance rather than correctness. See `hyperframes-core/references/determinism-rules.md` for the full deterministic-render contract.\n\n## References\n\n- `./gsap-timeline-and-labels.md` — timeline creation, position parameter (`+=`, `<`, `>`), labels, nesting, sub-comp `fromTo` preference, playback control.\n- `./gsap-easing-and-stagger.md` — easing families, stagger objects, function-based values, `gsap.matchMedia()`, `gsap.defaults()`.\n- `./gsap-transforms-and-perf.md` — transform aliases, autoAlpha, `quickTo`, `will-change`, performance rules.\n- `../rules/gsap-effects.md` — drop-in recipes: typewriter (with cursor / backspace / word rotation) + audio visualizer (uses `skills/hyperframes-creative/scripts/extract-audio-data.py`).\n\n## Best Practices\n\n- Use camelCase property names; prefer transform aliases and autoAlpha.\n- Prefer timelines over chained tweens with delays; use the position parameter.\n- Add labels with `addLabel()` for readable sequencing.\n- Pass defaults into the timeline constructor.\n- Store the tween/timeline return value when controlling playback.\n\n## Do Not\n\n- Animate layout properties (`width`/`height`/`top`/`left`) when transforms suffice.\n- Use both `svgOrigin` and `transformOrigin` on the same SVG element.\n- Chain animations with `delay` when a timeline can sequence them.\n- Create tweens before the DOM exists.\n- Use infinite `repeat: -1` without a finite root `data-duration` (with one, export clips to that window); when a loop must end before the composition does, use a finite repeat count computed from the visible duration.\n\n## Credits And References\n\n- HyperFrames adapter source: `packages/core/src/runtime/adapters/gsap.ts`.\n- GSAP documentation: https://gsap.com/docs/v3/\n- GSAP timeline pause and seek behavior: https://gsap.com/docs/v3/GSAP/Timeline/pause%28%29/\n\nFile v1.0.36:adapters/html-in-canvas-patterns.md\n\n# HTML-in-Canvas Patterns\n\nHyperFrames' most powerful visual capability. Capture ANY live HTML/CSS as a GPU texture, then render it through WebGL shaders, Three.js 3D scenes, or post-processing effects — at 60fps, pixel-perfect, with every CSS feature supported.\n\n**Read this file when a beat deserves cinematic treatment beyond flat GSAP animations.** Use for 1-3 hero beats per video, not every beat. The rest can use standard GSAP — the contrast between flat beats and HTML-in-Canvas beats IS part of the visual storytelling.\n\n---\n\n## Core Boilerplate (same in every HTML-in-Canvas composition)\n\nEvery HTML-in-Canvas effect shares this structure. Learn this once, adapt it for any effect.\n\n```html\n<!-- 1. Source HTML — your content goes inside a layoutsubtree canvas -->\n<canvas\n  id=\"hic-source\"\n  layoutsubtree\n  width=\"1920\"\n  height=\"1080\"\n  style=\"position:absolute;inset:0;opacity:0;\"\n>\n  <div id=\"hic-content\" style=\"width:1920px;height:1080px;\">\n    <!-- YOUR HTML CONTENT HERE — text, images, cards, dashboards, anything -->\n  </div>\n</canvas>\n\n<!-- 2. Render target — the visible canvas that shows the effect -->\n<canvas id=\"hic-output\" width=\"1920\" height=\"1080\" style=\"position:absolute;inset:0;\"></canvas>\n```\n\n```js\n// 3. Feature detection — always check, always provide fallback\nfunction isHiCSupported() {\n  var tc = document.createElement(\"canvas\");\n  if (!(\"layoutSubtree\" in tc)) return false;\n  tc.setAttribute(\"layoutsubtree\", \"\");\n  var ctx = tc.getContext(\"2d\");\n  return ctx && typeof ctx.drawElementImage === \"function\";\n}\nvar apiOk = isHiCSupported();\n\n// 4. Capture function — call this every frame in onUpdate\nvar capCanvas = document.getElementById(\"hic-source\");\nvar capCtx = capCanvas.getContext(\"2d\");\nfunction captureContent() {\n  if (apiOk) {\n    capCtx.drawElementImage(document.getElementById(\"hic-content\"), 0, 0, 1920, 1080);\n  }\n}\n\n// 5. Drive from GSAP timeline — capture + render every frame\ntl.to(\n  proxy,\n  {\n    /* your animation properties */\n    duration: BEAT_DURATION,\n    ease: \"sine.inOut\",\n    onUpdate: function () {\n      captureContent();\n      // render your effect here (Three.js or WebGL2)\n    },\n  },\n  0,\n);\n```\n\n**Fallback:** When `drawElementImage` is not available (preview without Chrome flag), draw a solid-color placeholder or use Canvas 2D text. The HyperFrames renderer auto-enables the flag — the effect WILL work in the final video. See the liquid-glass block for a complete fallback example.\n\n---\n\n## Effect Catalog\n\n### 1. 3D Rotation with Bloom (Three.js)\n\n**What it looks like:** Content floats in 3D space, slowly rotating with cinematic glow around bright edges. Like a product screenshot displayed in a dark theater.\n\n**When to use:** Hero product showcase, feature reveal, CTA with premium feel.\n\n**Key Three.js components:** `PlaneGeometry` + `CanvasTexture` + `EffectComposer` + `UnrealBloomPass`\n\n```js\n// After the boilerplate above, add:\nvar scene3d = new THREE.Scene();\nvar camera = new THREE.PerspectiveCamera(45, 1920 / 1080, 0.1, 100);\ncamera.position.set(0, 0, 4);\n\nvar renderer = new THREE.WebGLRenderer({\n  canvas: document.getElementById(\"hic-output\"),\n  antialias: true,\n  alpha: true,\n});\nrenderer.setSize(1920, 1080);\n\nvar texture = new THREE.CanvasTexture(capCanvas);\nvar mesh = new THREE.Mesh(\n  new THREE.PlaneGeometry(3.6, 2.2),\n  new THREE.MeshBasicMaterial({ map: texture }),\n);\nscene3d.add(mesh);\n\n// Post-processing: bloom for cinematic glow.\n// EffectComposer / RenderPass / UnrealBloomPass are ES-module named imports\n// (see the import block below) — they're NOT properties of THREE in modern\n// versions. Three.js r150+ removed the UMD `examples/js/` globals.\nvar composer = new EffectComposer(renderer);\ncomposer.addPass(new RenderPass(scene3d, camera));\ncomposer.addPass(new UnrealBloomPass(new THREE.Vector2(1920, 1080), 0.3, 0.4, 0.85));\n\nvar proxy = { rotY: -0.12, zoom: 4.2 };\ntl.to(\n  proxy,\n  {\n    rotY: 0.12,\n    zoom: 3.6,\n    duration: BEAT_DURATION,\n    ease: \"sine.inOut\",\n    onUpdate: function () {\n      captureContent();\n      texture.needsUpdate = true;\n      mesh.rotation.y = proxy.rotY;\n      camera.position.z = proxy.zoom;\n      composer.render();\n    },\n  },\n  0,\n);\n```\n\n**Load Three.js and post-processing via ESM (use a `type=\"module\"` script):**\n\n```html\n<script type=\"module\">\n  import * as THREE from \"https://cdn.jsdelivr.net/npm/three@0.181.2/+esm\";\n  import { EffectComposer } from \"https://cdn.jsdelivr.net/npm/three@0.181.2/examples/jsm/postprocessing/EffectComposer.js\";\n  import { RenderPass } from \"https://cdn.jsdelivr.net/npm/three@0.181.2/examples/jsm/postprocessing/RenderPass.js\";\n  import { ShaderPass } from \"https://cdn.jsdelivr.net/npm/three@0.181.2/examples/jsm/postprocessing/ShaderPass.js\";\n  import { UnrealBloomPass } from \"https://cdn.jsdelivr.net/npm/three@0.181.2/examples/jsm/postprocessing/UnrealBloomPass.js\";\n  // ... rest of composition code using these imports\n</script>\n```\n\nThe `examples/js/` path was removed in Three.js r152. Use `examples/jsm/` (ES modules) with `three@0.181.2` — the version used by the HyperFrames Three.js adapter.\n\n---\n\n### 2. Magnetic Cursor Distortion (Raw WebGL2)\n\n**What it looks like:** Content warps and bends toward a moving point, like a magnet pulling on pixels. Chromatic aberration splits RGB channels at the distortion site.\n\n**When to use:** Interactive feel, product demo with cursor, \"look at THIS feature\" moment.\n\n**Key technique:** Custom fragment shader with Gaussian warp + chromatic split. No Three.js needed — just raw WebGL2.\n\n```js\n// WebGL2 setup\nvar gl = document.getElementById(\"hic-output\").getContext(\"webgl2\", {\n  alpha: false,\n  preserveDrawingBuffer: true,\n});\n\n// Vertex shader — full-screen quad\nvar VS = `#version 300 es\nin vec2 a_pos;\nout vec2 v_uv;\nvoid main() {\n  v_uv = a_pos * 0.5 + 0.5;\n  gl_Position = vec4(a_pos, 0.0, 1.0);\n}`;\n\n// Fragment shader — magnetic warp + chromatic aberration\nvar FS = `#version 300 es\nprecision highp float;\nin vec2 v_uv;\nout vec4 fragColor;\nuniform sampler2D u_tex;\nuniform vec2 u_cursor;   // cursor position (0-1)\nuniform float u_strength; // warp strength (0-1)\n\nvoid main() {\n  vec2 uv = v_uv;\n  vec2 delta = uv - u_cursor;\n  float dist = length(delta);\n  float warp = u_strength * exp(-dist * dist * 8.0);\n  vec2 warped = uv - delta * warp * 0.3;\n\n  // Chromatic aberration at distortion site\n  float aberration = warp * 0.008;\n  float r = texture(u_tex, warped + vec2(aberration, 0.0)).r;\n  float g = texture(u_tex, warped).g;\n  float b = texture(u_tex, warped - vec2(aberration, 0.0)).b;\n  fragColor = vec4(r, g, b, 1.0);\n}`;\n\n// Compile, link, setup quad geometry, upload texture...\n// (See registry/blocks/vfx-magnetic/vfx-magnetic.html for complete implementation)\n\n// Drive cursor position from GSAP\nvar proxy = { cx: 0.2, cy: 0.5, strength: 0.0 };\ntl.to(\n  proxy,\n  {\n    cx: 0.8,\n    cy: 0.4,\n    strength: 1.0,\n    duration: BEAT_DURATION,\n    ease: \"power2.inOut\",\n    onUpdate: function () {\n      captureContent();\n      // Upload texture, set uniforms, draw\n      gl.uniform2f(cursorLoc, proxy.cx, proxy.cy);\n      gl.uniform1f(strengthLoc, proxy.strength);\n      gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);\n    },\n  },\n  0,\n);\n```\n\n---\n\n### 3. Shatter / Fragment Explosion (Three.js)\n\n**What it looks like:** Content breaks into geometric fragments that fly apart, revealing what's behind.\n\n**When to use:** Dramatic transition, \"breaking free\" moment, tension release.\n\n**Key technique:** Subdivide the source texture into triangle mesh fragments using BufferGeometry, then animate each fragment's position/rotation with GSAP.\n\nStudy `registry/blocks/vfx-shatter/vfx-shatter.html` for the complete 1156-line implementation. The core idea:\n\n```js\n// 1. Capture content to texture (same boilerplate)\n// Seeded PRNG for determinism — Math.random() is banned\nfunction mulberry32(seed) {\n  return function () {\n    seed |= 0;\n    seed = (seed + 0x6d2b79f5) | 0;\n    var t = Math.imul(seed ^ (seed >>> 15), 1 | seed);\n    t ^= t + Math.imul(t ^ (t >>> 7), 61 | t);\n    return ((t ^ (t >>> 14)) >>> 0) / 4294967296;\n  };\n}\nvar rng = mulberry32(42);\n\n// 2. Create N triangle fragments from the texture\nvar fragments = [];\nfor (var i = 0; i < NUM_FRAGMENTS; i++) {\n  var geom = new THREE.BufferGeometry();\n  var mesh = new THREE.Mesh(geom, new THREE.MeshBasicMaterial({ map: texture }));\n  scene3d.add(mesh);\n  fragments.push({ mesh: mesh, targetPos: randomExplosionVector(rng), delay: rng() * 0.5 });\n}\n\n// 3. Animate: first hold still, then EXPLODE\ntl.to({}, { duration: holdTime }, 0);\nfragments.forEach(function (frag) {\n  tl.to(\n    frag.mesh.position,\n    {\n      x: frag.targetPos.x,\n      y: frag.targetPos.y,\n      z: frag.targetPos.z,\n      duration: 0.8,\n      ease: \"power3.in\",\n    },\n    holdTime + frag.delay,\n  );\n  tl.to(\n    frag.mesh.rotation,\n    { x: rng() * 4, y: rng() * 4, duration: 0.8, ease: \"power2.in\" },\n    holdTime + frag.delay,\n  );\n});\n```\n\n---\n\n### 4. Liquid / Fluid Surface (Three.js)\n\n**What it looks like:** Content floats above a rippling liquid surface with real-time wave dynamics. Or content IS the surface, undulating like water.\n\n**When to use:** Organic/premium feel, ambient background, \"living\" product showcase.\n\n**Key technique:** Subdivided PlaneGeometry with vertex displacement driven by noise functions in a vertex shader.\n\nStudy `registry/blocks/vfx-liquid-background/vfx-liquid-background.html` for the 1244-line implementation. Core idea:\n\n```js\n// Custom vertex shader with wave displacement\nvar vertexShader = `\n  varying vec2 vUv;\n  uniform float u_time;\n  void main() {\n    vUv = uv;\n    vec3 pos = position;\n    // Sine wave displacement\n    pos.z += sin(pos.x * 3.0 + u_time * 2.0) * 0.15;\n    pos.z += cos(pos.y * 2.5 + u_time * 1.5) * 0.1;\n    gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);\n  }\n`;\n\nvar mesh = new THREE.Mesh(\n  new THREE.PlaneGeometry(4, 3, 64, 64), // heavily subdivided for smooth waves\n  new THREE.ShaderMaterial({\n    vertexShader: vertexShader,\n    fragmentShader: `varying vec2 vUv; uniform sampler2D u_tex;\n      void main() { gl_FragColor = texture2D(u_tex, vUv); }`,\n    uniforms: {\n      u_tex: { value: texture },\n      u_time: { value: 0 },\n    },\n  }),\n);\n```\n\n---\n\n### 5. Portal / Dimensional Reveal (Three.js)\n\n**What it looks like:** A glowing circular portal opens and content emerges through it from another dimension.\n\n**When to use:** Product reveal, \"entering the app\" moment, hero feature introduction.\n\nStudy `registry/blocks/vfx-portal/vfx-portal.html` for the complete 863-line implementation.\n\n---\n\n## When to Use HTML-in-Canvas vs Standard GSAP\n\n| Scenario                         | Use                                  | Why                                  |\n| -------------------------------- | ------------------------------------ | ------------------------------------ |\n| Hero product screenshot showcase | HTML-in-Canvas (3D rotation + bloom) | Makes flat UI feel cinematic         |\n| Feature list / stats             | Standard GSAP                        | Content-focused, doesn't need 3D     |\n| CTA / brand reveal               | HTML-in-Canvas (portal or magnetic)  | Makes the moment memorable           |\n| Social proof / logos             | Standard GSAP                        | Orderly cascade, trust is steady     |\n| Transition between acts          | HTML-in-Canvas (shatter)             | Dramatic act break                   |\n| Background atmosphere            | HTML-in-Canvas (liquid surface)      | Premium ambient feel                 |\n| Quick feature cards              | Standard GSAP                        | Speed matters, 3D would slow it down |\n\n---\n\n## More Effects You Can Build\n\nThese aren't in the VFX blocks — build them yourself from the core boilerplate + a custom fragment shader. Each effect is a single GLSL function applied to the captured texture.\n\n### 6. Noise Dissolve\n\nContent dissolves into noise particles, revealing what's behind. Great for transitions.\n\n```glsl\n// Fragment shader — noise-based dissolve\nuniform float u_progress; // 0.0 = fully visible, 1.0 = fully dissolved\nuniform sampler2D u_tex;\n\nfloat hash(vec2 p) {\n  return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453);\n}\n\nvoid main() {\n  vec2 uv = v_uv;\n  float noise = hash(uv * 50.0);\n  float threshold = u_progress;\n  if (noise < threshold) {\n    // Edge glow at the dissolve boundary\n    float edge = smoothstep(threshold - 0.05, threshold, noise);\n    fragColor = vec4(1.0, 0.6, 0.2, 1.0) * (1.0 - edge); // orange edge glow\n  } else {\n    fragColor = texture(u_tex, uv);\n  }\n}\n```\n\n### 7. Holographic / Iridescent\n\nContent gets a rainbow-shifting holographic sheen that moves with time. Premium, futuristic feel.\n\n```glsl\nuniform float u_time;\nuniform sampler2D u_tex;\n\nvoid main() {\n  vec4 color = texture(u_tex, v_uv);\n  // Iridescent color shift based on position + time\n  float angle = v_uv.x * 6.28 + v_uv.y * 3.14 + u_time * 0.5;\n  vec3 holo = vec3(\n    sin(angle) * 0.5 + 0.5,\n    sin(angle + 2.094) * 0.5 + 0.5,\n    sin(angle + 4.189) * 0.5 + 0.5\n  );\n  // Blend holographic over content (subtle overlay)\n  fragColor = vec4(mix(color.rgb, holo, 0.15 + 0.1 * sin(u_time)), color.a);\n}\n```\n\n### 8. Scan Lines + CRT\n\nRetro CRT monitor look — scan lines, slight curvature, phosphor glow. Great for \"code\" or \"terminal\" beats.\n\n```glsl\nuniform sampler2D u_tex;\nuniform float u_time;\n\nvoid main() {\n  vec2 uv = v_uv;\n  // Barrel distortion (CRT curvature)\n  vec2 centered = uv - 0.5;\n  float dist = dot(centered, centered);\n  uv = uv + centered * dist * 0.15;\n\n  vec4 color = texture(u_tex, uv);\n  // Scan lines\n  float scanline = sin(uv.y * 800.0) * 0.04;\n  color.rgb -= scanline;\n  // Slight RGB offset (phosphor)\n  color.r = texture(u_tex, uv + vec2(0.001, 0.0)).r;\n  color.b = texture(u_tex, uv - vec2(0.001, 0.0)).b;\n  // Vignette\n  float vignette = 1.0 - dist * 2.0;\n  fragColor = vec4(color.rgb * vignette, 1.0);\n}\n```\n\n### 9. Frosted Glass Blur\n\nContent behind frosted glass — visible but softened, with subtle light refraction. Good for \"behind the scenes\" or \"coming soon\" moments.\n\n```glsl\nuniform sampler2D u_tex;\nuniform float u_blur; // 0.0 = clear, 1.0 = full frost\n\nvoid main() {\n  vec2 uv = v_uv;\n  vec4 color = vec4(0.0);\n  // Box blur with offset\n  float radius = u_blur * 0.015;\n  for (float x = -2.0; x <= 2.0; x += 1.0) {\n    for (float y = -2.0; y <= 2.0; y += 1.0) {\n      color += texture(u_tex, uv + vec2(x, y) * radius);\n    }\n  }\n  color /= 25.0;\n  // Add frost noise texture\n  float frost = fract(sin(dot(uv * 200.0, vec2(12.9898, 78.233))) * 43758.5453);\n  color.rgb += frost * 0.03 * u_blur;\n  fragColor = color;\n}\n```\n\n### 10. Pixel Sort / Glitch Art\n\nPixels rearrange themselves in vertical or horizontal strips — digital art aesthetic. Great for tech/creative brands.\n\n```glsl\nuniform sampler2D u_tex;\nuniform float u_intensity; // 0-1\n\nvoid main() {\n  vec2 uv = v_uv;\n  // Random horizontal displacement per row\n  float row = floor(uv.y * 80.0);\n  float noise = fract(sin(row * 127.1) * 43758.5);\n  float displace = step(0.7, noise) * u_intensity * 0.1;\n  // Shift UV with RGB split\n  float r = texture(u_tex, uv + vec2(displace, 0.0)).r;\n  float g = texture(u_tex, uv).g;\n  float b = texture(u_tex, uv - vec2(displace * 0.5, 0.0)).b;\n  fragColor = vec4(r, g, b, 1.0);\n}\n```\n\n---\n\n## Creating ANY Custom Effect\n\nThe fragment shaders above are templates. The pattern is always:\n\n1. **Capture your HTML content** with `drawElementImage` (the boilerplate at the top)\n2. **Upload the captured canvas as a WebGL texture**\n3. **Write a fragment shader** that reads from the texture and outputs modified colors\n4. **Drive shader uniforms from GSAP** via `onUpdate`\n\nAny GLSL effect from ShaderToy, The Book of Shaders, CodePen, or anywhere else can be adapted:\n\n1. Find an effect you like (search \"GLSL [effect name]\" or browse shadertoy.com)\n2. Copy the fragment shader\n3. Replace `iResolution` with `vec2(1920.0, 1080.0)`, `iTime` with your `u_time` uniform\n4. Add `uniform sampler2D u_tex;` for the captured content texture\n5. Wire the uniforms to GSAP proxy values\n\n**Geometry ideas beyond flat planes:**\n\n- `SphereGeometry` — content mapped onto a globe (world map, global reach)\n- `CylinderGeometry` — content on a rotating cylinder (carousel/scroll feel)\n- `TorusGeometry` — content wrapped around a ring (infinity, cycle)\n- `BoxGeometry` — content on a 3D box (product packaging, dice)\n- GLTF models — content mapped as screen texture on phone, laptop, monitor (see `vfx-iphone-device`)\n\n**Post-processing stacking** (Three.js EffectComposer):\n\n- Bloom + film grain = cinematic\n- Bloom + chromatic aberration = lens effect\n- Depth of field + vignette = focused attention\n- Film grain + scan lines = retro\n- Multiple passes stack — add as many as you want\n\n**You are not limited to the effects listed here.** If you can imagine a visual treatment, you can build it. The HTML-in-Canvas API gives you the source material (any HTML rendered as a texture), and WebGL/Three.js gives you unlimited creative control over how that material is presented.\n\nFile v1.0.36:adapters/lottie.md\n\n---\nname: hyperframes-lottie\ndescription: Lottie and dotLottie adapter patterns for HyperFrames. Use when embedding lottie-web JSON animations, .lottie files, @lottiefiles/dotlottie-web players, registering instances on window.__hfLottie, or making After Effects exports deterministic in HyperFrames.\n---\n\n# Lottie for HyperFrames\n\nHyperFrames can seek both `lottie-web` and dotLottie players through its `lottie` runtime adapter. Lottie is a strong fit because the animation timeline is already encoded in the asset; HyperFrames only needs a player object it can seek.\n\n## Contract\n\n- Load assets from local project files, usually under `assets/`.\n- Set `autoplay: false`.\n- Use `loop: false` for a one-shot and `loop: true` for a cycle (walk, idle, spinner). A looping animation is seeked to composition time modulo its own length, so it keeps cycling for the whole scene; a one-shot holds its last frame. Always set `loop`: lottie-web treats a missing `loop` as `true`. A numeric `loop` count does not repeat under seeking; bake the repeats into the file.\n- Register every returned animation or player on `window.__hfLottie`.\n- Keep the Lottie container dimensions stable with CSS.\n\nThe adapter seeks `lottie-web` with `goToAndStop(timeMs, false)` and dotLottie with frame or percentage APIs depending on player shape.\n\n## lottie-web Pattern\n\n```html\n<div id=\"logo-lottie\" class=\"lottie-layer\"></div>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/bodymovin/5.12.2/lottie.min.js\"></script>\n<script>\n  const anim = lottie.loadAnimation({\n    container: document.getElementById(\"logo-lottie\"),\n    renderer: \"svg\",\n    loop: false,\n    autoplay: false,\n    path: \"assets/logo-reveal.json\",\n  });\n\n  window.__hfLottie = window.__hfLottie || [];\n  window.__hfLottie.push(anim);\n</script>\n```\n\n```css\n.lottie-layer {\n  width: 100%;\n  height: 100%;\n}\n```\n\n## dotLottie Pattern\n\n```html\n<canvas id=\"product-lottie\" class=\"lottie-canvas\"></canvas>\n<script src=\"https://unpkg.com/@lottiefiles/dotlottie-web\"></script>\n<script>\n  const player = new DotLottie({\n    canvas: document.getElementById(\"product-lottie\"),\n    src: \"assets/product-flow.lottie\",\n    autoplay: false,\n    loop: false,\n  });\n\n  window.__hfLottie = window.__hfLottie || [];\n  window.__hfLottie.push(player);\n</script>\n```\n\n```css\n.lottie-canvas {\n  width: 100%;\n  height: 100%;\n  display: block;\n}\n```\n\n## Multiple Animations\n\nPush each player into the same registry:\n\n```js\nwindow.__hfLottie = window.__hfLottie || [];\nwindow.__hfLottie.push(backgroundAnim);\nwindow.__hfLottie.push(iconAnim);\nwindow.__hfLottie.push(confettiAnim);\n```\n\nHyperFrames seeks them all to the same composition time.\n\n## Composition Duration\n\nThe render engine needs the composition's total length. GSAP timelines report duration automatically; a Lottie-only composition has no timeline object, so the runtime reads the registered animation's native length directly — `totalFrames / frameRate` for `lottie-web`, or the player's own `duration` for dotLottie. `data-duration` on the root element is optional for Lottie compositions: as long as every animation is registered on `window.__hfLottie` (per the contract above), the runtime has a finite duration to work with. With `loop: true` that length is one cycle, so a looping Lottie in a longer scene needs `data-duration` or a GSAP timeline to set the scene length.\n\n## Characters\n\nFor a character that walks, gestures or reacts (a mascot, a walk cycle, a jointed puppet), put the acting in one Lottie and the stage in GSAP:\n\n- **The Lottie owns the body.** Walk, stop, point and idle live in the file's own timeline, so limbs stay jointed and feet stay planted exactly as the animator made them.\n- **GSAP owns everything around it.** Cards, captions and camera moves go on the paused timeline, timed to the Lottie's beats. Read the beat times from the file's `markers` array (`tm` is in frames, divide by `fr`) or from the animator's notes.\n- **Every registered animation plays against composition time.** There is no per-animation start offset, so a gesture that should begin at 4 s must begin at 4 s inside its file. Author the whole performance as one Lottie, or offset the action inside the file, rather than stacking separate action files.\n- **A short cycle is fine.** A 1 s walk cycle with `loop: true` cycles for the whole scene. Move the character across the stage with GSAP `x` only if the cycle walks in place, and match the travel speed to the stride or the feet slide.\n- **License the character.** Use a file the user or their designer made, or one whose license allows redistribution, and say where it came from. Never ship a character ripped from a site.\n\nThe `lottie-character-walk` registry block is a working example: a jointed flat character walks in on planted feet, stops, and points at a card that GSAP brings in on the point (`npx hyperframes add lottie-character-walk`).\n\n## Good Uses\n\n- After Effects exports that are already known to render correctly in lottie-web.\n- Logo reveals, icon loops, decorative accents, and product UI motion.\n- Characters: walk cycles, mascots, gestures (see Characters above).\n- Translating Remotion Lottie usage into plain HyperFrames HTML.\n\n## Avoid\n\n- Relying on remote `path` URLs at render time.\n- Starting playback with `play()`.\n- Assuming unsupported After Effects effects will survive export. Test the JSON or `.lottie` file in a browser first.\n- Loading a player asynchronously and registering it after HyperFrames validation has already inspected the page.\n\n## Validation\n\nAfter editing a Lottie composition:\n\n```bash\nnpx hyperframes lint\nnpx hyperframes check\n```\n\n## Credits And References\n\n- HyperFrames adapter source: `packages/core/src/runtime/adapters/lottie.ts`.\n- Duration auto-inference: `packages/core/src/runtime/init.ts` (`resolveAdapterDurationFloorSeconds`), `getInferredDurationSeconds` in the adapter above.\n- lottie-web by Airbnb: https://github.com/airbnb/lottie-web\n- lottie-web `loadAnimation` options: https://github.com/airbnb/lottie-web/wiki/loadAnimation-options\n- dotLottie web player methods by LottieFiles: https://developers.lottiefiles.com/docs/dotlottie-player/dotlottie-web/methods\n\nArchive v1.0.35: 124 files, 476517 bytes\n\nFiles: adapters/animate-text.md (4069b), adapters/animejs.md (6224b), adapters/css-animations.md (5407b), adapters/gsap-easing-and-stagger.md (13270b), adapters/gsap-timeline-and-labels.md (3791b), adapters/gsap-transforms-and-perf.md (8552b), adapters/gsap.md (7602b), adapters/html-in-canvas-patterns.md (17166b), adapters/lottie.md (6181b), adapters/three.md (8207b), adapters/typegpu.md (8230b), adapters/waapi.md (4248b), blueprints-index.md (26512b), blueprints/agent-progress-theater.md (16091b), blueprints/camera-journey.md (13648b), blueprints/comparison-split.md (3316b), blueprints/constellation-hub.md (7144b), blueprints/cta-morph-press.md (6407b), blueprints/cursor-ui-demo.md (20276b), blueprints/dataviz-countup.md (14611b), blueprints/device-surface-showcase.md (17151b), blueprints/fixed-anchor-cycle.md (9089b), blueprints/grid-card-assemble.md (15594b), blueprints/kinetic-type-beats.md (29151b), blueprints/logo-assemble-lockup.md (26609b), blueprints/overwhelm-surround.md (6140b), blueprints/panel-edit-live-sync.md (13394b), blueprints/prompt-type-submit-generate.md (18636b), blueprints/spatial-pan-stations.md (4868b), blueprints/ticker-takeover.md (3052b), blueprints/titlecard-reveal.md (7441b), blueprints/transcript-scroll-artifact-reveal.md (11666b), blueprints/typewriter-reveal.md (6276b), blueprints/video-text-pivot.md (3281b), blueprints/zoom-out-workspace-reveal.md (14714b), examples/brand-reveal-assemble-zoom.html (12285b), examples/comparison-split-cards.html (21754b), examples/concept-demo-decode-pan.html (18382b), examples/cta-morph-press.html (15192b), examples/cta-orbit-collapse.html (50646b), examples/demo-page-scroll-spotlight.html (25019b), examples/hook-counter-burst.html (23004b), examples/messaging-multi-phrase.html (12223b), examples/metric-video-text-pivot.html (25640b), examples/problem-mockup-overwhelm.html (44482b), examples/proof-logo-chain.html (28911b), examples/takeover-ticker-displace.html (10947b), examples/workflow-approve-press.html (20656b), references/motion-blur.md (14104b), rules-index.md (21484b), rules/3d-camera-flight.md (18137b), rules/3d-page-scroll.md (7406b), rules/3d-text-depth-layers.md (6708b), rules/ai-tracking-box.md (6562b), rules/ambient-glow-bloom.md (9840b), rules/anchored-layout-expand.md (10313b), rules/asr-keyword-glow.md (6762b), rules/avatar-cloud-network.md (6578b), rules/camera-cursor-tracking.md (7911b), rules/card-morph-anchor.md (7411b), rules/center-outward-expansion.md (4263b), rules/chart-scrub-readout.md (10348b), rules/chromatic-glitch.md (10339b), rules/context-sensitive-cursor.md (6938b), rules/control-target-sync.md (10276b), rules/coordinate-target-zoom.md (7607b), rules/counting-dynamic-scale.md (5977b), rules/css-marker-patterns.md (5702b), rules/cursor-click-ripple.md (6397b), rules/cursor-drag.md (10215b), rules/depth-of-field-blur.md (8876b), rules/depth-scatter-assemble.md (7054b), rules/discrete-text-sequence.md (6454b), rules/dynamic-content-sequencing.md (8558b), rules/gradient-text-sweep.md (8751b), rules/gsap-effects.md (6912b), rules/hacker-flip-3d.md (5586b), rules/kinetic-beat-slam.md (6445b), rules/motion-blur-streak.md (16190b), rules/multi-cursor-choreography.md (10520b)\n\nFile v1.0.35:SKILL.md\n\n---\nname: hyperframes-animation\ndescription: \"All animation knowledge for HyperFrames — atomic motion rules, multi-phase scene blueprints, scene transitions, broader motion-design techniques, AND the seven runtime adapters (GSAP default, plus Lottie, Three.js, Anime.js, CSS keyframes, Web Animations API, TypeGPU). Use for any motion or animation task: pick 2-4 rules and compose, or load a blueprint, or look up runtime-specific API (e.g. GSAP eases / Lottie player / Three.js mixer). Also covers auditing an existing composition's choreography (animation map) and 24 named text-animation effects. HyperFrames-native: single paused timeline, seek-safe, deterministic.\"\n---\n\n**Plugin installs:** Before setup or freshness commands, follow [plugin execution rules](../hyperframes/references/plugin-installation.md) when this skill is inside a HyperFrames plugin. Standalone installs keep the update instructions below.\n\n# HyperFrames Animation\n\nAll motion knowledge in one skill: **rules** (atomic recipes), **blueprints** (multi-phase scene templates), **transitions** (scene-to-scene), **techniques** (broader motion-design patterns), and **adapters** (per-runtime APIs).\n\nFor the composition contract (data attributes, sub-compositions, determinism) see `hyperframes-core`.\n\n## Default: compose atomic rules\n\nPick 2-4 rules from `rules-index.md`, glue them together with a single paused GSAP timeline, done. This is faster and produces less code than starting from a blueprint.\n\n## Load a blueprint when\n\n- The scene matches an existing pre-designed multi-phase template (brand-reveal, social-proof, etc.) and reusing its phase pipeline saves real authoring time\n- You want runnable ground-truth code for a complex 4-5 phase choreography\n\nBlueprints live in `blueprints-index.md`. Each entry points to `blueprints/<id>.md` (recipe). Do not read it speculatively; load it when you've already decided you need scene-level orchestration.\n\n## Routing\n\n| Want to…                                                                       | Read                                                |\n| ------------------------------------------------------------------------------ | --------------------------------------------------- |\n| Pick an atomic motion pattern by trigger / tag                                 | `rules-index.md`                                    |\n| Read one rule's full HTML / CSS / GSAP recipe                                  | `rules/<name>.md`                                   |\n| Pick a multi-phase scene template                                              | `blueprints-index.md`                               |\n| Read one blueprint's full recipe                                               | `blueprints/<id>.md`                                |\n| Author a scene transition (CSS-driven, between two clips)                      | `transitions/overview.md`, `transitions/catalog.md` |\n| Look up a broader motion-design technique                                      | `techniques.md`                                     |\n| Motion blur — shutter smear on an element, and when not to use it              | `references/motion-blur.md`                         |\n| Analyze an existing composition's animation map                                | `scripts/animation-map.mjs`                         |\n| GSAP API — timeline / tweens / position parameters                             | `adapters/gsap.md`                                  |\n| GSAP — drop-in effect recipes                                                  | `rules/gsap-effects.md`                             |\n| GSAP — transforms / perf                                                       | `adapters/gsap-transforms-and-perf.md`              |\n| GSAP — eases / stagger                                                         | `adapters/gsap-easing-and-stagger.md`               |\n| GSAP — timeline / labels                                                       | `adapters/gsap-timeline-and-labels.md`              |\n| Lottie / dotLottie (After Effects exports, `window.__hfLottie`)                | `adapters/lottie.md`                                |\n| Character animation (walk cycle, mascot, jointed puppet, gestures)             | `adapters/lottie.md` → Characters                   |\n| Three.js / WebGL (3D scenes, `AnimationMixer`, `hf-seek`)                      | `adapters/three.md`                                 |\n| Anime.js (`window.__hfAnime`)                                                  | `adapters/animejs.md`                               |\n| CSS keyframes (`animation-delay` / `play-state` / `fill-mode`)                 | `adapters/css-animations.md`                        |\n| Web Animations API (`element.animate()`, `currentTime` seek)                   | `adapters/waapi.md`                                 |\n| TypeGPU / WebGPU (`navigator.gpu`, WGSL, compute pipelines)                    | `adapters/typegpu.md`                               |\n| HTML-as-texture + WebGL/GLSL post-fx (capture live DOM via `drawElementImage`) | `adapters/html-in-canvas-patterns.md`               |\n| Named text-animation effects (24 IDs via external `animate-text` skill)        | `adapters/animate-text.md`                          |\n\n## Picking a runtime\n\n- **GSAP** is the default for 95% of motion work — covers timeline orchestration, transforms, easing, stagger. All atomic rules in this skill are GSAP-based.\n- **Lottie** when an asset has its own pre-baked timeline (typically After Effects exports), including characters that walk, gesture or react.\n- **Three.js** for 3D scenes, camera motion, shader-driven visuals.\n- **Anime.js** for lightweight tweening when GSAP is overkill.\n- **CSS** for simple repeated motifs, decoration, shimmer — no JavaScript animation cost.\n- **WAAPI** for native browser keyframes without a GSAP dependency.\n- **TypeGPU / WebGPU** for GPU-rendered canvases (particles, liquid glass, custom shaders).\n\nMultiple runtimes can coexist in one composition. Each registers its instances on the runtime-specific global so HyperFrames can seek all of them in one pass.\n\n## Critical Constraints\n\n**Prerequisite: `hyperframes-core` → One paused timeline + Non-negotiable rules** (single paused timeline, `data-duration` governs length, no `Math.random` / `Date.now` / `performance.now`, no `repeat: -1` without a finite root `data-duration`, no page-load `gsap.set` on later-scene clips, no `display` or raw `visibility` tweens, and register the timeline only after it is fully built, including when the build runs inside an async callback such as `document.fonts.ready`). GSAP `autoAlpha` and zero-duration visibility sets at explicit timeline boundaries remain allowed by core. Use those exceptions only on non-clip elements or wrappers inside a clip; the framework owns `.clip` lifecycle. Don't restate the full contract here.\n\nAnimation-craft additions on top of core's contract:\n\n- **Pre-calculated layout constants** — never derive positions from `getBoundingClientRect()` at tween time. Tween-time DOM measurements desync because the renderer samples in parallel; compute coordinates once at composition setup and reuse.\n- **Spatial motion uses GSAP transform aliases only** (`x`, `y`, `scale`, `rotation`). Core's allowlist also permits `opacity` / `color` / `backgroundColor` / `borderRadius` for non-spatial property tweens — but never `width` / `height` / `top` / `left` for layout changes.\n\n## Scripts\n\n```bash\nnode <SKILL_DIR>/scripts/animation-map.mjs <composition-dir> \\\n  --out <composition-dir>/.hyperframes/anim-map\n```\n\nReads every GSAP timeline registered on `window.__timelines`, enumerates tweens, samples bboxes, computes flags, outputs `animation-map.json`. Use it to audit choreography (dead zones, stagger consistency, lifecycle warnings) after authoring.\n\n`animation-map.mjs` resolves helper packages from the current project first, then can bootstrap the bundled HyperFrames package version. Set `HYPERFRAMES_SKILL_PKG_VERSION=<version>` only when running the skill outside the bundled CLI/skill install and you need to pin that bootstrap version explicitly.\n\n## See Also\n\n- `hyperframes-core` — composition structure, data attributes, sub-compositions, deterministic render contract\n- `hyperframes-creative` — palettes, typography, narration, beat planning (non-animation creative direction)\n- `hyperframes-cli` — `npx hyperframes lint / check / snapshot / preview / render`\n\nFile v1.0.35:_meta.json\n\n{\n  \"ownerId\": \"kn77d06grj6xqp3dqwkk4bavhn89pegt\",\n  \"slug\": \"hyperframes-animation\",\n  \"version\": \"1.0.35\",\n  \"publishedAt\": 1791337440483\n}\n\nFile v1.0.35:references/motion-blur.md\n\n# Motion blur — shutter smear on any animated element\n\n## Read this part before you blur anything\n\nBlur is not polish. It is the smear a real shutter leaves while the subject\ncrosses the frame, so it only reads as correct when the subject crosses enough\nof the frame to have smeared. Applied to motion that was never fast enough, it\nreads as a soft, cheap render: the eye sees mush where it expected an edge.\n\nDo not blur:\n\n| Case                                             | Why                                                                                                    | Do instead                          |\n| ------------------------------------------------ | ------------------------------------------------------------------------------------------------------ | ----------------------------------- |\n| Travel under about one element-width per frame   | The copies pile up inside the element's own silhouette, so the result is a softer element, not a smear | Leave it sharp                      |\n| A fade, a color change, a blur-in, a filter beat | Nothing reaches `transform`, so there is no trajectory to integrate and no smear to draw               | Leave it sharp                      |\n| Text meant to be read at that moment             | A smeared word is an unreadable word, which is usually the opposite of the brief                       | Blur the approach, land sharp, hold |\n| A slow drift, a parallax layer, a breathing loop | Below the half-pixel deadband it renders sharp anyway; above it, it looks like a mistake               | Leave it sharp                      |\n| The whole scene, or a container of many elements | Cost is the copies of an entire subtree, and a container that never moves smears nothing inside it     | Point it at the element that moves  |\n\nBlur the beats that snap: a slam, a whip, a hard cut in position, a spin, a\nscale punch. One to three of them in a composition, not every tween.\n\n## Two routes, and they are not interchangeable\n\n| Route                                   | What it is                                                                                                       | Use when                                                                                                             |\n| --------------------------------------- | ---------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |\n| The `motion-blur` registry component    | A DOM shutter synthesised in the page: N+1 additively blended copies of the element along its sampled trajectory | You want the smear visible in the preview, on selected elements, authored per element                                |\n| The engine's `motionBlur` render option | The renderer integrates real sub-frame samples of the whole frame                                                | You want every moving thing smeared, including canvas, video and transformed ancestors, and only in the final render |\n\nThe component smears what a `transform` can express, on the elements you mark.\nThe engine smears the frame. The component is the one an agent authors; the\nengine option is a render-time decision and shows nothing in a preview.\n\n## The contract: one attribute\n\nPaste the component's snippet into the composition, then mark the element:\n\n```html\n<div id=\"root\" data-composition-id=\"hero\" data-duration=\"4\" data-fps=\"30\">\n  <div id=\"slam\" data-hf-motion-blur></div>\n  <div id=\"title\" data-hf-motion-blur='{\"shutterAngle\": 360}'></div>\n</div>\n```\n\nNothing to call and no ordering to get right. A marked element is attached as\nsoon as its composition's timeline is registered, and the registry is also\npolled for about eight seconds after load, so a composition that mounts\nasynchronously is picked up too. An element takes the timeline registered under\nthe `data-composition-id` of its nearest ancestor carrying that attribute, so a\nsub-composition's targets follow that sub-composition.\n\nEmpty attribute means defaults. Any other value must parse as a JSON object, so\nit needs double quotes on the keys. A value that parses to something else,\n`null` or a bare number, warns and is skipped rather than quietly taken as\ndefaults.\n\n`attachMotionBlur(target, timeline, options)` is for an element created later\nthan that window. It has an ordering contract, after every tween so the\ntimeline's final duration is known, and getting it wrong is silent. For markup\nthat is already in the document, use the attribute.\n\n## Options\n\n| Option            | Default                                                      | Meaning                                                                                                                                                                                                                                                                                                         |\n| ----------------- | ------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `shutterAngle`    | 720                                                          | Degrees of the frame interval the shutter is open. 720 is two frames, measured off a real After Effects export. 360 is one frame. 0 disables the smear                                                                                                                                                          |\n| `shutterPhase`    | -360                                                         | Degrees the window start sits from the frame time. -360 centres the window on the frame                                                                                                                                                                                                                         |\n| `samplesPerFrame` | 16                                                           | Sub-intervals of the window, so this many plus one copies, each at 1 over this many opacity. Max 64                                                                                                                                                                                                             |\n| `fps`             | the `data-fps` of the target's own composition root, else 30 | Composition frame rate. Pass it explicitly when rendering with an fps override                                                                                                                                                                                                                                  |\n| `sharp`           | 1                                                            | 1 paints the element itself, crisp, over its smear. 0 hides it while it moves, leaving only the shutter average: a soft blur with no frame-time edge. It hides through a marker attribute, never the element's own visibility, so autoAlpha still works (an inline `visibility: visible !important` on it wins) |\n\nA key that is none of these five, and a value that is not a finite number, are\nboth refused by name rather than read as defaults. `{\"shutterAngle\": \"720deg\"}`\nis a refusal, not a 720 degree shutter.\n\nThere is no axis, no strength and no radius. The smear is the trajectory,\nintegrated; its extent is speed times shutter time and is not a free parameter.\nA template that passes `axis`, `blurMax` or `blurScale` carries an older fork of\nthis snippet and its options do nothing in the current one.\n\n## The failure modes are all silent\n\nEvery one of these renders a plausible-looking sharp element and reports\nnothing except where noted:\n\n| Symptom                                                                  | Cause                                                                                                               | Fix                                                                                  |\n| ------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ |\n| Console warns that no composition registered a timeline for an element   | The element is outside every `data-composition-id`, or that composition never registers a timeline                  | Put it inside the composition's root, or register the timeline                       |\n| Console warns the attribute is not JSON                                  | Single quotes, unquoted keys, a trailing comma                                                                      | Double-quoted JSON, or an empty attribute for defaults                               |\n| Console warns the attribute is not a JSON object                         | `null`, a bare number, a quoted string, an array                                                                    | An object, or an empty attribute for defaults                                        |\n| Console warns the attribute names no such option                         | A misspelled key, for example `samplesperframe`                                                                     | Use one of the five names above, case-sensitive                                      |\n| Console warns the attribute needs a number for an option                 | A quoted or unit-suffixed value, for example `\"720deg\"`                                                             | A bare JSON number                                                                   |\n| Console warns it cannot blur a target inside another target              | Both an element and one of its ancestors carry the attribute                                                        | Mark one of them, the one that moves                                                 |\n| Console warns one call cannot blur compositions at different frame rates | One `attachMotionBlur` call named elements in two compositions whose `data-fps` differ                              | One call per composition                                                             |\n| Console warns a second timeline registered for an element                | The same `data-composition-id` key was registered twice with different timelines; the copies still follow the first | Register once per composition                                                        |\n| No smear, no warning                                                     | The beat animates `left`, `top`, `width` or `height`; the snippet reads the resolved `transform`                    | Animate `x`, `y`, `scale`, `rotation`                                                |\n| No smear on a container's children                                       | The marked element does not move; its children do                                                                   | Mark the elements that move                                                          |\n| A smear that lags the element                                            | A transformed ancestor is doing the moving                                                                          | Move the element itself, or use the engine route                                     |\n| Blur only on the first frame, or never in a preview                      | The host never seeks the timeline                                                                                   | HyperFrames seeks every frame; a paused timeline nobody seeks shows nothing          |\n| A smear left behind in the old parent                                    | The target was reparented after attaching; the group stays where it was inserted and is never moved                 | Do not reparent a blurred element. Animate `x`/`y` instead, or attach after the move |\n| A selector stops matching after attaching                                | A copy keeps the element's classes, because a class rule is the only thing that can style a copy's pseudo-elements  | Address the element by id or by reference, never by a class a copy also carries      |\n\n## Cost\n\nEach target costs N+1 copies of its whole subtree. Those are restyled on attach\nand on resize, and re-transformed every frame, and the timeline is seeked N+1\ntimes per frame to sample the trajectory. At the default 16 that is 17 copies\nand 17 seeks per target per frame. Three marked elements is fine. Thirty is a\ndifferent render.\n\nDrop `samplesPerFrame` before you drop the effect: 8 halves the cost and the\nstaircase is still smooth on a fast beat.\n\n## The numbers, so you can argue with them\n\nThe defaults are measured against a 1920x1080 30 fps After Effects export of\ntranslating text, not chosen. In that export the outermost trailing copy sits\nexactly at the previous frame's position and the outermost leading copy exactly\nat the next frame's, with 8 evenly spaced copies between on each side: a window\nof two frames, phase minus one frame, 16 sub-intervals. The staircase across a\nstroke steps by 0.063 plus or minus 0.002 of the sharp text intensity, a flat\n1/16 per copy with no taper toward the window edges. Triangle weighting scores\n1.6 dB worse against the same export.\n\nPer-beat PSNR against that reference, sharp render versus this component:\ntranslate +3.38 dB, scale +0.62, rotate X +0.43, rotate Y +0.84, rotate Z\n+1.15. A beat that only scales or only rotates smears, which the earlier\nSVG-filter stage could not do.\n\n## See also\n\n`../../../registry/components/motion-blur/motion-blur.html` is the snippet and its\nfull header. `shutter-slam` is the same model as an installable component: the\nAfter Effects reference case, six beats, elastic to the container.\n\nFile v1.0.35:adapters/animate-text.md\n\n# Text Effects — Reference\n\nFor deterministic text-animation specs (e.g., `typewriter` at exact `240ms / 46ms stagger / steps(1, end) easing`), this skill defers to the separate **`animate-text`** skill maintained by Pixel Point at [github.com/pixel-point/animate-text](https://github.com/pixel-point/animate-text). It provides a catalog of 24 named text effects with portable contracts and per-library implementation recipes (GSAP, Anime.js, WAAPI).\n\n**We do NOT ship the catalog inside this repo.** Pixel Point's `animate-text` is the source of truth; vendoring its files here would violate the upstream's licensing (no explicit license declared upstream as of this writing). Loading the skill separately keeps the legal picture clean while giving you the same catalog.\n\n## How to use it\n\nWhen a beat needs a deterministic text animation, load the upstream skill alongside this one:\n\n```bash\n# In your project root, install the upstream skill into .agents/skills/\nnpx skills add pixel-point/animate-text\n```\n\nOr in a skill-aware agent runtime, the skill is invoked by name:\n\n```\n/animate-text\n```\n\nOnce installed, the specs live at:\n\n```\n.agents/skills/animate-text/assets/effects/<id>.json   # per-library implementation recipe\n.agents/skills/animate-text/assets/specs/<id>.json     # portable motion contract\n```\n\nSub-agents reading those files get exact GSAP timings, easing strings, DOM split rules, and stagger algorithms — no creative invention needed.\n\n## When you don't need the upstream skill\n\nIf a beat's text animation is simple enough to describe in prose (\"headline fades up word-by-word, 80ms stagger\"), implement it inline using the GSAP knowledge already in these skills (`hyperframes-creative` → `references/motion-principles.md` and `references/beat-direction.md`; `hyperframes-animation` → `techniques.md`, entry #4 \"Per-Word Kinetic Typography\"). The upstream catalog is most valuable when:\n\n- You want a specific NAMED effect across multiple beats (so they feel like one design system, not one-offs)\n- You're choosing between several similar effects (typewriter vs per-character-rise vs bottom-up-letters) and want to see all 24 in one place\n- You need layout-aware effects (`kinetic-center-build`, `short-slide-right`, `short-slide-down`) where parameters alone aren't enough — those ship with custom layout algorithms\n\n## Effect names — vocabulary (do NOT use this as the implementation source)\n\nFor convenience while writing storyboards: the upstream skill provides 24 effects. Their IDs are listed here so you can name them in `STORYBOARD.md` even before loading the upstream skill. **The implementation specs are in the upstream skill, not here.**\n\n- **Per-character (7):** soft-blur-in, per-character-rise, typewriter, bottom-up-letters, top-down-letters, stagger-from-center, stagger-from-edges\n- **Per-word (8):** per-word-crossfade, spring-scale-in, shared-axis-y, blur-out-up, kinetic-center-build, short-slide-right, short-slide-down, depth-parallax-words\n- **Per-line (2):** mask-reveal-up, line-by-line-slide\n- **Whole element (7):** micro-scale-fade, shimmer-sweep, fade-through, shared-axis-z, scale-down-fade, focus-blur-resolve, shared-axis-x\n\nFor descriptions, durations, easing curves, and the per-library recipes: load `/animate-text` and read its own catalog page.\n\n## In the storyboard\n\nEvery text element in every beat can name an effect by ID, e.g.:\n\n```markdown\n**Text Animations:**\n\n- Main headline: `kinetic-center-build`\n- Eyebrow label: `soft-blur-in`\n- Body copy 3 lines: `mask-reveal-up`\n```\n\nSub-agents implementing the beat will load `/animate-text` if it's not already loaded, then read the spec for each named effect from the upstream skill's files.\n\nIf the upstream skill isn't available (offline build, network restrictions, agent runtime that doesn't support skill loading), sub-agents fall back to implementing the effect from the description alone — using GSAP knowledge plus the effect ID as a description of intent (e.g., \"typewriter\" = per-character stepped reveal with no interpolation).\n\nFile v1.0.35:adapters/animejs.md\n\n---\nname: hyperframes-animejs\ndescription: Anime.js adapter patterns for HyperFrames. Use when writing Anime.js animations or timelines inside HyperFrames compositions, registering animations on window.__hfAnime, making Anime.js seek-driven and deterministic, or translating Anime.js examples into render-safe HyperFrames HTML.\n---\n\n# Anime.js for HyperFrames\n\nHyperFrames can seek Anime.js instances through its `animejs` runtime adapter. The composition owns the animation objects; HyperFrames owns the clock.\n\n**This page targets v4 (examples pinned to 4.5.0, MIT).** v4 is a hard break from v3 — there is no callable `anime()`, `easing:` is now `ease:`, and ease names lost their `ease` prefix. Writing v3 from memory produces a composition that throws or silently animates nothing.\n\nThe repo's own producer fixtures pin `animejs@4.0.2/lib/anime.iife.min.js`, which still resolves — but that build predates `splitText` / `scrambleText` / `createSeededRandom` / `createLayout` used below, and 4.1+ moved the bundles to `dist/bundles/`, so a version bump needs the path changed too.\n\n## Contract\n\n- Create animations or timelines synchronously during composition initialization.\n- Set `autoplay: false` so Anime.js does not advance on its own clock.\n- Register every returned animation or timeline on `window.__hfAnime` — **explicitly. There is no working auto-discovery on v4** (see Avoid).\n- Use finite durations and loop counts.\n- Avoid callbacks that mutate DOM based on wall-clock time, network state, or unseeded randomness.\n\nThe adapter seeks every registered instance with `instance.seek(timeMs)`, where `timeMs` is HyperFrames time **in milliseconds** (`ctx.time` seconds × 1000). It also calls `pause()` and `play()` on each instance; anything exposing those three methods works, whatever created it.\n\n## Loading v4\n\n```html\n<!-- UMD: the global `anime` is a NAMESPACE OBJECT, not a function -->\n<script src=\"https://cdn.jsdelivr.net/npm/animejs@4.5.0/dist/bundles/anime.umd.min.js\"></script>\n```\n\n`anime.animate(...)`, `anime.createTimeline(...)`, `anime.utils.*`, `anime.svg.*`, `anime.stagger(...)`. **Calling `anime(...)` is a TypeError** — every v4 build (UMD and IIFE alike) assigns a namespace object to the global, so v3's `anime({ targets })` form cannot work no matter which v4 file you load.\n\n## Basic Pattern\n\n```html\n<script>\n  const anim = anime.animate(\".mark\", {\n    x: 280, // v4 shorthand for translateX\n    rotate: \"1turn\",\n    opacity: [0, 1],\n    duration: 1200,\n    ease: \"outExpo\", // NOT easing: \"easeOutExpo\"\n    autoplay: false,\n  });\n\n  window.__hfAnime = window.__hfAnime || [];\n  window.__hfAnime.push(anim);\n</script>\n```\n\n## Timeline Pattern\n\n`createTimeline` replaces `anime.timeline`, and `add()` takes **targets as its first argument** — `add(targets, parameters, position)`:\n\n```html\n<script>\n  const tl = anime.createTimeline({\n    autoplay: false,\n    defaults: { ease: \"outCubic\" }, // per-timeline defaults, not a bare `easing`\n  });\n\n  tl.add(\".title\", { y: [40, 0], opacity: [0, 1], duration: 650 });\n  tl.add(\".accent\", { scaleX: [0, 1], duration: 450 }, 250); // 250 = time position\n\n  window.__hfAnime = window.__hfAnime || [];\n  window.__hfAnime.push(tl);\n</script>\n```\n\nPosition accepts a number, a label, `\"+=250\"` / `\"-=100\"`, `\"<\"` (previous **end**) and `\"<<\"` (previous **start**).\n\n## Module Builds\n\nThe adapter does not care how the instance was created — only that it exposes `seek()`, `pause()`, and `play()`:\n\n```html\n<script type=\"module\">\n  import { animate } from \"https://cdn.jsdelivr.net/npm/animejs@4.5.0/+esm\";\n\n  const anim = animate(\".chip\", { x: \"18rem\", duration: 900, autoplay: false });\n\n  window.__hfAnime = window.__hfAnime || [];\n  window.__hfAnime.push(anim);\n</script>\n```\n\n## Determinism\n\nv4 ships `createSeededRandom(seed)` — use it instead of `Math.random()` when a composition needs scatter/jitter, so the same frame renders the same on every pass:\n\n```js\nconst rnd = anime.createSeededRandom(1337);\nanime.animate(\".dot\", { y: () => -40 * rnd(), duration: 800, autoplay: false });\n```\n\n`anime.utils.random()` / `randomPick()` / `shuffle()` are **not** seeded — they break frame-to-frame reproducibility.\n\n## Good Uses\n\n- Small SVG and DOM flourishes where Anime.js syntax is compact.\n- Free `splitText` / `scrambleText` (Motion puts these behind Motion+; GSAP SplitText is the other free option).\n- `svg.createDrawable` / `svg.morphTo` / `svg.createMotionPath` line-draw and path work.\n- Multiple independent micro-animations pushed into the same registry.\n\nUse GSAP for complex scene sequencing unless the user specifically asks for Anime.js. GSAP is still the primary HyperFrames authoring path.\n\n## Avoid\n\n- Leaving `autoplay` at the Anime.js default.\n- **Relying on the adapter's `anime.running` auto-discovery — it cannot work on v4.** `running` is not among v4.5.0's exports (verified against the published bundle), so `discover()` returns immediately and any instance you did not `push()` is never seeked. Explicit registration is mandatory, not a nicety.\n- `autoplay: onScroll(...)` — there is no scroll in a headless seek render, so the animation would never advance. Drive it off composition time instead.\n- `waapi.animate()` for anything the adapter must seek — the adapter seeks via `.seek()`, and whether WAAPI-backed instances honor it is **unverified**. Use the JS engine (`animate`) for rendered compositions; `waapi` is an off-main-thread optimization for live pages.\n- `createDraggable`, and any pointer-driven `createAnimatable` loop — input does not exist at render time.\n- Infinite loops. Compute a finite repeat count from the composition duration (v4 `loop` counts **repeats**: `loop: 1` plays twice).\n- Building animations in timers, promises, event handlers, or after async asset loads.\n\n## Validation\n\nAfter editing a composition that uses Anime.js:\n\n```bash\nnpx hyperframes lint\nnpx hyperframes validate\n```\n\n## Credits And References\n\n- HyperFrames adapter source: `packages/core/src/runtime/adapters/animejs.ts`.\n- Anime.js v4 docs: https://animejs.com/documentation/\n- v3 → v4 migration (not on animejs.com): https://github.com/juliangarnier/anime/wiki/Migrating-from-v3-to-v4\n\nFile v1.0.35:adapters/css-animations.md\n\n---\nname: hyperframes-css-animations\ndescription: CSS animation adapter patterns for HyperFrames. Use when authoring CSS keyframes, animation-delay based timing, animation-fill-mode, animation-play-state, or CSS-only motion that HyperFrames must seek deterministically during preview and rendering.\n---\n\n# CSS Animations for HyperFrames\n\nHyperFrames can seek CSS keyframe animations through its `css` runtime adapter. Use this for simple repeated motifs, background motion, shimmer, glow, masks, and non-sequenced decoration.\n\nFor scene choreography, GSAP is usually clearer. CSS animations work best when the motion belongs to one element and has a fixed duration.\n\n## Contract\n\n- Put the animated element in the DOM before runtime initialization finishes.\n- Give timed elements a `data-start` value so local animation time matches the clip.\n- Use finite `animation-duration` and `animation-iteration-count` because the negative-delay fallback cannot represent unbounded duration in environments without WAAPI-backed CSS animations.\n- Prefer `animation-fill-mode: both` so seeked states hold before and after active motion.\n- Avoid wall-clock JavaScript, hover-triggered state, and class toggles that depend on user events.\n\nThe adapter discovers elements with computed `animation-name`, seeks their browser `Animation` handles when available, and falls back to pausing with negative `animation-delay`.\n\n## Basic Pattern\n\n```html\n<div\n  id=\"pulse-ring\"\n  class=\"clip pulse-ring\"\n  data-start=\"0\"\n  data-duration=\"4\"\n  data-track-index=\"2\"\n></div>\n\n<style>\n  .pulse-ring {\n    width: 280px;\n    height: 280px;\n    border: 4px solid rgba(255, 255, 255, 0.7);\n    border-radius: 50%;\n    animation-name: pulse-ring;\n    animation-duration: 1200ms;\n    animation-timing-function: cubic-bezier(0.2, 0, 0, 1);\n    animation-iteration-count: 3;\n    animation-fill-mode: both;\n  }\n\n  @keyframes pulse-ring {\n    from {\n      opacity: 0;\n      transform: scale(0.82);\n    }\n    35% {\n      opacity: 1;\n    }\n    to {\n      opacity: 0;\n      transform: scale(1.18);\n    }\n  }\n</style>\n```\n\n## Stagger Pattern\n\nUse CSS custom properties to avoid duplicating keyframes:\n\n```html\n<div class=\"clip dots\" data-start=\"1\" data-duration=\"3\" data-track-index=\"3\">\n  <span style=\"--i: 0\"></span>\n  <span style=\"--i: 1\"></span>\n  <span style=\"--i: 2\"></span>\n</div>\n\n<style>\n  .dots span {\n    display: inline-block;\n    width: 18px;\n    height: 18px;\n    margin-right: 10px;\n    border-radius: 50%;\n    background: currentColor;\n    animation: dot-pop 900ms ease-out both;\n    animation-delay: calc(var(--i) * 120ms);\n  }\n\n  @keyframes dot-pop {\n    from {\n      opacity: 0;\n      transform: translateY(18px) scale(0.75);\n    }\n    to {\n      opacity: 1;\n      transform: translateY(0) scale(1);\n    }\n  }\n</style>\n```\n\n## Good Uses\n\n- Decorative loops with a known repeat count.\n- Mask, glow, shimmer, grain, and subtle parallax layers.\n- Simple one-element entrances where a full JS timeline would be excessive.\n\n## Avoid\n\n- Infinite CSS animations unless you have verified the browser exposes seekable WAAPI-backed CSS animation handles. Prefer a finite iteration count covering the visible duration. If you do use `infinite`, add `data-duration` to the root element — see Composition Duration below.\n- Animating layout properties like `top`, `left`, `width`, or `height` when transforms work.\n- Relying on hover, focus, scroll, or media queries to trigger render-critical motion.\n- Changing animation classes after startup unless another deterministic timeline controls that change.\n\n## Composition Duration\n\nThe render engine needs to know the composition's total length. GSAP timelines report this automatically; CSS-only compositions have no timeline object, so the runtime infers duration from the longest running animation's computed end time (`animation-delay` + `animation-duration` × finite `animation-iteration-count`, per element with `data-start` added as an offset). `data-duration` on the root element is optional whenever every CSS animation on the page is finite — you don't need to add it just because the composition is CSS-driven.\n\n`animation-iteration-count: infinite` (or any unresolved/unbounded animation) has no finite end time, so it cannot be auto-inferred. If the composition's only animation is infinite, you **must** add `data-duration=\"<seconds>\"` to the root `[data-composition-id]` element with your intended total length — `npx hyperframes lint` errors on this case (`root_composition_missing_duration_source`) precisely because there is nothing for the runtime to infer.\n\n```html\n<div\n  data-composition-id=\"root\"\n  data-start=\"0\"\n  data-duration=\"6\"\n  data-width=\"1920\"\n  data-height=\"1080\"\n>\n  <div class=\"clip spinner\" data-start=\"0\" style=\"animation: spin 1s linear infinite\"></div>\n</div>\n```\n\n## Validation\n\nAfter editing CSS animation compositions:\n\n```bash\nnpx hyperframes lint\nnpx hyperframes check\n```\n\n## Credits And References\n\n- HyperFrames adapter source: `packages/core/src/runtime/adapters/css.ts`.\n- Duration auto-inference: `packages/core/src/runtime/init.ts` (`resolveAdapterDurationFloorSeconds`), `getInferredDurationSeconds` in the adapter above.\n- MDN CSS animation documentation: https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/animation\n- MDN `animation-fill-mode`: https://developer.mozilla.org/en-US/docs/Web/CSS/animation-fill-mode\n\nFile v1.0.35:adapters/gsap-easing-and-stagger.md\n\n# Easing, Stagger, and Function-Based Values\n\n## Easing\n\nBuilt-in eases: `power1`, `power2`, `power3`, `power4`, `back`, `bounce`, `circ`, `elastic`, `expo`, `sine`, `none`.\n\nEach has `.in`, `.out`, `.inOut` variants.\n\n| Ease                                       | Use for                                                                                         |\n| ------------------------------------------ | ----------------------------------------------------------------------------------------------- |\n| `power1.out`, `power2.out`                 | Gentle motion for secondary elements (a caption fade, a small shift). NOT the entrance default. |\n| `power3.out` (house default), `power4.out` | The standard long-tail settle. Entrances, title cards, hero reveals.                            |\n| `sine.inOut`                               | Long, slow, calm motion. Crossfades, ambient drift.                                             |\n| `back.out(1.7)`                            | Overshoot then settle. RARE — explicitly-playful register only, never a default.                |\n| `elastic.out(1, 0.3)`                      | Springy bounce. Same playful-only rule; prefer a baked spring (see Spring Eases below).         |\n| `expo.inOut`                               | Snappy, dramatic. Quick transitions between hero scenes.                                        |\n| `none` (linear)                            | Camera moves with timed counterpoint, mechanical motion.                                        |\n\nPick `.out` for entrances, `.in` for exits, `.inOut` for symmetric moves and continuous motion.\n\n**Smooth beats bouncy** — the motion doctrine (`rules/spring-pop-entrance.md`, the workflows' `motion-language.md`): entrances default to `power3.out` or the baked critically-damped spring (see Spring Eases below); overshoot eases (`back` / `elastic` / `bounce`) are a rare, explicitly-playful register, never the house style.\n\n## Easing Vocabulary (character & mood)\n\nEasings are tone of voice: a video that only whispers is boring; one that varies between whisper, normal, and punch is engaging. A composition should draw on ~3 easing characters across its beats — but vary **within the smooth families by energy** (`sine` / `power1` calm → `power3` standard → `power4` / `expo` punch); don't reach for overshoot to add variety. Overshoot is a _register_ (explicitly playful), not a spice. One ease everywhere reads flat; bounce everywhere reads cheap — the second failure is worse.\n\nThe full palette by character (each family has `.in`, `.out`, `.inOut` variants):\n\n| Family               | Character                                                                    | Typical use                                                                                                                                  |\n| -------------------- | ---------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |\n| `power1`–`power4`    | Gentle (1) to aggressive (4) acceleration curves                             | General purpose. **power3 is the house workhorse**; power2 for gentle secondary motion, power4 for dramatic snaps                            |\n| `back(N)`            | Overshoot then settle. N controls how far past the target (1=subtle, 4=wild) | RARE — explicitly-playful register only, never a default. Keep N ≤ 2; prefer a baked spring at ζ 0.6–0.7 (physical settle, see Spring Eases) |\n| `elastic(amp, freq)` | Spring bounce. amp=magnitude, freq=oscillation speed                         | RARE — same playful-only rule; the baked spring (below) is the physical version                                                              |\n| `bounce`             | Ball-drop bouncing                                                           | RARE — physical-comedy register only (something literally dropping)                                                                          |\n| `expo`               | Extreme acceleration curve (much steeper than power4)                        | Premium/luxury reveals, dramatic entrances                                                                                                   |\n| `sine`               | Smooth, organic, no hard edges                                               | Ambient float, breathing, Ken Burns, anything that loops. `.inOut` for yoyo motion                                                           |\n| `circ`               | Circular acceleration (starts very fast, ends very gentle or vice versa)     | Camera moves, scene transitions, orbital motion                                                                                              |\n| `steps(N)`           | Discrete N-step jumps, no interpolation                                      | Typing effects, cursor blink, counter ticks, retro/digital aesthetics                                                                        |\n\n**Mood mapping:** Match easing character to the beat's emotional content. Smooth/organic easings (`sine`, `power1`) feel contemplative and drifting. Aggressive deceleration (`power4.out`, `expo.out`) feels snappy and confident. Spring overshoot (`back.out`) feels bouncy and physical — but bouncy is a register, not an emphasis tool; reach for it only on explicitly-playful beats. The storyboard's mood description should guide which character fits — not a formula.\n\n## Defaults\n\n```javascript\nconst tl = gsap.timeline({\n  paused: true,\n  defaults: { duration: 0.6, ease: \"power3.out\" }, // the house settle — smooth beats bouncy\n});\n```\n\nOr globally:\n\n```javascript\ngsap.defaults({ duration: 0.6, ease: \"power3.out\" });\n```\n\nSetting defaults at timeline scope is preferred — it documents the motion language of that composition in one place.\n\n## Spring Eases (baked physics, seek-safe)\n\nThe \"iOS feel\" is a **damped spring's velocity curve**, not a bounce: a fast launch into a long asymptotic settle. Well-made system animations are critically damped or close to it — they barely overshoot, or don't at all. `power3.out` / `expo.out` approximate that curve; when you want the exact one — or a _physical_ overshoot for the rare playful register — bake the spring's closed-form solution into a function ease.\n\nWhy not a real-time spring library: an interactive spring is a stateful integrator (velocity accumulates frame to frame), which cannot be seeked dete\n\nArchive v1.0.34: 124 files, 476424 bytes\n\nFiles: adapters/animate-text.md (4069b), adapters/animejs.md (6224b), adapters/css-animations.md (5407b), adapters/gsap-easing-and-stagger.md (13270b), adapters/gsap-timeline-and-labels.md (3791b), adapters/gsap-transforms-and-perf.md (8552b), adapters/gsap.md (7602b), adapters/html-in-canvas-patterns.md (17166b), adapters/lottie.md (6181b), adapters/three.md (8207b), adapters/typegpu.md (8100b), adapters/waapi.md (4248b), blueprints-index.md (26512b), blueprints/agent-progress-theater.md (16091b), blueprints/camera-journey.md (13648b), blueprints/comparison-split.md (3316b), blueprints/constellation-hub.md (7144b), blueprints/cta-morph-press.md (6407b), blueprints/cursor-ui-demo.md (20276b), blueprints/dataviz-countup.md (14611b), blueprints/device-surface-showcase.md (17151b), blueprints/fixed-anchor-cycle.md (9089b), blueprints/grid-card-assemble.md (15594b), blueprints/kinetic-type-beats.md (29151b), blueprints/logo-assemble-lockup.md (26609b), blueprints/overwhelm-surround.md (6140b), blueprints/panel-edit-live-sync.md (13394b), blueprints/prompt-type-submit-generate.md (18636b), blueprints/spatial-pan-stations.md (4868b), blueprints/ticker-takeover.md (3052b), blueprints/titlecard-reveal.md (7441b), blueprints/transcript-scroll-artifact-reveal.md (11666b), blueprints/typewriter-reveal.md (6276b), blueprints/video-text-pivot.md (3281b), blueprints/zoom-out-workspace-reveal.md (14714b), examples/brand-reveal-assemble-zoom.html (12285b), examples/comparison-split-cards.html (21754b), examples/concept-demo-decode-pan.html (18382b), examples/cta-morph-press.html (15192b), examples/cta-orbit-collapse.html (50646b), examples/demo-page-scroll-spotlight.html (25019b), examples/hook-counter-burst.html (23004b), examples/messaging-multi-phrase.html (12223b), examples/metric-video-text-pivot.html (25640b), examples/problem-mockup-overwhelm.html (44482b), examples/proof-logo-chain.html (28911b), examples/takeover-ticker-displace.html (10947b), examples/workflow-approve-press.html (20656b), references/motion-blur.md (14104b), rules-index.md (21484b), rules/3d-camera-flight.md (18137b), rules/3d-page-scroll.md (7406b), rules/3d-text-depth-layers.md (6708b), rules/ai-tracking-box.md (6562b), rules/ambient-glow-bloom.md (9840b), rules/anchored-layout-expand.md (10313b), rules/asr-keyword-glow.md (6762b), rules/avatar-cloud-network.md (6578b), rules/camera-cursor-tracking.md (7911b), rules/card-morph-anchor.md (7411b), rules/center-outward-expansion.md (4263b), rules/chart-scrub-readout.md (10348b), rules/chromatic-glitch.md (10339b), rules/context-sensitive-cursor.md (6938b), rules/control-target-sync.md (10276b), rules/coordinate-target-zoom.md (7607b), rules/counting-dynamic-scale.md (5977b), rules/css-marker-patterns.md (5702b), rules/cursor-click-ripple.md (6397b), rules/cursor-drag.md (10215b), rules/depth-of-field-blur.md (8876b), rules/depth-scatter-assemble.md (7054b), rules/discrete-text-sequence.md (6454b), rules/dynamic-content-sequencing.md (8558b), rules/gradient-text-sweep.md (8751b), rules/gsap-effects.md (6912b), rules/hacker-flip-3d.md (5586b), rules/kinetic-beat-slam.md (6445b), rules/motion-blur-streak.md (16190b), rules/multi-cursor-choreography.md (10520b)\n\nArchive v1.0.33: 124 files, 476186 bytes\n\nFiles: adapters/animate-text.md (4069b), adapters/animejs.md (6224b), adapters/css-animations.md (5407b), adapters/gsap-easing-and-stagger.md (13270b), adapters/gsap-timeline-and-labels.md (3791b), adapters/gsap-transforms-and-perf.md (8552b), adapters/gsap.md (7602b), adapters/html-in-canvas-patterns.md (17166b), adapters/lottie.md (6181b), adapters/three.md (8207b), adapters/typegpu.md (8100b), adapters/waapi.md (4248b), blueprints-index.md (26512b), blueprints/agent-progress-theater.md (16091b), blueprints/camera-journey.md (13648b), blueprints/comparison-split.md (3316b), blueprints/constellation-hub.md (7144b), blueprints/cta-morph-press.md (6407b), blueprints/cursor-ui-demo.md (20276b), blueprints/dataviz-countup.md (14611b), blueprints/device-surface-showcase.md (17151b), blueprints/fixed-anchor-cycle.md (9089b), blueprints/grid-card-assemble.md (15594b), blueprints/kinetic-type-beats.md (29151b), blueprints/logo-assemble-lockup.md (26609b), blueprints/overwhelm-surround.md (6140b), blueprints/panel-edit-live-sync.md (13394b), blueprints/prompt-type-submit-generate.md (18636b), blueprints/spatial-pan-stations.md (4868b), blueprints/ticker-takeover.md (3052b), blueprints/titlecard-reveal.md (7441b), blueprints/transcript-scroll-artifact-reveal.md (11666b), blueprints/typewriter-reveal.md (6276b), blueprints/video-text-pivot.md (3281b), blueprints/zoom-out-workspace-reveal.md (14714b), examples/brand-reveal-assemble-zoom.html (12285b), examples/comparison-split-cards.html (21754b), examples/concept-demo-decode-pan.html (18382b), examples/cta-morph-press.html (15192b), examples/cta-orbit-collapse.html (50646b), examples/demo-page-scroll-spotlight.html (25019b), examples/hook-counter-burst.html (23004b), examples/messaging-multi-phrase.html (12223b), examples/metric-video-text-pivot.html (25640b), examples/problem-mockup-overwhelm.html (44482b), examples/proof-logo-chain.html (28911b), examples/takeover-ticker-displace.html (10947b), examples/workflow-approve-press.html (20656b), references/motion-blur.md (12795b), rules-index.md (21484b), rules/3d-camera-flight.md (18137b), rules/3d-page-scroll.md (7406b), rules/3d-text-depth-layers.md (6708b), rules/ai-tracking-box.md (6562b), rules/ambient-glow-bloom.md (9840b), rules/anchored-layout-expand.md (10313b), rules/asr-keyword-glow.md (6762b), rules/avatar-cloud-network.md (6578b), rules/camera-cursor-tracking.md (7911b), rules/card-morph-anchor.md (7411b), rules/center-outward-expansion.md (4263b), rules/chart-scrub-readout.md (10348b), rules/chromatic-glitch.md (10339b), rules/context-sensitive-cursor.md (6938b), rules/control-target-sync.md (10276b), rules/coordinate-target-zoom.md (7607b), rules/counting-dynamic-scale.md (5977b), rules/css-marker-patterns.md (5702b), rules/cursor-click-ripple.md (6397b), rules/cursor-drag.md (10215b), rules/depth-of-field-blur.md (8876b), rules/depth-scatter-assemble.md (7054b), rules/discrete-text-sequence.md (6454b), rules/dynamic-content-sequencing.md (8558b), rules/gradient-text-sweep.md (8751b), rules/gsap-effects.md (6912b), rules/hacker-flip-3d.md (5586b), rules/kinetic-beat-slam.md (6445b), rules/motion-blur-streak.md (16190b), rules/multi-cursor-choreography.md (10520b)\n\nArchive v1.0.32: 124 files, 475480 bytes\n\nFiles: adapters/animate-text.md (4069b), adapters/animejs.md (6224b), adapters/css-animations.md (5407b), adapters/gsap-easing-and-stagger.md (13270b), adapters/gsap-timeline-and-labels.md (3791b), adapters/gsap-transforms-and-perf.md (8552b), adapters/gsap.md (7602b), adapters/html-in-canvas-patterns.md (17166b), adapters/lottie.md (6181b), adapters/three.md (6473b), adapters/typegpu.md (8100b), adapters/waapi.md (4248b), blueprints-index.md (26512b), blueprints/agent-progress-theater.md (16091b), blueprints/camera-journey.md (13648b), blueprints/comparison-split.md (3316b), blueprints/constellation-hub.md (7144b), blueprints/cta-morph-press.md (6407b), blueprints/cursor-ui-demo.md (20276b), blueprints/dataviz-countup.md (14611b), blueprints/device-surface-showcase.md (17151b), blueprints/fixed-anchor-cycle.md (9089b), blueprints/grid-card-assemble.md (15594b), blueprints/kinetic-type-beats.md (29151b), blueprints/logo-assemble-lockup.md (26609b), blueprints/overwhelm-surround.md (6140b), blueprints/panel-edit-live-sync.md (13394b), blueprints/prompt-type-submit-generate.md (18636b), blueprints/spatial-pan-stations.md (4868b), blueprints/ticker-takeover.md (3052b), blueprints/titlecard-reveal.md (7441b), blueprints/transcript-scroll-artifact-reveal.md (11666b), blueprints/typewriter-reveal.md (6276b), blueprints/video-text-pivot.md (3281b), blueprints/zoom-out-workspace-reveal.md (14714b), examples/brand-reveal-assemble-zoom.html (12285b), examples/comparison-split-cards.html (21754b), examples/concept-demo-decode-pan.html (18382b), examples/cta-morph-press.html (15192b), examples/cta-orbit-collapse.html (50646b), examples/demo-page-scroll-spotlight.html (25019b), examples/hook-counter-burst.html (23004b), examples/messaging-multi-phrase.html (12223b), examples/metric-video-text-pivot.html (25640b), examples/problem-mockup-overwhelm.html (44482b), examples/proof-logo-chain.html (28911b), examples/takeover-ticker-displace.html (10947b), examples/workflow-approve-press.html (20656b), references/motion-blur.md (12795b), rules-index.md (21484b), rules/3d-camera-flight.md (18137b), rules/3d-page-scroll.md (7406b), rules/3d-text-depth-layers.md (6708b), rules/ai-tracking-box.md (6562b), rules/ambient-glow-bloom.md (9840b), rules/anchored-layout-expand.md (10313b), rules/asr-keyword-glow.md (6762b), rules/avatar-cloud-network.md (6578b), rules/camera-cursor-tracking.md (7911b), rules/card-morph-anchor.md (7411b), rules/center-outward-expansion.md (4263b), rules/chart-scrub-readout.md (10348b), rules/chromatic-glitch.md (10339b), rules/context-sensitive-cursor.md (6938b), rules/control-target-sync.md (10276b), rules/coordinate-target-zoom.md (7607b), rules/counting-dynamic-scale.md (5977b), rules/cs...","readmeExcerpt":"Skill: hyperframes-animation Owner: heygen-com Summary: All animation knowledge for HyperFrames — atomic motion rules, multi-phase scene blueprints, scene transitions, broader motion-design techniques, AND the seven runtime adapters (GSAP default, plus Lottie, Three.js, Anime.js, CSS keyframes, Web Animations API, TypeGPU). Use for any motion or animation task: pick 2-4 rules and compose, or load a blueprint, or look","codeSnippets":[],"executableExamples":[{"language":"bash","snippet":"node <SKILL_DIR>/scripts/animation-map.mjs <composition-dir> \\\n  --out <composition-dir>/.hyperframes/anim-map"},{"language":"html","snippet":"<div id=\"root\" data-composition-id=\"hero\" data-duration=\"4\" data-fps=\"30\">\n  <div id=\"slam\" data-hf-motion-blur></div>\n  <div id=\"title\" data-hf-motion-blur='{\"shutterAngle\": 360}'></div>\n</div>"},{"language":"bash","snippet":"# In your project root, install the upstream skill into .agents/skills/\nnpx skills add pixel-point/animate-text"},{"language":"text","snippet":"/animate-text"},{"language":"text","snippet":".agents/skills/animate-text/assets/effects/<id>.json   # per-library implementation recipe\n.agents/skills/animate-text/assets/specs/<id>.json     # portable motion contract"},{"language":"markdown","snippet":"**Text Animations:**\n\n- Main headline: `kinetic-center-build`\n- Eyebrow label: `soft-blur-in`\n- Body copy 3 lines: `mask-reveal-up`"}],"parameters":null,"dependencies":[],"permissions":[],"extractedFiles":[{"path":"SKILL.md","content":"---\nname: hyperframes-animation\ndescription: \"All animation knowledge for HyperFrames — atomic motion rules, multi-phase scene blueprints, scene transitions, broader motion-design techniques, AND the seven runtime adapters (GSAP default, plus Lottie, Three.js, Anime.js, CSS keyframes, Web Animations API, TypeGPU). Use for any motion or animation task: pick 2-4 rules and compose, or load a blueprint, or look up runtime-specific API (e.g. GSAP eases / Lottie player / Three.js mixer). Also covers auditing an existing composition's choreography (animation map) and 24 named text-animation effects. HyperFrames-native: single paused timeline, seek-safe, deterministic.\"\n---\n\n**Plugin installs:** Before setup or freshness commands, follow [plugin execution rules](../hyperframes/references/plugin-installation.md) when this skill is inside a HyperFrames plugin. Standalone installs keep the update instructions below.\n\n# HyperFrames Animation\n\nAll motion knowledge in one skill: **rules** (atomic recipes), **blueprints** (multi-phase scene templates), **transitions** (scene-to-scene), **techniques** (broader motion-design patterns), and **adapters** (per-runtime APIs).\n\nFor the composition contract (data attributes, sub-compositions, determinism) see `hyperframes-core`.\n\n## Default: compose atomic rules\n\nPick 2-4 rules from `rules-index.md`, glue them together with a single paused GSAP timeline, done. This is faster and produces less code than starting from a blueprint.\n\n## Load a blueprint when\n\n- The scene matches an existing pre-designed multi-phase template (brand-reveal, social-proof, etc.) and reusing its phase pipeline saves real authoring time\n- You want runnable ground-truth code for a complex 4-5 phase choreography\n\nBlueprints live in `blueprints-index.md`. Each entry points to `blueprints/<id>.md` (recipe). Do not read it speculatively; load it when you've already decided you need scene-level orchestration.\n\n## Routing\n\n| Want to…                                                                       | Read                                                |\n| ------------------------------------------------------------------------------ | --------------------------------------------------- |\n| Pick an atomic motion pattern by trigger / tag                                 | `rules-index.md`                                    |\n| Read one rule's full HTML / CSS / GSAP recipe                                  | `rules/<name>.md`                                   |\n| Pick a multi-phase scene template                                              | `blueprints-index.md`                               |\n| Read one blueprint's full recipe                                               | `blueprints/<id>.md`                                |\n| Author a scene transition (CSS-driven, between two clips)                      | `transitions/overview.md`, `transitions/catalog.md` |\n| Look up a broader motion-design technique                                      | `techni"},{"path":"_meta.json","content":"{\n  \"ownerId\": \"kn77d06grj6xqp3dqwkk4bavhn89pegt\",\n  \"slug\": \"hyperframes-animation\",\n  \"version\": \"1.0.36\",\n  \"publishedAt\": 1791491483510\n}"},{"path":"references/motion-blur.md","content":"# Motion blur — shutter smear on any animated element\n\n## Read this part before you blur anything\n\nBlur is not polish. It is the smear a real shutter leaves while the subject\ncrosses the frame, so it only reads as correct when the subject crosses enough\nof the frame to have smeared. Applied to motion that was never fast enough, it\nreads as a soft, cheap render: the eye sees mush where it expected an edge.\n\nDo not blur:\n\n| Case                                             | Why                                                                                                    | Do instead                          |\n| ------------------------------------------------ | ------------------------------------------------------------------------------------------------------ | ----------------------------------- |\n| Travel under about one element-width per frame   | The copies pile up inside the element's own silhouette, so the result is a softer element, not a smear | Leave it sharp                      |\n| A fade, a color change, a blur-in, a filter beat | Nothing reaches `transform`, so there is no trajectory to integrate and no smear to draw               | Leave it sharp                      |\n| Text meant to be read at that moment             | A smeared word is an unreadable word, which is usually the opposite of the brief                       | Blur the approach, land sharp, hold |\n| A slow drift, a parallax layer, a breathing loop | Below the half-pixel deadband it renders sharp anyway; above it, it looks like a mistake               | Leave it sharp                      |\n| The whole scene, or a container of many elements | Cost is the copies of an entire subtree, and a container that never moves smears nothing inside it     | Point it at the element that moves  |\n\nBlur the beats that snap: a slam, a whip, a hard cut in position, a spin, a\nscale punch. One to three of them in a composition, not every tween.\n\n## Two routes, and they are not interchangeable\n\n| Route                                   | What it is                                                                                                       | Use when                                                                                                             |\n| --------------------------------------- | ---------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |\n| The `motion-blur` registry component    | A DOM shutter synthesised in the page: N+1 additively blended copies of the element along its sampled trajectory | You want the smear visible in the preview, on selected elements, authored per element                                |\n| The engine's `motionBlur` render option | The renderer integrates real sub-frame samples of the whole frame                                                | You want every "},{"path":"adapters/animate-text.md","content":"# Text Effects — Reference\n\nFor deterministic text-animation specs (e.g., `typewriter` at exact `240ms / 46ms stagger / steps(1, end) easing`), this skill defers to the separate **`animate-text`** skill maintained by Pixel Point at [github.com/pixel-point/animate-text](https://github.com/pixel-point/animate-text). It provides a catalog of 24 named text effects with portable contracts and per-library implementation recipes (GSAP, Anime.js, WAAPI).\n\n**We do NOT ship the catalog inside this repo.** Pixel Point's `animate-text` is the source of truth; vendoring its files here would violate the upstream's licensing (no explicit license declared upstream as of this writing). Loading the skill separately keeps the legal picture clean while giving you the same catalog.\n\n## How to use it\n\nWhen a beat needs a deterministic text animation, load the upstream skill alongside this one:\n\n```bash\n# In your project root, install the upstream skill into .agents/skills/\nnpx skills add pixel-point/animate-text\n```\n\nOr in a skill-aware agent runtime, the skill is invoked by name:\n\n```\n/animate-text\n```\n\nOnce installed, the specs live at:\n\n```\n.agents/skills/animate-text/assets/effects/<id>.json   # per-library implementation recipe\n.agents/skills/animate-text/assets/specs/<id>.json     # portable motion contract\n```\n\nSub-agents reading those files get exact GSAP timings, easing strings, DOM split rules, and stagger algorithms — no creative invention needed.\n\n## When you don't need the upstream skill\n\nIf a beat's text animation is simple enough to describe in prose (\"headline fades up word-by-word, 80ms stagger\"), implement it inline using the GSAP knowledge already in these skills (`hyperframes-creative` → `references/motion-principles.md` and `references/beat-direction.md`; `hyperframes-animation` → `techniques.md`, entry #4 \"Per-Word Kinetic Typography\"). The upstream catalog is most valuable when:\n\n- You want a specific NAMED effect across multiple beats (so they feel like one design system, not one-offs)\n- You're choosing between several similar effects (typewriter vs per-character-rise vs bottom-up-letters) and want to see all 24 in one place\n- You need layout-aware effects (`kinetic-center-build`, `short-slide-right`, `short-slide-down`) where parameters alone aren't enough — those ship with custom layout algorithms\n\n## Effect names — vocabulary (do NOT use this as the implementation source)\n\nFor convenience while writing storyboards: the upstream skill provides 24 effects. Their IDs are listed here so you can name them in `STORYBOARD.md` even before loading the upstream skill. **The implementation specs are in the upstream skill, not here.**\n\n- **Per-character (7):** soft-blur-in, per-character-rise, typewriter, bottom-up-letters, top-down-letters, stagger-from-center, stagger-from-edges\n- **Per-word (8):** per-word-crossfade, spring-scale-in, shared-axis-y, blur-out-up, kinetic-center-build, short-slide-right, short-slide-down, depth-parallax-words\n- **Per-li"},{"path":"adapters/animejs.md","content":"---\nname: hyperframes-animejs\ndescription: Anime.js adapter patterns for HyperFrames. Use when writing Anime.js animations or timelines inside HyperFrames compositions, registering animations on window.__hfAnime, making Anime.js seek-driven and deterministic, or translating Anime.js examples into render-safe HyperFrames HTML.\n---\n\n# Anime.js for HyperFrames\n\nHyperFrames can seek Anime.js instances through its `animejs` runtime adapter. The composition owns the animation objects; HyperFrames owns the clock.\n\n**This page targets v4 (examples pinned to 4.5.0, MIT).** v4 is a hard break from v3 — there is no callable `anime()`, `easing:` is now `ease:`, and ease names lost their `ease` prefix. Writing v3 from memory produces a composition that throws or silently animates nothing.\n\nThe repo's own producer fixtures pin `animejs@4.0.2/lib/anime.iife.min.js`, which still resolves — but that build predates `splitText` / `scrambleText` / `createSeededRandom` / `createLayout` used below, and 4.1+ moved the bundles to `dist/bundles/`, so a version bump needs the path changed too.\n\n## Contract\n\n- Create animations or timelines synchronously during composition initialization.\n- Set `autoplay: false` so Anime.js does not advance on its own clock.\n- Register every returned animation or timeline on `window.__hfAnime` — **explicitly. There is no working auto-discovery on v4** (see Avoid).\n- Use finite durations and loop counts.\n- Avoid callbacks that mutate DOM based on wall-clock time, network state, or unseeded randomness.\n\nThe adapter seeks every registered instance with `instance.seek(timeMs)`, where `timeMs` is HyperFrames time **in milliseconds** (`ctx.time` seconds × 1000). It also calls `pause()` and `play()` on each instance; anything exposing those three methods works, whatever created it.\n\n## Loading v4\n\n```html\n<!-- UMD: the global `anime` is a NAMESPACE OBJECT, not a function -->\n<script src=\"https://cdn.jsdelivr.net/npm/animejs@4.5.0/dist/bundles/anime.umd.min.js\"></script>\n```\n\n`anime.animate(...)`, `anime.createTimeline(...)`, `anime.utils.*`, `anime.svg.*`, `anime.stagger(...)`. **Calling `anime(...)` is a TypeError** — every v4 build (UMD and IIFE alike) assigns a namespace object to the global, so v3's `anime({ targets })` form cannot work no matter which v4 file you load.\n\n## Basic Pattern\n\n```html\n<script>\n  const anim = anime.animate(\".mark\", {\n    x: 280, // v4 shorthand for translateX\n    rotate: \"1turn\",\n    opacity: [0, 1],\n    duration: 1200,\n    ease: \"outExpo\", // NOT easing: \"easeOutExpo\"\n    autoplay: false,\n  });\n\n  window.__hfAnime = window.__hfAnime || [];\n  window.__hfAnime.push(anim);\n</script>\n```\n\n## Timeline Pattern\n\n`createTimeline` replaces `anime.timeline`, and `add()` takes **targets as its first argument** — `add(targets, parameters, position)`:\n\n```html\n<script>\n  const tl = anime.createTimeline({\n    autoplay: false,\n    defaults: { ease: \"outCubic\" }, // per-timeline defaults, not a bare `easing`\n  });\n\n  tl.add(\".tit"}],"languages":[],"docsSourceLabel":"CLAWHUB","editorialOverview":"All animation knowledge for HyperFrames — atomic motion rules, multi-phase scene blueprints, scene transitions, broader motion-design techniques, AND the seven runtime adapters (GSAP default, plus Lottie, Three.js, Anime.js, CSS keyframes, Web Animations API, TypeGPU). Use for any motion or animation task: pick 2-4 rules and compose, or load a blueprint, or look up runtime-specific API (e.g. GSAP eases / Lottie player / Three.js mixer). Also covers auditing an existing composition's choreography (animation map) and 24 named text-animation effects. HyperFrames-native: single paused timeline, seek-safe, deterministic. Skill: hyperframes-animation Owner: heygen-com Summary: All animation knowledge for HyperFrames — atomic motion rules, multi-phase scene blueprints, scene transitions, broader motion-design techniques, AND the seven runtime adapters (GSAP default, plus Lottie, Three.js, Anime.js, CSS keyframes, Web Animations API, TypeGPU). Use for any motion or animation task: pick 2-4 rules and compose, or load a blueprint, or look","editorialQuality":{"score":100,"threshold":65,"status":"ready","wordCount":1864,"uniquenessScore":46,"reasons":[]}},"media":{"evidence":{"source":"no-media","verified":false,"confidence":"low","updatedAt":"2026-10-09T09:32:49.859Z","emptyReason":"No screenshots, media assets, or demo links are available."},"primaryImageUrl":null,"mediaAssetCount":0,"assets":[],"demoUrl":null},"ownerResources":{"evidence":{"source":"unclaimed","verified":false,"confidence":"low","updatedAt":"2026-10-09T09:32:49.859Z","emptyReason":"This page has not been claimed by the agent owner."},"hasCustomPage":false,"customPageUpdatedAt":null,"customLinks":[],"structuredLinks":{"docsUrl":null,"demoUrl":null,"supportUrl":null,"pricingUrl":null,"statusUrl":null},"customPage":null},"relatedAgents":{"evidence":{"source":"protocol-neighbors","verified":false,"confidence":"medium","updatedAt":"2026-10-09T23:49:31.307Z","emptyReason":null},"items":[{"id":"8ebccd8e-3863-4187-8355-c3f14e1f9edf","entityType":"agent","canonicalPath":"/agent/iofficeai-aionui","slug":"iofficeai-aionui","name":"AionUi","description":"Free, local, open-source 24/7 Cowork app and OpenClaw for Gemini CLI, Claude Code, Codex, OpenCode, Qwen Code, Goose CLI, Auggie, and more | 🌟 Star if you like it!","url":"https://github.com/iOfficeAI/AionUi","homepage":"https://www.aionui.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-10-09T19:11:12.944Z","createdAt":"2026-02-25T03:38:16.584Z","downloads":null},{"id":"b917f68a-ebff-438e-84f8-3f4b2494c0bc","entityType":"agent","canonicalPath":"/agent/activepieces-activepieces","slug":"activepieces-activepieces","name":"activepieces","description":"AI Agents & MCPs & AI Workflow Automation • (~400 MCP servers for AI agents) • AI Automation / AI Agent with MCPs • AI Workflows & AI Agents • MCPs for AI Agents","url":"https://github.com/activepieces/activepieces","homepage":"https://www.activepieces.com","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-15T02:22:12.426Z","createdAt":"2026-02-25T03:38:12.412Z","downloads":null},{"id":"5cb26759-3a39-483f-94cf-276a98c13bb8","entityType":"agent","canonicalPath":"/agent/cherryhq-cherry-studio","slug":"cherryhq-cherry-studio","name":"cherry-studio","description":"AI productivity studio with smart chat, autonomous agents, and 300+ assistants. Unified access to frontier LLMs","url":"https://github.com/CherryHQ/cherry-studio","homepage":"https://cherry-ai.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-11T14:38:40.986Z","createdAt":"2026-02-25T03:38:19.379Z","downloads":null},{"id":"6f6582d0-5d76-4f0f-b81d-86520247950b","entityType":"agent","canonicalPath":"/agent/copilotkit-copilotkit","slug":"copilotkit-copilotkit","name":"CopilotKit","description":"The Frontend for Agents & Generative UI. React + Angular","url":"https://github.com/CopilotKit/CopilotKit","homepage":"https://docs.copilotkit.ai","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-03-25T09:50:57.846Z","createdAt":"2026-02-25T03:39:14.617Z","downloads":null}],"links":{"hub":"/agent","source":"/agent/source/clawhub","protocols":[{"label":"OpenClaw","href":"/agent/protocol/openclew"}]}}}