Skip to content

V3: Upgrade three.js to 0.185.1 (0.186.1 blocked by a TSL codegen bug) - #190

Merged
git-chad merged 121 commits into
git-chad/shader-lab-v3-planfrom
git-chad/v3-three-upgrade
Oct 1, 2026
Merged

git-chad merged 121 commits into
git-chad/shader-lab-v3-planfrom
git-chad/v3-three-upgrade

Conversation

@git-chad

Copy link
Copy Markdown
Collaborator

Step 2 of roadmap 4.4, in the order agreed on September 29: upgrade three.js before animation clips, SVG to 3D and, last, the path-traced Render mode.

What changes

  • three 0.183.2 → 0.185.1, @types/three 0.183.1 → 0.185.4.
  • The published runtime's peer dependency moves from three ^0.183.0 to ^0.185.0. Projects on 0.183 have to update three together with it (minor changeset included).
  • @webgpu/types is an explicit dev dependency, referenced from src/types/webgpu.d.ts. @types/three stops pulling it in with 0.186.
  • The 3D layer drops its transmission compileAsync workaround. Checked with a negative control: 0.183 still throws the copy-format error without it, 0.185 doesn't.
  • The composition suite now tolerates one known error chain. three (since r185) reports uncaptured GPU errors through console.error, and SwiftShader can't import decoded video frames. That produces the same 64 validation errors on 0.183, where Chrome printed them as warnings nobody counted. The suite ignores exactly that ExternalTexture chain, prints how many it skipped, and still fails on any other error.
  • The roadmap records the new step order.

Why not 0.186.1

0.186.1 is the latest release, and I tried it first. It has a TSL codegen bug, three #34339 (still open). When a shared math value is first read inside an If/select branch, three caches it in a variable assigned only in that branch, so every read outside it gets 0.

  • It broke the Photographic Cells outline at gap 0 and Focus Blur widths.
  • I captured every WGSL shader the suite generates on 0.185.1 and on 0.186.1 and scanned them for reads of variables that are never assigned on that path. 0.186.1 adds such reads in Glass, Connected Dots, Photocopy and Plotter as well. 0.185.1 adds none.
  • A patch that turned the cache off inside branches and loops fixed Cells. It also made the Glass and Blur shaders compile several times slower and still failed a Focus Blur check, so it isn't in this PR.

