Skip to content

feat(registry): add path-traced-3d example with raster preview and path-traced export - #7219

Merged
jrusso1020 merged 2 commits into
mainfrom
feat/path-tracing-starter
Nov 5, 2026
Merged

jrusso1020 merged 2 commits into
mainfrom
feat/path-tracing-starter

Conversation

@jrusso1020

Copy link
Copy Markdown
Collaborator

What

Adds path-traced-3d, an opt-in Three.js starter. It previews with raster rendering and exports with a path tracer, from the same scene file. It also adds a skill reference (skills/hyperframes/references/path-tracing.md) and a docs guide (docs/guides/path-tracing.mdx).

  • The renderer variable is raster (the default, for fast preview) or path (for export). samples and bounces are also variables.

  • three 0.180.0, three-gpu-pathtracer 0.0.24 and three-mesh-bvh 0.9.5 ship as one local ES module, with:

    • their licences;
    • a note with the versions and the bundle's hash;
    • a pinned rebuild recipe in source/.

    There are no CDN imports, and 2D projects never load the tracer.

Why

Path tracing in HyperFrames already works with existing APIs: window.__hf.buildReady, hf-seek with waitUntil, and composition variables. But it's easy to get wrong:

  • Setup that waits on an animation frame or the clock never finishes during a render.
  • A setup loop that polls with scheduler.yield() never finishes anywhere. Yield continuations run ahead of timers, so the loop starves the setTimeout that Three's compileAsync polls with. Plain headless Chrome 152 hangs on a ten-line page that does this.
  • Unseeded randomness changes the grain between page loads and render workers. The tracer's noise textures and Three's object ids, which set the order lights are sampled in, both come from Math.random.

This starter and recipe show a deterministic, verified version of each.

Related work

How

  • assets/scene.js builds the scene, and poseAt(t) is the only code that moves anything.
  • assets/render.js:
    • Setup is one buildReady promise. It awaits renderer.compileAsync (raster), or the tracer's compile promise behind isCompiling (path), in one commented helper. It draws one throwaway frame, because a renderer's first frame differs from later ones, then reads one pixel back.
    • Frames are drawn only in the hf-seek listener, registered with waitUntil. Each new time restarts accumulation and traces exactly samples passes.
    • Settings that depend on elapsed time are off: renderDelay and fadeDuration are 0, and dynamicLowRes is off.
    • The scene and the tracer are built under a fixed Math.random seed.
    • Nothing uses timers, animation frames, the clock or scheduler.yield(), and nothing starts a render loop.
  • source/ holds the repo-only files, which aren't installed:
    • pinned package.json and lockfile, plus build.mjs (a rebuild produces the same hash);
    • path-traced-3d.test.mjs (in test:scripts), which checks the manifest, local-only imports, the bundle hash and pins, that render.js uses no timers, animation frames or clock, and that a pose depends only on its time;
    • verify-render.mjs, the render check.

Test plan

  • Unit tests added/updated: path-traced-3d.test.mjs (5 tests) passes. The skill lint, core-skills and catalog-source-PR checks pass (64 tests).
  • Manual testing performed: verify-render.mjs, run on a machine without a hardware GPU (software WebGL), at 320x180 and 2 samples per pixel in path mode:
    • hyperframes check passes in path mode, so the async setup resolves.
    • Snapshots at 4 s, 0 s, then 4 s again: the two 4 s PNGs are byte-identical, in both raster and path mode. A fresh page draws the same 4 s path still (same hash).
    • Exported frames decoded with ffmpeg: raster, 1 s at 30 fps (30 frames); path, 0.8 s at 10 fps on 2 workers (8 frames). No frame is blank, the clip moves, and there's no jump where the two workers' frames meet.
    • Software wall times: path setup about 55 s (mostly the first shader run), path render 66 s, raster render 6.8 s.
  • Documentation updated (if applicable): new guide and skill reference, linked from frame-adapters.mdx, types/3d.md and runtimes.md.
  • Comments follow CONTRIBUTING.md "Comments".

Notes

  • hyperframes init --example path-traced-3d starts working once the catalog publication PR adds the example to registry/registry.json.
  • A catalog PR may not edit docs/docs.json, so the guide is linked from the frame-adapters page instead of the sidebar. A small follow-up can add the sidebar entry.
  • Without a hardware GPU, the tracer's first shader run takes about a minute, which is longer than the default 45 s readiness wait. The guide says to raise --timeout or --player-ready-timeout.
  • The cost figures in the guide (8 s 720p clip: 10.8 s raster vs 810.5 s path traced, at 32 samples per pixel and 6 bounces, on an Apple M4 Max) come from the study this starter is based on. A hardware-GPU benchmark is running, and I'll update the figures if it disagrees.

