Skip to content

feat(registry): six WebGPU shader backgrounds from the Shaders library - #5138

Merged
miguel-heygen merged 1 commit into
mainfrom
feat/registry-webgpu-shaders
Oct 7, 2026
Merged

miguel-heygen merged 1 commit into
mainfrom
feat/registry-webgpu-shaders

Conversation

@miguel-heygen

@miguel-heygen miguel-heygen commented Oct 6, 2026 •

Copy link
Copy Markdown
Collaborator

What

Six 2-second WebGPU blocks built on the MIT Shaders library (shaders@4.0.0): Godrays, Liquid Metal, Marble, Mesh Gradient, Nebula and Flowing Gradient. Each is tagged webgpu and webgpu-shader, declares data-requires-webgpu, and has a preview video. Their catalog pages are in #5139, which merges after this.

How

  • One shared bundle. All six blocks load the same lib/shaders.iife.js (1.3 MB), installed at compositions/lib/shaders.iife.js like the liquid-glass blocks' shared library. It holds the library, a small driver and only these six shaders; the canvas's data-shader attribute picks one. The six copies are one file in git.
  • Source next to it. registry/blocks/godrays/source/ has the driver, the build script, and a lockfile pinning every bundled package. npm ci && npm run build rebuilds the bundle and copies it into all six blocks.
  • Frame-accurate. The driver turns off the library's own clock. On every HyperFrames seek it renders the frame for that exact time, so preview and render draw the same frame and seeking backward gives the same frame as seeking forward.
  • Licenses. lib/shaders.THIRD-PARTY-LICENSES.txt, installed next to the bundle, carries the verbatim licenses of everything in it: shaders, typegpu, tinyest, typed-binary (MIT) and tsover-runtime (Apache-2.0). license is Apache-2.0 AND MIT.
  • preview.video points at the docs CDN; there is no poster.

The driver still reads the library's clock through __testing.getFrameDiagnostics() rather than tracking the last time itself. initialize() draws one frame and moves the clock by 16 ms before the driver takes over, and no public API reports it. A driver that tracks its own last time renders every frame 16 ms late (measured: up to 0.5/255 off the reference frames, against 0 when reading the clock). The library is pinned and bundled, so this hook cannot change under a published block; a deliberate upgrade rebuilds the bundle and re-checks it.

Verification

  • Same frames as before. The shared bundle renders each block pixel-identical to the per-block bundles it replaces, at 0.25, 1.0, 1.5 and 1.75 s. The published previews still match the blocks exactly.
  • Seek order. t = 1.5 s is pixel-identical whether reached from 0, from 1.9 s, or in steps (0, 0.25, 1.0, 1.5), for all six. Frames at 1.0 and 1.75 s differ, so the check is not vacuous.
  • Install. hyperframes add godrays then hyperframes add marble into one project write compositions/godrays.html, compositions/marble.html and one shared compositions/lib/. The installed Godrays renders, matching its preview within 1.8/255 (the preview is a 720p encode).
  • Lint and checks. hyperframes lint: 0 errors and 0 warnings on each block. oxfmt and oxlint pass on the source. node scripts/check-catalog-source-pr.mjs origin/main passes: this PR changes only the six block folders.
  • Previews. Five were rendered on a host without a GPU through the software WebGPU opt-in (feat(engine): render WebGPU compositions on SwiftShader behind an opt-in #5133), and each matches a Metal render within 1.5/255. Nebula takes over a minute per frame in software, so its preview is the Metal render with the same encoding. The sheet below pairs Metal (left) and software (right) at 0.25, 1.0 and 1.75 s.

Not exercised: Studio preview against the render on a GPU. On the GPU-less host, Studio draws these blocks with the software WebGPU flags.

Limits

  • One shader block per page: the driver finds its canvas with a page-wide selector, so a second shader block in the same film stays black.
  • Embedded in a larger film, the engine does not see the block's data-requires-webgpu (it reads the film's own root), so a GPU-less render of that film needs the root to declare it too. On a GPU, nothing changes.
  • The driver runs on the film's time, so a block placed at 10 s shows its shader from t = 10 s.
  • A host without a GPU renders these blocks only with feat(engine): render WebGPU compositions on SwiftShader behind an opt-in #5133's opt-in.

Merge order

Merge this, then #5139 right after, with the catalog publish PR held in between: #5139 adds these blocks to registry/registry.json and carries the generator fix their docs players need, and a publish in between would generate payloads with the old generator.

Metal render (left) and software render (right) of each block at 0.25, 1.0 and 1.75 s

@mintlify

mintlify Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

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

Project Status Preview Updated
hyperframes 🟢 Ready View Preview Oct 7, 2026, 2:55 AM

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

@github-actions

github-actions Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

Edit accuracy: accurate 2059 (base branch 2059), smooth 1614 of those

The gate passes.
Smoothness is reported in the artifact, not gated. A case fails only if it fails 2 of 3 runs.

Quarantined, measured but not gated (0)

Unstable (1)

  • seqfastkeys-none-px-r0-root-z100: tracking 7.76, pressJump 0.05, drop 0, reload 0, render 0.02, renderKey -, undo true, teleport true / tracking 0.07, pressJump 0.05, drop 0, reload 0, render 0.02, renderKey -, undo true, teleport true / tracking 0.06, pressJump 0.05, drop 0, reload 0, render 0.02, renderKey -, undo true, teleport true

@miguel-heygen
miguel-heygen marked this pull request as ready for review October 7, 2026 01:25

@jrusso1020 jrusso1020 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Request changes @ c8cb9987. The blocks are wired correctly and the licensing is complete. The blocker is reuse: this vendors the same 1.27 MB library six times.

Blocker (reuse, simpler): six copies of one bundle

  • Each lib/shaders.iife.js is 1.27–1.28 MB minified, 7.6 MB in total. Their sizes differ by at most 17 KB, so roughly 99% of each file is the same shaders + typegpu runtime, and only the shader on top differs.
  • Each installs at its own target (compositions/<block>/lib/shaders.iife.js), so a user who adds all six gets 7.6 MB in their project too.
  • The repo already has the pattern for this. The three liquid-glass blocks ship one byte-identical liquid-glass.iife.js (md5 082b0241… in all three) at a shared target, compositions/lib/liquid-glass.iife.js.
  • The markup is already halfway there: every canvas says which shader it wants (data-shader="Godrays"), but the driver ignores the value because each bundle hard-codes one shader.
  • Suggested shape: one bundle with all six shaders that picks by data-shader, installed at compositions/lib/shaders.iife.js with one THIRD-PARTY-LICENSES.txt. By the sizes above that's about 1.35 MB instead of 7.6 MB. A shaders bump then means one rebuild, not six.
  • Worth doing before merge rather than after, because 7.6 MB of minified text stays in git history for good.

Should-fix: the driver has no source in the repo
The only HyperFrames-written code here is the ~2 KB driver at the end of each bundle. It exists only minified, and nothing records how the bundle was built (entry file, bundler, flags). Please commit the driver source and the build command next to the bundle so it can be reviewed and rebuilt. The liquid-glass bundle has the same gap, so this isn't new, but the driver is ours.

Nits on the driver (from reading the minified tail)

  • The frame delta comes from renderer.__testing.getFrameDiagnostics().globalElapsedTime, which is a test hook. Tracking the last rendered time in the driver (renderSyntheticFrame(t - last); last = t) is simpler and doesn't depend on library internals across a rebuild.
  • data-shader-input (inputs a/b), data-shader-progress and the __render_frame__ sibling lookup aren't used by any of the six blocks. If they're meant for later transition blocks, fine; otherwise they're dead weight in every copy.
  • Not verified (no GPU here): whether a backward seek gives the same frame as a forward one. renderSyntheticFrame takes a delta, and if any of these shaders keeps smoothing state across frames, Studio scrubbing could drift from the render. Your Metal/software sheet samples forward renders. One check of t=1.5 reached from 0 and from 1.9 would settle it.

