feat(registry): add five video primitives - #3116
Open
miguel-heygen wants to merge 4 commits into
Open
Conversation
Two arcs of light travel the edge of a rounded panel and snap corner to corner, trailing a three-layer bloom. We ship eleven lower thirds and none of them has a travelling light. The traversal is closed form rather than accumulated, which is what makes it seekable: total = t/beat, corner = floor(total), and the arc head is the eased interpolation between two corner positions, each a pure function of an integer. Seeking to frame N and playing to frame N produce the same bytes, verified by hash across two separate browser launches with a control frame to rule out a static image. Arcs are SVG dash offsets rather than sampled polylines, which is exact along a path and removes the need for a minimum-arc clamp. The bloom widens the stroke before blurring, since blur alone reads as haze rather than emitted light. Nine variables, every number carrying explicit min, max and step.
Five blocks aimed at the gaps in the catalog rather than at what was easiest to build. We are thick in passes, wipes, kinetic type and lower thirds, and thin in hero objects, depth and texture. cosmic-orb, a nebula sphere behind a refracting glass limb. A hero object, a shape the catalog had almost none of; it carries a title card alone. Analytic rather than raymarched, so cost is flat per pixel. gallery-tunnel, a corridor of panels flying toward camera. The cheapest real depth win available, and the only one whose panels are content slots, so it carries a shot rather than decorating one. lt-neon-border, two arcs of light travelling a rounded panel and snapping corner to corner. We ship eleven lower thirds and none had a travelling light. weight-wave, a crest of thickness moving along a headline. Every other kinetic type move animates transform, opacity or a mask; this animates the letterforms themselves. Ships Recursive subset to the two axes it uses. Requires the sweep-fingerprint fix, since font axes were not part of the layout audit and a duplexed font removes the last trace of motion. halftone-field, a dot grid tracking a flowing noise field. The catalog had no texture register, and this runs under type without competing with it. All five are closed form: state at frame N is computed from N, never from frame N-1. That is what makes them seekable, and it is verified per block by hashing a direct seek against stepped playback and against an out-of-order scrub, with a control frame to rule out a vacuously static pass. Frames were rendered and looked at rather than inferred from a passing check, which is not a visual gate. Two framework defects surfaced while building these and are fixed in the blocks that hit them. A timeline onUpdate callback does not fire on seek, because GSAP suppresses events there by default, so a canvas driven that way freezes on frame 0; the repaint hangs off a property setter instead. And a CSS-sized image uploads a 1x1 texture, because the browser samples the laid-out size rather than the natural one.
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Workflows to automatically generate PRs for you. |
This was referenced Aug 8, 2026
| var el = document.createElement("img"); | ||
| el.id = "gt-img-" + k; | ||
| el.alt = ""; | ||
| el.src = src; |
All nine of the block's variables were unreachable at init, not just the one that showed it. The CLI's runtime injection appends its script before </body>, so it lands after the composition's own inline script and window.__hyperframes is undefined at the moment the block reads it. Every variable resolved to undefined on the play path and on a raw file open. Eight were masked by hardcoded JS fallbacks that silently duplicated the declared defaults, so they rendered correctly while ignoring every override. That masking is why this survived review: only pulseEnvelope, which had no fallback, showed a visible symptom. Proven rather than reasoned: changing the declared hue default and rendering produced a byte-identical frame beforehand, and a different one after. The authored data-composition-variables attribute is now the single owner of declared defaults, with the CSS custom property and the runtime bag layered on top as overrides. Same pattern as gallery-tunnel, which hit this first. The underlying injection-order defect is untouched and affects any composition reading getVariables() at init.
Its registry-item.json carried cosmic-orb's manifest verbatim: same name, title, description and files entry. The composition HTML was always correct; only the manifest was wrong. The catalog generator keys off the manifest name, so it read one identity from two directories. Cosmic Orb was emitted twice into the nav and Neon Border never appeared at all, with no page generated for it. Neither block was broken, and nothing in lint caught it because both manifests were individually valid. The replacement is derived from the composition rather than hand-copied: nine params read out of its declared data-composition-variables, and dimensions and duration read off the root attributes.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Five video primitives in one PR. Supersedes #3108, #3109, #3110, #3112 and #3113, which are closed in favour of this.
cosmic-orbgallery-tunnellt-neon-borderweight-wavehalftone-field19 files, +3104/-235.
Why these five
Chosen against the catalog's gaps rather than by what was cheapest to build. We are thick in passes, wipes, kinetic type, lower thirds and captions, and thin in hero objects, depth and texture.
cosmic-orbis a hero object, a shape we had almost none of. It carries a title card by itself and loops indefinitely.gallery-tunnelis the cheapest real depth win available, and uniquely its panels are content slots — a caller supplies their own screenshots — so it carries a shot rather than decorating one.lt-neon-border: we ship eleven lower thirds and not one had a travelling light.weight-wave: every other kinetic type move we own animates transform, opacity or a mask. This animates the letterforms themselves.halftone-field: we had no texture register at all, and this runs under type without competing with it.How
All five are closed form: state at frame N is computed from N, never from frame N-1. That is what makes them seekable, and it is the reason each is a rewrite rather than a port — the reference implementations are all
requestAnimationFrameaccumulators, which render frame 0 forever under a seeking renderer.The rewrites, concretely:
lt-neon-border's corner traversal isfloorandfracoft/beat, bit-exact.cosmic-orb's spin collapses to a constant rate.halftone-fieldisuTime = frame/fpswith no state at all.gallery-tunnelkeys every choice off a hash of the absolute segment number.weight-wave's per-character weight is a function of distance from a sweeping crest, so stagger falls out of the geometry.Two framework defects found while building these
Both are fixed in the blocks that hit them, and both are worth knowing about beyond this PR:
A timeline
onUpdatecallback does not fire onseek(). GSAP defaultssuppressEventsto true, so a canvas driven that way freezes on frame 0 under any consumer that seeks. Proven directly:t=0,t=6.0andt=6.2produced byte-identical pixels. The repaint hangs off a property setter on the tweened driver instead, since tweened values are always written during render. Note this is the pattern our own registry contributing guide currently recommends.A CSS-sized image uploads a 1x1 texture. The browser samples the laid-out size, not
naturalWidth, so every image panel sampled one dark pixel — invisible against a black backdrop. Isolated with a minimal repro that passed, then bisected against the composition.Test plan
bun run lintexit 0. Registry item lint passes for all five, 0 errors.hyperframes checkpasses per block.checkis not a visual gate — a composition can pass it and render nothing — so every block was verified by rendering frames and looking at them, and determinism was verified by hashing rather than by eye. Per block: a direct seek to frame N against stepped playback against an out-of-order scrub, all identical, with a control frame proving the hashes are not just of a static image.weight-wave's font-axis liveness has a falsifier rather than an assertion: with the font file removed the guard fires andcheckfails. That matters because the failure mode is silent — every character renders at the default instance while the page looks fine. An ink-only guard would have been a false pass, since the fallback family varies its own ink with weight.Dependency
weight-waveneeds #3107 to land first. Without it the layout audit reports its timeline as static: the sweep fingerprint is rect plus opacity, a font axis touches neither, and Recursive is duplexed so even the element rect stays fixed.Not covered
Catalog preview assets are not generated for the new blocks; their mdx points at URLs that 404 until
scripts/upload-docs-images.shruns with the publishing profile. Maintainer step.All WebGL blocks were verified only under software GL; the build box has no hardware GPU.
gallery-tunnel's wall cells are portrait at the default grid, so a 16:9 screenshot letterboxes rather than filling. Deliberate — contain-fit beats squashing — but a caller wanting edge-to-edge artwork will see gutters.