Skip to content

feat(registry): add the gallery-tunnel depth block - #3113

Closed
miguel-heygen wants to merge 1 commit into
mainfrom
feat/primitive-gallery-tunnel
Closed

feat(registry): add the gallery-tunnel depth block#3113
miguel-heygen wants to merge 1 commit into
mainfrom
feat/primitive-gallery-tunnel

Conversation

@miguel-heygen

Copy link
Copy Markdown
Collaborator

What

gallery-tunnel: a square corridor receding to a vanishing point, its faces tiled with colour slabs and images that swell toward camera and pass.

Why

Depth is the catalog's largest gap. This is also the only depth move whose panels are content slots — a caller supplies their own screenshots — so it carries a shot rather than decorating one.

How

Three.js rather than CSS 3D. CSS 3D would have needed hand-rolled substitutes for three things the design makes load-bearing: perspective-correct texture mapping on wall panels, per-fragment linear fog, and a depth buffer. Edges are real tube geometry, so they thicken with perspective; a CSS border would not.

Everything keys off one integer: the absolute segment number in a given pool slot at the current distance. Rhythm, slot visibility, colour-vs-image and both indices all derive from a hash of that number, so a segment carries identical panels however it is scrubbed to. The reference's sequential colour and image counters are gone, as is its camera-chase lerp, which under a constant rate contributed only a fixed lag.

The recycle boundary is derived from the fog distance rather than duplicated: BACK = clamp(15 - fogFar, 0, 1.5). A segment reappears exactly where fog reaches the backdrop, so moving the fog moves the boundary with it instead of desynchronising two constants.

Two bugs found and root-fixed

window.__hyperframes is undefined when an inline composition script runs. Every string variable silently resolved to empty, so images never produced a single element and the content-slot capability was dead. Variables now come from the authored data-composition-variables attribute, which is the source; the CSS custom property and runtime bag layer on top as overrides. This also removed a second owner, since defaults had been written twice.

A CSS-sized image uploads a 1x1 texture. Chrome's texImage2D uses the laid-out size, not naturalWidth, so every image panel sampled one dark pixel — indistinguishable from black on a black backdrop. Images now keep natural layout size inside a clipped well, which also keeps the page load event gated on decode. Isolated with a minimal three.js repro that passed, then bisected against the composition.

Test plan

  • Unit tests added/updated
  • Manual testing performed
  • Documentation updated (if applicable)

hyperframes check passes: 0 errors across lint, runtime, layout, motion and contrast. oxfmt --check clean. The runtime warnings are llvmpipe ReadPixels noise from software rendering.

Frames were rendered and looked at at 0/1.5/3/4.5/6/7.5/9/11s. Panels swell from the vanishing point and exit past camera; the cross-section holds dead centre in every frame.

The far end was checked specifically, since a visible pop is the failure mode this design guards against: a 4x zoom crop of the centre across 5.00 to 5.25s, a window spanning several recycle events, shows the vanishing point pure black with slabs emerging from black and brightening monotonically. Nothing pops in.

Content does not visibly loop: geometry is 5s-periodic at the default speed, but content keys off an ever-increasing absolute index, so 0s and 5s differ.

Determinism, canvas bytes compared with images loaded: same frame twice, direct seek to 7.0 equals stepping 0 to 7.0 at 30fps, equals the same after rewinding to 0, equals the same after seeking backwards from 11.999.

Content slots verified two ways: as the block's own images default and as a host CSS override, both rendering supplied screenshots contain-fit.

Not covered

Only verified under software GL; the devbox has no hardware GPU. One frame is about 166ms there, and a real GPU will be far faster, but that is unmeasured.

Wall cells are portrait at the default grid, so a 16:9 screenshot letterboxes rather than filling. Deliberate, since contain-fit beats squashing, but a caller wanting edge-to-edge artwork will see gutters.

No catalog preview PNG; the mdx points at a URL that 404s until the upload script runs with the publishing profile.

A square corridor receding to a vanishing point, its faces tiled with
colour slabs and images that swell toward camera and pass. Depth is the
catalog's largest gap, and this is the only depth move whose panels are
content slots, so it carries a shot rather than decorating one.

Everything keys off one integer: the absolute segment number in a pool
slot at the current distance. Rhythm, visibility, colour and image choice
all derive from a hash of that number, so a segment carries identical
panels however it is scrubbed to. The reference's sequential colour and
image counters are gone, along with its camera-chase lerp, which under a
constant rate only contributed a fixed lag.

The recycle boundary is derived from the fog distance rather than
duplicated, so a segment reappears exactly where fog reaches the backdrop
and moving the fog moves the boundary with it. Verified on a zoom crop
across several recycle events: slabs emerge from black and brighten
monotonically, nothing pops in.

Two bugs surfaced while building it. Composition variables are read from
the authored attribute rather than the runtime bag, which is undefined
when an inline script runs and silently resolved every string variable to
empty. And image panels are laid out at natural size inside a clipped
well, because a CSS-sized img uploads a 1x1 texture: the browser samples
the laid-out size, not the natural one.
@mintlify

mintlify Bot commented Aug 8, 2026

Copy link
Copy Markdown

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
hyperframes 🟢 Ready View Preview Aug 8, 2026, 4:22 PM

💡 Tip: Enable Workflows to automatically generate PRs for you.

var el = document.createElement("img");
el.id = "gt-img-" + k;
el.alt = "";
el.src = src;
@miguel-heygen

Copy link
Copy Markdown
Collaborator Author

Superseded by #3116, which carries all five video primitives in one PR as requested. Same commits, same verification; nothing dropped.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants