m4bwav/threewright
Evergreen three.js skills for AI agents: current r186 APIs (WebGL, WebGPU, TSL), a knowledge base of topics, scenarios, libraries, recipes and rules with release ranges so stale advice stays labelled legacy, verified starters (single-file, WebGPU, Vite, React Three Fiber, 3D chart, video turntable, Rapier game), and a zero-dependency Node CLI that checks pages headlessly as text first (errors, scene numbers, pixel evidence), lints stale APIs against the installed release, reports glTF models, and records frame-exact video.
Changelog
Plugin versions. Skill-level changes are in each skills/*/CHANGELOG.md; the session record is ai-docs/log.md.
0.6.5 · 2026-10-05
-
Copilot-ready: a root
plugin.json(Agent Plugins 1.0 shape:$schema, at most 10 lowercase-hyphenated keywords, author URL) so GitHub Copilot CLI and the awesome-copilot marketplace find the plugin; the awesome-copilot intake gates look only at.github/plugin/,.plugin/or the root, never.claude-plugin/. Claude Code still reads.claude-plugin/plugin.json; keep both versions equal. Installed with Copilot CLI 1.0.92 andvally lint(0.17.0) passes. The release tag v0.6.5 is the ref awesome-copilot pins. -
tw lintruletyped-buffer-attribute-copies(info): flags aFloat32BufferAttribute(or another typed subclass) made from a variable that the same function writes again later. Those classes copy the array, so the later writes never reach the GPU, silently (LEARNINGS L-20261004-1; it cost markdavidrogers-web PR #37 its whole road for a round of screenshots).
0.6.4 · 2026-10-04
Nothing to install with the plugin.
package.jsonandpackage-lock.jsonmoved from the plugin root todev/. The Claude directory held 0.6.3 for content policy review because Claude Code would install from a root lockfile when a user installs the plugin; the only dependency was the dev dependency three, used by the tests. Runnpm installandnpm testindev/.tw(CDN serving,tw newasset generation,tw deprecations), the browser tests andevals/headless/run.pyfind three indev/node_modulesas well asnode_modules, and still fall back to the tw cache ornpm pack.
0.6.3 · 2026-10-03
Prepared for the Claude plugin directory, which takes no binary files except images and no files over 256 KiB.
- The product-viewer model.glb and the splats scene.splat are no longer in the repository.
tw newwrites them in the new folder from the template's own script (make-model.mjs, make-splat.mjs), listed undergeneratein template.json; the model's three 0.186.1 comes from node_modules, the tw cache ornpm pack. Both come out byte for byte the same as the files they replace. The browser tests andevals/headless/make-inputs.mjsgenerate them the same way. - Every
npxandnpm createcommand in the knowledge base names an exact version:@gltf-transform/cli@4.5.1,gltfpack@1.3.0,@iwsdk/cli@1.0.1,npm create @iwsdk@1.0.1. The globe-gl texture URL names three-globe 2.45.3. - plugin.json gains homepage, documentationUrl, supportUrl and privacyPolicyUrl; the README gains a Privacy section listing every network route.
0.6.2 · 2026-09-26
Less to run, one trigger gap closed.
- Outcome cases the no-plugin baseline also passes are marked
redundantin evals.json (8 of 10 skills; games and curate keep theirs).evals/headless/run.pyskips them unless--all, so a full pass is 37 runs instead of 69. - threewright-r3f's description covers checking that an R3F app builds and runs; the case that missed now fires the skill 3 of 3.
- XR is dropped from the plan.
0.6.1 · 2026-09-26
The eval pass is complete, with a harness in the repo so the next one is one command.
evals/headless/:run.pyruns every action and outcome case throughclaude -p --plugin-dirin fresh workspaces (3 runs each plus a no-plugin baseline, resumable), andgrade.pygrades them on the trace and the disk into evals/results/headless-latest.md.make-inputs.mjsregenerates the input models and CSV.- Result (69 runs, $39): every skill's action and outcome cases pass 3 of 3 through the Skill tool, except r3f outcome-1, where run 3 fired no skill. Baselines without the plugin also pass most outcome cases, so the plugin's measurable difference is the verified
twstep (action cases). - threewright-curate edits the knowledge base of a git checkout, never the installed plugin copy (which Claude Code refuses to edit and an update would overwrite).
- Outcome cases no longer grade the reply's wording (quotes, alt text, driver names): only files, exit codes and trace calls.
0.6.0 · 2026-09-26
product-viewer ships with no CDN requests, the decoder advice agrees with r186 everywhere, and a partial eval pass through the Skill tool.
tw vendoralso copies the files a vendored module fetches bynew URL('<relative>', import.meta.url). That is how r186's DRACOLoader and KTX2Loader find their WASM decoders, so vendoring them bringslibs/draco/andlibs/basis/along. These files are copied, not scanned for imports.- product-viewer is marked
"vendor": true, sotw newvendors it (21 files, 4.3 MB). It no longer sets a CDN transcoder path; both decoders load from next to their loaders. A Draco model and a KTX2 model check OK undertw check --cdn offline, and no CDN URL is left in the page. tw glbneeds lines andtw checkfix hints no longer tell you to callsetDecoderPath(...)orsetTranscoderPath(...). The load-gltf-with-decoders recipe comments the transcoder path out, and loaders-and-assets no longer says KTX2 "still needs" one.- Knowledge base: the games, video-and-turntables and generative-art scenarios no longer call game-starter, video-turntable or html-webgpu's WebGPU backend unverified, since their template.json records verified runs. video-and-turntables now points
template:at video-turntable. - Evals: 28 of a planned 70 headless runs through
claude -p --plugin-dir. The Skill tool fired in all 25 with-plugin runs. Stopped on a harness fault and a classifier refusal; details and follow-ups in evals/results/2026-09-26-headless.md.
0.5.0 · 2026-09-26
The scroll-hero gaps from the eval runs, and the bounds warnings that misfired on landscapes.
tw vendor <dir|page.html> [--offline] [--dry-run]: copies the pinned jsDelivr and unpkg modules a page imports, and their own relative and mapped imports, intovendor/<name>@<version>/, then points the import map at the copies. Only reached files are copied (scroll-hero: 9 files, 2.4 MB). Bytes come from node_modules, the tw cache ornpm pack, never the CDN.tw newvendors templates marked"vendor": truein template.json (scroll-hero);--no-vendorkeeps the CDN import map.- scroll-hero follows threewright-web's Step 2: a
<picture>poster (wide and tall, start and final view, 33 to 48 KB each) paints first, WebGL starts after first paint and the canvas fades in once a frame is drawn; with reduced motion the view holds the final keyframe while the text scrolls (0 pixels change between top and bottom, checked withtw diff). tw shot --out x.jpg|x.webp [--quality 82]writes JPEG or WebP (for posters); pixel stats stay PNG-only.tw checkleaves backdrop objects out of the scene bounds: built-in materials withfog: falsein a fogged scene, and objects beyond a third ofcamera.faror spanning half of it. The bounds line says how many were left out. "The camera is inside the scene bounds" no longer fires for wide, flat bounds (a landscape or floor the viewer stands on). New fixturetests/fixtures/pages/landscape; the campfire demo now checks clean.
0.4.1 · 2026-09-26
First full run of every skill's eval suite (61 cases, all passed; evals/ at the plugin root), and the fixes it turned up.
- Evals: trigger and decoy cases through
claude plugin eval2.1.281 (3 runs per arm, no-plugin baseline), action and outcome cases through the evergreen tester with a no-skill baseline for each action case. Results inevals/results/; each skill's TESTS.md has the entry. - Fixed:
tw videoMP4s lost the BT.709 primaries and transfer tags with ffmpeg 9.0.1; they are now written into the H.264 stream (h264_metadata). WebM output is now tagged BT.709 too (VP9 carries the matrix only). - Fixed: a Git Bash path with a query string (
tw check "/c/.../page/?model=x.glb") was not found, because Git Bash skips its path conversion for arguments holding a?; tw maps/c/...itself on Windows. - product-viewer wires
KTX2Loader, so models fromgltf-transform etc1soruastcload (checked with a KTX2 model: texture shows,tw scenereadsmap:CompressedTexture/srgb). - color-management: Verify named a lint rule that never existed; it now points at
tw scene's map colour-space label.
0.4.0 · 2026-09-26
The last items of the tools research list, and the KTX2 texture steps checked with KTX-Software installed.
tw sheet --sweep "<expr>=a,b,c"(alsocheck --sheet f --sweep ...): one tile per value from the current view, labelled, with the old value put back afterwards.tw videosteps a page throughwindow.advanceTime(ms)when it defines one and no__tw.renderFrame; the driver prints aspage advanceTime().- tw shares an existing
__THREE_DEVTOOLS__hook (the three.js DevTools extension) instead of replacing it. - KTX-Software 4.4.2 installed on the Windows test machine;
gltf-transform etc1sanduastcran against a generated textured model andvalidatefound no errors.
0.3.0 · 2026-09-26
tw learns to drive a page and to find leaks and shader errors; six tw bugs from the 0.2.0 handoff are fixed.
--actions "key KeyW 500; click 480,270; drag ...; wheel ...; type ...; wait ..."on check, shot, sheet and video: trusted CDP keyboard, mouse, pointer and wheel input before measuring (virtual-clock waits in video).tw check --cycles N [--cycle "<js>"]: runswindow.__tw.cycle()or the js N times and fails on steady growth of geometries, textures or programs.tw shaders <page> [--dump dir]: WebGL programs with their materials and link status, a failing shader's error lines with source context, sources written to files.--labelson shot and sheet: name tags at projected object centres (duplicates as xN, overlapping tags pushed apart), with the legend printed as text.tw check --shot a.png --sheet b.png --tree: several outputs from one browser launch.tw checkprintsrender_game_to_text()output asgame:; game-starter now defines it.--color-scheme light|darkemulatesprefers-color-scheme; the light palettes of surface and globe are now shot and verified.- Local paths take a query string or hash (
tw check "page/?model=x.glb"). - Fixed:
tw glbbounds forKHR_mesh_quantizationmodels (normalized accessor min and max are raw integers). - Fixed: the aspect check reads the projection matrix, so a missing
updateProjectionMatrix()is caught. - Fixed:
tw scenenames a map's texture kind andNoColorSpaceinstead ofmap:(none). - Fixed:
tw video --capture canvasrecorded blank frames on render-on-demand pages; the last frame's screen passes are replayed when a step drew nothing. - Fixed: a flat-shaded object on a plain background (two colours with real coverage) was reported as a blank canvas.
0.2.0 · 2026-09-26
Second release: every planned template is built, every recipe has been run, and WebGPU has been verified on a real GPU.
- New templates, all verified on Windows 11, Chrome 153 and an RTX 5060 Ti: surface, globe, product-viewer (with a generated
model.glband itsmake-model.mjs), scroll-hero (GSAP 3.15.0, Lenis 1.3.26) and splats. - All ten recipes that had never been run now have been. Six needed fixes: environment-lighting leaked one geometry and one texture per swap; optimize-gltf's
--slotsvalues matched nothing in @gltf-transform/cli 4.5.0; dispose-a-scene, resize-and-pixel-ratio, keyboard-orbit-and-reduced-motion and upgrade-an-old-project needed corrections or missing steps. tw perf: frame-time percentiles, draw calls per frame, memory growth,--cpu-throttle.tw check --saveand--against: a text diff between two runs.tw shotandtw sheettake--evalto set a state before capture.tw checkpixel stats andshot --canvasmeasure the right area on a scrolled page.- RoomEnvironment is disposed after baking in html-importmap, html-webgpu and vite-ts.
tw check --evalbindsrenderer,sceneandcamerato the ones tw observed, so--eval "renderer.info.render.calls"works on pages that keep them module scoped (most do). The page's own globals still win.tw checkfails on three's own deprecation warnings (THREE.X ... deprecated), shown under their own heading;--strictalso fails on any other console warning. Warnings from other libraries still only print by default.tw checkignores a 404 for/favicon.ico(browsers request it on their own).tw checkbounds forGaussianSplatuse the splat cloud, not its quad geometry.- npm is spawned without the Node 24 DEP0190 warning on Windows (
scripts/lib/proc.mjs). - Browser tests build vite-ts, r3f and game-starter and check their
distwhen the template'snode_modulesexists. tw kb validatewarns on an em dash outside code fences (house style);--strictfails on it.- WebGPU verified on the real backend (Windows, Chrome 153, RTX 5060 Ti): the html-webgpu template and the bloom-webgpu, tsl-custom-material and webgpu-backend-check recipes.
- bloom-webgpu: lower emissive and bloom strength and add a directional light; the old values blew the whole frame out on both backends.
0.1.0 · 2026-09-26
First release.
- CLI
scripts/tw.mjs(Node 22+, no dependencies):- verification:
check(with pixel evidence and--eval),scene,shot,sheet,videoanddiff - static tools:
lint,deprecations,glb,kb,templates,new,versionsanddoctor - runs in containers and offline:
--cdn, Playwright Chromium,--no-sandboxas root
- verification:
- Knowledge base: 89 entries (23 topics, 13 scenarios, 30 libraries, 20 recipes, 3 rules) with release ranges and a current or legacy status. Nine one-page recipes were run through
tw check. 67 stale-API lint rules, with release numbers confirmed against npm tarballs; every r186@deprecatedmarker is covered or skipped on purpose. - Templates, all verified with
tw check: html-importmap, html-webgpu (WebGL 2 fallback only), vite-ts, r3f, chart-3d-scatter, video-turntable, game-starter. - Ten skills with evergreen files and eval suites. The suites are written but not yet run.
- Research notes for ten tracks in
ai-docs/research/.