r187 rewrites that cache (#34607, #34637). Moving to it should be a version bump plus the same suite and WGSL scan.

Still in place

  • Exact-pose uniform refresh in the 3D layer. three only fixes the underlying bug in r186; 0.185 without it fails the pose-return test.
  • Shadow node-frame advance. ShadowNode still updates once per frame and camera.
  • Persistent environment node.

Visible differences

  • Studio Rotation turns the other way. three r184 aligned environment rotation with object rotation. The 3D layer hasn't shipped, so there's no migration.
  • Nothing else moved. 94 of the 95 images the suite writes are byte-identical to 0.183. relief-letterpress.png has 227 pixels off by at most 3/255.

Tests

  • tsc, tsgo, the runtime typecheck and the runtime build pass. Lint has only the two existing warnings.
  • The full bun run test:composition suite passes on 0.185.1 (10m49s).
  • The before/after image diff is described above.
  • All GPU runs used SwiftShader. Nothing was checked on a hardware GPU.

🤖 Generated with Claude Code

@vercel

vercel Bot commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
shader-lab Ready Ready Preview Oct 1, 2026 2:04pm UTC

@greptile-apps

greptile-apps Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

RetriggerConfidence Score: 4/5

[High risk] Upgrades the 3D graphics library and adjusts for API changes.

The PR appears safe to merge, with non-blocking improvements needed to GPU-error filtering and transmission-path coverage.

Summary

Upgrades Three.js and its types to the 0.185 line, updates the published runtime peer requirement, removes the model transmission compilation workaround, and adjusts composition-test GPU error handling.

  • The error filter can conceal an unrelated GPU error.
  • The newly enabled imported-transmission compilation path needs a focused regression test.

Reviews (1) · Last reviewed commit: "Upgrade three.js to 0.185.1"

Comment thread tests/composition/run.mjs
Comment thread src/renderer/model-pass.ts
…roup failures

Media load callbacks now ignore replaced or removed passes and pending loads are cleared on disposal. Strict-mode pass failures are reported once at the originating pass instead of at every group boundary.
Publishing now drops every descendant of a hidden group and clears the
parentId of any retained layer (the audio source) whose parent was removed,
so the published scene passes hierarchy validation on reopen.
Cap the neighbor row and column search to two cells each side so the
worst-case cellAt count per fragment is 25 instead of unbounded in the
packed minimum-size, maximum-stroke configuration. Mirrored in the
runtime package.
…e on buttons

Clear the active paint layer when the layer stops being paintable (selection,
mode, visibility or lock change) so Paint never resumes without Edit Paint.
Only preventDefault on Space for non-interactive targets so focused buttons
and links still activate.
Derive output alpha from the red, green and blue samples so displaced
fringes survive at transparent edges. Skip the blur pyramid when distance
and frost are both zero, in the app and runtime copies.
…est, video benchmark

- Image background keeps the input alpha instead of forcing opaque (editor and runtime passes)
- Duplication check edits the real mode param and asserts both layers
- Add a --dots scenario set (graph, blobs, plexus) to the effect video benchmark
…trols

Mesh drift is scaled to keep each point inside its cell, and each quad
splits along whichever diagonal is interior. Dot and link controls that
Mesh ignores are hidden, via a new oneOf visibility condition.
… style paper, duplicate test

- Un-premultiply ink color in transparent-paper output so alpha coverage is not applied twice (app and SDK pass)
- Show Angle for hatch and squiggle (new oneOf visibility condition); show Cross Angle only in hatch
- Plotter styles also set paper to color
- Duplication test now edits the real mode param and checks both layers
- Scale transparent-paper toner by input alpha so empty pixels stay clear
- Mix Amount in premultiplied space so partial amounts do not darken toner
- Skip the blur pyramid when Amount is zero
- Update a real parameter in the duplicate-independence check
- Mirror the pass changes into the React runtime
…ate test

Seed small shapes at their top-left tip so Cut Line and Cloud style them.
Raise the flood reach cap to 4096 so distant rings survive.
Let Scalloped draw inside for negative offsets.
Make the duplicate-independence check update a real parameter.
…terials, shadows, exact depth and Blender-style transforms

A new 3D Model source layer renders a .glb (or self-contained .gltf) into
its own HalfFloat MSAA target and composites it as a source over the layers
below. Draco, meshopt and KTX2 decoders are served from public/three. The
model is fitted to a tight vertex sphere with a margin; focal length changes
perspective without changing size.

Lighting: five CC0 Poly Haven studios or a custom .hdr, each normalized to
the same mean irradiance so switching studios keeps the exposure. The
environment goes through one persistent pmremTexture node set as
scene.environmentNode, because r183 bakes the environment texture node into
compiled materials and swapping scene.environment left them sampling the
disposed PMREM. A camera-relative key light casts PCF soft shadows onto an
invisible floor, and a blurred height pass under the model draws the
contact shadow. Tone mapping runs in the composite (Neutral by default).

Materials: the file's PBR materials or Chrome, Brushed Metal, Glass, Clay,
Rubber and Iridescent overrides. Anisotropy was dropped (flat white on
derivative tangents); iridescence is a view-angle spectral tint because
thin-film interference was invisible on metals.

Depth: an override pass writes near=1 depth normalized to the model's
bounding sphere, composited over any incoming scene depth, so Blur, Glass,
Relief, Input -> Depth and depth masks read the model, also inside groups.

Transform UX (asked for Blender basics): Location/Rotation/Scale as XYZ
fields with scrubbable axis letters, Move/Rotate/Scale gizmos, G/R/S modal
transforms with X/Y/Z locks, Alt+G/R/S clear, middle-drag orbit and
Shift+middle-drag shift. Modal keys are captured before other handlers so a
focused toolbar tooltip cannot swallow Escape.

Workarounds for three r183, found by tests:
- NodeMaterialObserver is shared per node builder state and its renderId
  fast path skips the world-matrix cache, so a mesh returning to an exact
  earlier pose kept a stale GPU matrix. A node-valued property on file
  materials makes the observer refresh every render.
- compileAsync allocates the transmission backdrop copy with the canvas
  format, breaking glass in HalfFloat targets; transmissive models compile
  on first draw instead.
- ShadowNode updates once per node frame per camera and frames only advance
  on three's own rAF, so the pass advances the node frame before each draw
  (hidden-tab exports and several draws per tick froze shadows).
- WebGPU only allows 1 or 4 samples; MSAA turns off above 4K so 8192 px
  exports stay within memory.

Exports wait up to 30 s for model and HDR loads.
The picker already uses the cube for 3D Model; the layer row fell back to
the generic image placeholder like effect layers do.
…, load tracking

- Offset the floor, contact shadow and key light target by Location Y
- Render the depth pass with per-material alpha-tested variants so cutouts stay empty
- Carry alpha test, alpha map, map alpha, transparency and depth write into material presets
- Track model and environment loads per request so stale loads cannot clear readiness
- Keep model and environment errors in separate slots and flag a missing custom HDR
three 0.183.2 -> 0.185.1 and @types/three 0.185.4. The published runtime now
asks for three ^0.185.0, so projects on 0.183 have to update three with it
(changeset included). @webgpu/types is now an explicit dev dependency
referenced from src/types/webgpu.d.ts, because @types/three stops pulling
it in with 0.186.

0.186.1, the latest release, was tried first and set aside. It has a TSL
codegen bug (three #34339, still open): a shared math value first read
inside an If/select branch is cached in a variable assigned only in that
branch, so every other read sees 0. It broke the Photographic Cells outline
at gap 0 and Focus Blur, and a scan of every WGSL shader the composition
suite generates found the same uninitialized reads in Glass, Connected Dots,
Photocopy and Plotter, with none on 0.185.1. r186 started counting usage
through ContextNode, so far more nodes get cached than before. Turning that
cache off inside branches and loops fixed Cells but made glass and blur
shaders compile several times slower and still failed a Focus Blur check,
so it is not worth shipping. r187 rewrites this cache; retry then with the
full suite and the WGSL scan.

The 3D layer no longer skips compileAsync for transmissive models: 0.183
still throws the transmission CopyTextureToTexture format error without the
skip, 0.185 does not. The exact-pose uniform refresh stays, since three only
fixes that in r186 (0.185 without it fails the pose-return test). The shadow
node-frame advance and the persistent environment node stay as well.

Visible changes: environment rotation follows three r184 (same direction as
rotating an object), so Studio Rotation turns the other way than on 0.183;
the layer is unreleased, so there is no migration. The r185 premultiplied-
alpha output change only touches transparent output pixels and the canvas
is opaque; all alpha checks pass.

three now reports uncaptured GPU errors through console.error. SwiftShader
cannot import decoded video frames, which produces the same 64 validation
errors on 0.183 (Chrome printed them as warnings, so they were never
counted). The composition suite now ignores exactly that ExternalTexture
chain, prints how many it skipped, and still fails on any other GPU error.

The roadmap records the new order the user asked for on September 29: three
upgrade, animation clips, SVG to 3D, and the path-traced Render mode last.
The shader reference lists every three/tsl export so the MCP can tell agents
which TSL functions exist. three 0.185 adds packNormalToRGB,
unpackRGBToNormal, transformNormalByInverseViewMatrix and the other names
from the r184 and r185 releases, so CI's snapshot check failed until the
generated file was refreshed.
@git-chad
git-chad force-pushed the git-chad/v3-model-layer branch from e73a83d to ecc6f02 Compare October 1, 2026 14:03
@git-chad
git-chad force-pushed the git-chad/v3-three-upgrade branch from 08db20e to 30df7bb Compare October 1, 2026 14:03
@git-chad
git-chad changed the base branch from git-chad/v3-model-layer to git-chad/shader-lab-v3-plan October 1, 2026 15:14
@git-chad
git-chad merged commit 30df7bb into git-chad/shader-lab-v3-plan Oct 1, 2026
4 of 5 checks passed

This branch was successfully deployed

1 active deployment
Preview — 30df7bb0 Deployed Oct 1, 2026 by vercel[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