🤖 Generated with Claude Code

…th-traced export

An opt-in Three.js starter: previews with raster rendering and exports with
three-gpu-pathtracer, from one scene file. three 0.180.0, three-gpu-pathtracer
0.0.24 and three-mesh-bvh 0.9.5 ship as one local, pinned ES module with their
licences and a rebuild recipe, so renders make no network requests and 2D
projects never load the tracer.

Setup is one buildReady promise that awaits library readiness promises only
and ends with a throwaway frame and a one-pixel readback. Frames are drawn in
the hf-seek listener through waitUntil, each time from zero with a fixed
sample count. Math.random is seeded while the scene and tracer are built,
because object ids order light sampling and the tracer's noise textures come
from it.

Adds a skill reference and a docs guide for the setup and seek contract, the
frozen clock, why scheduler.yield() waits hang, and sample/bounce cost.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
@mintlify

mintlify Bot commented Nov 5, 2026 •

Copy link
Copy Markdown

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

Project Status Preview Updated
hyperframes 🟢 Ready View Preview Nov 5, 2026, 6:20 AM

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

@github-actions

github-actions Bot commented Nov 5, 2026 •

Copy link
Copy Markdown
Contributor

Edit accuracy: accurate 267 (base branch 267), smooth 107 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 (8)

Unstable (1)

  • seqfastkeys-none-px-r0-root-z100: tracking 0.07, pressJump 0.05, drop 0, reload 0, render 0.02, renderKey -, undo false, teleport true / tracking 0.06, 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

@heygengenesis heygengenesis Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Verdict: REQUEST CHANGES · 1 blocker · 0 important · 3 nits · head 9e29ef8d

  • blocker (CI, file not in diff): required CI › Test (cli 2/2) fails packages/cli/src/commands/docs.test.ts. That test expects every registry/examples/* dir to have a ## <name> section in packages/cli/src/docs/examples.md containing its registry-item description. To fix it, add ## path-traced-3d + Three.js scene with a fast raster preview and an opt-in path-traced export. That file isn't a generated catalog path, so check-catalog-source-pr allows the edit.
Scope / determinism audit

I read render.js against the unminified three-gpu-pathtracer 0.0.24 / three 0.180.0 sources. The determinism properties hold:

  • Seeding: every order-sensitive Math.random consumer is inside a seed window. Light, mesh and texture order sort by uuid, and the uuids come from createScene (seed 1). Blue-noise and stratified textures are built in the tracer constructor (seed 2). Objects created later in per-seek setScene (the GradientEquirectTexture background, per-mesh intermediate geometry) only get equality-compared, never sorted.
  • Compile wait: isCompiling is exactly Boolean(_pathTracer._compilePromise), and the library clears it in a .then registered before ours. So the awaited promise is always real and the loop cannot spin. This is still true in 0.0.28.
  • Sample count: tiles default to 3×3, so each renderSample adds 1/9 and the last tile rounds. samples < target therefore runs exactly target full passes. stableNoise reset restores identity strata, index 0, generator seed 0 and material.seed = 0 on every reset, so a time's pixels don't depend on history. renderDelay 0 against the shim's constant-within-a-seek performance.now is safe.
  • BVH: it is built once at setup's poseAt(0), and later seeks only refit(). Every worker gets the same topology.

path-traced-3d.test.mjs passes locally (5/5). Windows render verification was still running when I posted. I skimmed the vendored bundle, verify-render.mjs and the lockfile; I didn't audit them.

— Genesis

Add the example's section to the CLI examples doc, which docs.test.ts
requires for every registry example. Throw if the tracer reports compiling
but exposes no compile promise, instead of spinning renderSample(). Say that
three's object uuids set the tracer's sort order, that later-created objects
need the seed too, and that Date and performance.now read the current seek
time during a render rather than staying at 0.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>

@heygengenesis heygengenesis Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Verdict: APPROVE — 4 resolved, 0 open, 0 new · 9e29ef8d → 3491c373. CI green, including Windows render verification.

— Genesis

@jrusso1020
jrusso1020 added this pull request to the merge queue Nov 5, 2026
Merged via the queue into main with commit d452a6f Nov 5, 2026
90 checks passed
@jrusso1020
jrusso1020 deleted the feat/path-tracing-starter branch November 5, 2026 07:11

This branch was successfully deployed

1 active deployment
staging - docs — 3491c373 Deployed Nov 5, 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.

1 participant