feat(registry): add Code Animations catalog section (9 blocks, incl. GPU)#1472
feat(registry): add Code Animations catalog section (9 blocks, incl. GPU)#1472miguel-heygen wants to merge 7 commits into
Conversation
Adds a "Code Animations" catalog section with six self-contained, deterministic code-animation blocks for building beautiful code-snippet videos: code-morph, code-snippet-flight, code-typing, code-diff, code-highlight, code-scroll. Each block reuses Shiki Magic Move's tokenizer + diff (baked at author time) and drives the motion as a paused GSAP timeline the engine seeks per frame — no CSS transitions, no render-time network. They group separately from the static code-snippet themes via a dedicated code-animation tag. - registry: 6 blocks (html + registry-item.json) + registry.json entries - catalog: 6 docs pages + a Code Animations nav group - studio: new code-animation block category (resolveBlockCategory keys on the code-animation tag; existing code-snippet themes are untouched) - generator: groupForItem maps code-animation -> Code Animations
Three WebGL "Code Animations" blocks driven by the GPU — effects Remotion's
2D code plugins (Shiki Magic Move / CodeHike) can't easily match:
- code-3d-extrude: syntax-highlighted code on a lit, beveled 3D slab
(Three.js) that rotates through space and settles to a readable rest
- code-shader-dissolve: the code "compiles" out of seeded GPU noise via a
fragment shader (chromatic dissolve front + edge glow), then holds crisp
- code-particle-assemble: thousands of GPU points scatter and fly to the
exact glyph pixels, resolving into readable syntax-highlighted code
Each is a self-contained single-HTML block (Three.js + GSAP from CDN, Shiki
tokens baked at author time) and fully deterministic: rendered via
tl.eventCallback("onUpdate") — never requestAnimationFrame — with a seeded
mulberry32 and no render-time network. Verified byte-identical across renders.
Also polishes the existing section:
- Code Animations is now the first catalog group
- code-snippet-flight chips are uniform-width, left-aligned, evenly stacked
- code-typing reveals per-character with a gliding caret (smooth)
- code-3d-extrude: render the code face unlit (screen-style) instead of a PBR material with an emissiveMap of the same texture, which double-counted the color and washed it pale under the lights. 3D depth now comes from the lit beveled edges + perspective; the face shows the exact Shiki colors. - code-particle-assemble: switch points from additive to normal alpha blending so overlapping particles no longer accumulate to white — the assembled code shows its true syntax colors. Both remain deterministic (verified byte-identical across renders).
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Workflows to automatically generate PRs for you. |
james-russo-rames-d-jusso
left a comment
There was a problem hiding this comment.
Verified at HEAD 9d3c9563. Reviewing under the canonical engineering rubric lane — HF-domain coherence (animation quality, GSAP/HF idioms, block-author UX, Catalog conventions) is deferred to @via separately.
Headline: determinism contract holds across all 9 blocks
Grepped every block at HEAD for Date.now, Math.random, performance.now, requestAnimationFrame, setTimeout, setInterval, fetch(, XMLHttpRequest, new Worker, import("http…, inline <script src="http… other than the pinned vendor deps. The only Date.now / Math.random references across the entire change are in comments that explicitly disclaim usage (code-3d-extrude.html:342,351, code-shader-dissolve.html:288,297, code-particle-assemble.html:198,207). All randomness flows through the inlined mulberry32(seed) PRNG seeded from window.__BLOCK.seed. Render is driven exclusively from tl.eventCallback("onUpdate", …) on a paused GSAP timeline — no requestAnimationFrame, no wall-clock timers, no render-time fetch. The "frame-exact and reproducible" load-bearing claim in the PR body checks out at the file level.
Concerns
-
registry/blocks/code-3d-extrude/code-3d-extrude.html,code-shader-dissolve/code-shader-dissolve.html,code-particle-assemble/code-particle-assemble.html—WebGLRendererdouble-render per onUpdate frame. Each block's innerrenderFnalready callsrenderer.render(scene, camera)(3d-extrude:536, shader-dissolve:493/576/692, particle-assemble:592). The outerrender()wrapper installed astl.eventCallback("onUpdate", render)then callsrenderer.render(scene, camera)AGAIN at 3d-extrude:780, shader-dissolve:736, particle-assemble:636. Result: every onUpdate frame submits the full scene to the GPU twice. Output is identical (deterministic) but it's wasted GPU work at 1920×1080 across the render farm. Cheapest fix: drop the trailingrenderer.render(scene, camera)from the outer wrapper since the per-effectrenderFnalready paints. Worth taking before this lands given the GPU blocks will run a lot. -
All 3 GPU blocks ship duplicated engine code that has already drifted within this PR. Each of
code-3d-extrude.html,code-shader-dissolve.html,code-particle-assemble.htmlincludes all three offxExtrude/fxDissolve/fxParticles+ the dispatch table, even though only one effect ever runs per block (selected bywindow.__BLOCK.effect). Diffing the dead-copy implementations between blocks:fxExtrudeincode-3d-extrude.html:437-541is the good unlit-screen version (MeshBasicMaterial, vivid Shiki colors per the rationale comment), whilefxExtrudeincode-shader-dissolve.html:383-495is an older lit version (MeshStandardMaterial+ emissive). SimilarlyfxParticlesincode-3d-extrude.htmlusesNormalBlendingwhile incode-shader-dissolve.htmlit usesAdditiveBlending. Runtime impact: zero (dispatch never reaches the stale copies). Maintenance impact: real — a future contributor readingcode-shader-dissolve.html'sfxExtrudeto understand the 3D-extrude block would be reading the wrong implementation. Same pattern in the 2D family:flight()incode-snippet-flight.html+code-typing.htmlis a substantial rewrite (equal-width chips, 2-pass layout, straight-left fly-in) whileflight()incode-morph.html+code-diff.html+code-highlight.html+code-scroll.htmlis the older two-column layout.typing()has the same split. If the catalog intends to keep one-file-per-block self-containment, suggest either (a) a// IGNORE — dead dispatch pathbanner on the duplicated dead effects per file, or (b) a build step that templates blocks from a single source-of-truth engine module. Today's shape is a foot-loaded maintenance trap. -
No
dispose()path in any of the 3 GPU blocks. Zero matches fordispose|onDestroy|teardownacross the change.WebGLRenderer,BufferGeometry,ShaderMaterial,CanvasTexture,ExtrudeGeometry,PlaneGeometry, lights — none are released. Whether this matters depends on whether the HF runtime tears down the entire iframe / page on block unmount (cleanly killing the WebGL context) vs. reuses the page. That's HF-runtime-interop knowledge — flagging for @via to confirm. If pages are reused, this leaks GPU resources block-by-block and will eventually exhaust the ~16 simultaneous WebGL context budget on Chromium.
Nits
- CDN
<script>tags load GSAP and Three.js fromcdn.jsdelivr.netwith no SRIintegrity=hash (every block, line 7-8). This matches the established catalog convention (88+ existing blocks do the same —cross-warp-morph,glitch,flowchart,transitions-cover,gravitational-lens, etc. — searched the repo). Not a regression but worth a catalog-wide hardening pass eventually. Out of scope here. - The
fillGutterhelper is identically duplicated in 6 of the 9 blocks (code-diff.html:671,code-highlight.html:614,code-morph.html:1060,code-scroll.html:1424,code-snippet-flight.html:516,code-typing.html:606). Same self-containment-vs-DRY tradeoff as the GPU engine. scripts/generate-catalog-pages.tshoists "Code Animations" toGROUP_ORDER: 0, demoting the previously-first Captions section to 1. Intentional per the PR body and PM call; flagging for visibility in case anyone tracks group-order changes.
Questions
- For @via — the PR body claims "every block is a single self-contained HTML composition" with "no render-time network." The vendor
<script src="https://cdn.jsdelivr.net/...">tags at line 7-8 of every block load over the network at composition load. I'm assuming the HF runtime intercepts these (offline cache / mirror / preflight bundling) so the "no render-time network" claim is true for the block's own JS code, not the dependency script tags. Worth confirming with one sentence in the PR body so a future reader doesn't ding the determinism claim on a literal reading.
What I didn't verify
- HF-domain coherence (animation quality, GSAP timeline structure, render-output correctness, Shiki token shape consistency, Catalog conventions, block-author UX, render-farm integration) — deferred to @via per our lane split.
- The 6 2D blocks beyond a structural spot-check + cross-block consistency grep — I deep-read
code-shader-dissolve.htmlend-to-end andcode-morph.html's structural skeleton; the others I verified via grep + the cross-block effect-function drift diffs above. - Whether the HF runtime's block-unmount path disposes Three.js resources (raised under the dispose concern above).
- Whether
static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/<name>.{mp4,png}URLs in the 9 registry-item.jsonpreviewfields are live — the "Render catalog previews" + "Preview parity" CI checks passed so I trust those.
Canonical-rubric pass by Rames D Jusso — HF-domain coherence deferred to @via
Regenerate all 9 Code Animation blocks from the canonical engine so the inlined effect code is consistent across blocks. Two dead copies had drifted: the GPU blocks shipped a stale fxExtrude using MeshStandardMaterial (vs the live MeshBasicMaterial), and morph/diff/highlight/scroll carried the pre-symmetry flight(). Live effects were already current — this aligns the dead copies so they can't mislead a maintainer. Also remove a redundant per-frame renderer.render() in the three WebGL blocks: the effect closure and the boot loop's render() each issued one, doubling GPU work at 1920x1080 for identical pixels. Closures now only mutate state; the boot loop does the single render. Byte-identical across two renders (determinism preserved).
…dor scripts Each block now inlines only the effect it dispatches — the generator strips the other effect bodies and their dispatch branches at build time. Removes the dead, drift-prone copies entirely (every block stays a single self-contained file); render output is byte-identical to the prior revision. GPU blocks register a beforeunload teardown that disposes the WebGLRenderer, geometries, materials and textures — one-shot renders already exit per job, but this frees GPU resources in long-lived Studio/player sessions. Vendor scripts (GSAP, Three.js) now carry SRI integrity + crossorigin, pinned to the sha384 of the jsdelivr-served files.
The catalog's 88+ existing blocks load GSAP/Three from jsdelivr without subresource integrity; adding it to only these 9 created an inconsistency and a divergent failure mode (a hash mismatch in any render environment would break only these blocks). Reverting to match convention. Render output unchanged.
The regenerated blocks were raw generator output, which is not oxfmt-canonical and failed CI's `oxfmt --check .` gate. Format them to match. Whitespace-only — render output byte-identical (verified on a GLSL block and a DOM block).
Code Animations catalog section
Adds a new Code Animations catalog section (now the first group in the catalog) with 9 self-contained, deterministic blocks for building beautiful code-snippet videos — including three GPU-driven effects that go beyond 2D code animation.
GPU / WebGL blocks (Three.js)
code-3d-extrude— syntax-highlighted code on a lit, beveled 3D slab that rotates through space and settles to a readable restcode-shader-dissolve— the code "compiles" out of seeded GPU noise via a fragment shader (chromatic dissolve front + edge glow), then holds crispcode-particle-assemble— thousands of GPU points scatter and fly to the exact glyph pixels, resolving into readable syntax-highlighted code2D blocks (paused GSAP timeline over baked Shiki tokens)
code-morph— one snippet morphs into another; tokens glide/fade between statescode-snippet-flight— discrete snippets fly in and assemble into a stacked programcode-typing— per-character typing reveal with a gliding caretcode-diff— line-level add/remove coloringcode-highlight— animated highlight sweep with context dimcode-scroll— scroll-to-line with spotlightHow it works
Every block is a single self-contained HTML composition. Shiki syntax tokens are baked at author time; the animation is a paused GSAP timeline the engine seeks per frame. The GPU blocks bridge the baked tokens into WebGL via an offscreen
CanvasTexture(slab face / shader sampler / particle target-sampling) and render viatl.eventCallback("onUpdate")— neverrequestAnimationFrame— with a seededmulberry32, so every block is frame-exact and reproducible. (GSAP and Three.js load from jsdelivr at pinned versions —gsap@3.14.2,three@0.147.0— matching the existing catalog convention; the blocks themselves issue no render-time data fetches, so output stays deterministic.)Catalog wiring
code-animationStudio block categoryregistry.jsonentriesstatic.heygen.ai)Validation
hyperframes lint— 0 errors on all 9 blockshyperframes validate— no console errors on all 9 (incl. WebGL/shader compile)Review follow-ups
rev
40be261renderer.render()in the three WebGL blocks (the effect closure and the boot loop each issued one, doubling GPU work for identical pixels). Closures now only mutate state; the boot loop does the single render. Byte-identical, still deterministic.rev
d8b9af1fxExtrude/flight()divergence at the root) and ~1960 lines come out across the 9 blocks. Render output byte-identical to the prior revision — previews unchanged.beforeunload. One-shot renders already exit per job; this frees resources in long-lived Studio/player sessions. (A per-composition unmount hook for block-swap-without-reload is still a runtime concern.)b3b3f60) — kept GSAP/Three on jsdelivr without SRI to match the catalog's 88+ existing blocks; no-SRI is the deliberate convention, and pinning it on only these 9 would create an inconsistency and a divergent failure mode.