What checks out

  • Runtime contract: the driver uses the existing TypeGPU adapter (packages/core/src/runtime/adapters/typegpu.ts) as documented. It listens for hf-seek, hands its render to detail.waitUntil, starts from window.__hfTypegpuTime, and registers window.__hf.buildReady.shaders.
  • No WebGPU: a root that declares data-requires-webgpu fails fast in assertWebGpuAdapterAvailable with a clear message. Embedded without the root flag, the build promise rejects with shaders: WebGPU unavailable (…), and frameCapture names the rejected key. So it fails loudly rather than rendering black, except in Studio, where the canvas stays black and the console shows the error. The PR's Limits section already states this.
  • Licensing: shaders@4.0.0 depends only on typegpu@0.12.3, which depends on tinyest, typed-binary and tsover-runtime. That is exactly the five entries in THIRD-PARTY-LICENSES.txt (npm view). license: "Apache-2.0 AND MIT" is right.
  • Manifests: consistent across all six (tags, 1920×1080, 2 s, three files each, per-block nested targets as most blocks use). All six preview URLs return 200.
  • Tests: registryBlocks.test.ts scans every block directory, so "installs every local script" does cover the new items. Nothing exercises the driver itself. The producer's typegpu-adapter fixture would be the place for one.

— Rames

@jrusso1020 jrusso1020 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approve @ c393d998. This resolves my request for changes at c8cb9987.

Blocker resolved: one bundle

  • All six lib/shaders.iife.js are byte-identical (md5 e5b9e5a4…, 1,306,694 bytes), and so are the six license files. Git stores each as one blob.
  • Every manifest installs both at the shared compositions/lib/ target, the same as the liquid-glass blocks.
  • driver.js picks the shader by canvas.dataset.shader and throws a named error for an unknown one. The unused data-shader-input / -progress paths are gone.

Should-fix resolved: source and build

  • registry/blocks/godrays/source/ has the driver, build.mjs, a pinned lockfile and a README.
  • I copied the six lib/ folders to scratch and ran npm ci && npm run build. It reproduced the committed bundle byte for byte in all six blocks, so the minified file is exactly this source plus shaders@4.0.0.

Nits

  • Clock hook: your reason for keeping __testing.getFrameDiagnostics() holds. initialize() advances the clock by one 16 ms frame and no public API reports it. The library is pinned and bundled, and the build fails loudly if shaders/core/index.js changes shape. Accepted.
  • Backward seek: the body reports t=1.5 pixel-identical when reached from 0, from 1.9 and in steps, for all six. I have no GPU, so I didn't re-run it.

Still checks out

  • registryBlocks.test.ts reads the blocks directory, so it covers these blocks before #5139 adds them to registry.json. Positive control: dropping the bundle from godrays' files fails "installs every local script" with godrays: lib/shaders.iife.js.
  • The one-shader-per-page limit is no worse than before, since each copy used the same page-wide selector. It is now stated under Limits.

Optional, holistic: nothing guards that blocks sharing a target ship the same bytes. If one copy drifts, a hand edit or a partial rebuild for example, the install order decides which version a project gets, and add keeps an edited file unless --force is passed. A small test could group every block's files[] by target and assert identical content. That would cover the liquid-glass blocks too.

CI had no failures when I checked; some jobs were still running.

— Rames

@miguel-heygen
miguel-heygen added this pull request to the merge queue Oct 7, 2026
Merged via the queue into main with commit 7cf042d Oct 7, 2026
60 checks passed
@miguel-heygen
miguel-heygen deleted the feat/registry-webgpu-shaders branch October 7, 2026 03:09

This branch was successfully deployed

1 active deployment
staging - docs — c393d998 Deployed Oct 7, 2026 by mintlify[bot]
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