Anatomy Three.js — Interactive 3D Technical Figures
Build interactive isometric 3D technical illustrations in Three.js + GLSL. Every figure explains a technical idea as a physical bench-top instrument or cutaway test rig you could set on a desk: a heavy beveled bedplate on turned feet, domain-authentic housings and linkages, a hero internal rotor or moving subject, directly draggable 3D controls and instruments built specifically for that machine, and a custom GLSL shader restricted strictly to the physical medium (fire, plasma, fluid, downwash, beam) inside the machine.
Instead of writing a 1,500-line monolithic HTML file and guessing 3D coordinates in your head, you first align with the user on the apparatus and interactions (Stage 0), then build every figure in six isolated stages inside figures/<slug>/, backed by the immutable kit/ engine and verified at every step by a two-tier 3D Spatial + Orthographic Visual Harness.
Directory Layout
anatomy-threejs/
├── SKILL.md # Stage 0 brief + 6-stage workflow & agent entrypoint
├── kit/ # IMMUTABLE ENGINE — import from here; never edit per figure
│ ├── shaders.mjs # 5 themes, 4-tone + top-cap cel shader, inverted hull, creases
│ ├── primitives.mjs # Axis-aware slabs, cylinders, gears, screws, knurls, ticks
│ ├── registry.mjs # Recorder3D: .group(), .part(), .anchor(), .tube(), .interactable(), .tag()
│ ├── audit3d.mjs # Headless Node 3D spatial audit (collisions, floating, bore, tubes)
│ ├── runtime.mjs # Orthographic rig, raycasted 3D drag, custom controls, springs, __ANATOMY_DEBUG__
│ ├── bundle.mjs # Compiles figure modules into a single self-contained HTML file
│ └── shell.html # Responsive card chrome, 5-theme CSS variables, feature-aware controls
├── scripts/
│ ├── inspect.mjs # Headless Chrome (CDP) 2x2 orthographic blueprint & joint overlay
│ ├── drive.mjs # Interactive contact sheet + hero stills + mobile 390px verify
│ └── chrome-port.mjs # Zero-dependency WebSocket + PNG stitcher for headless Chrome
├── references/
│ ├── craft-3d.md # 3D coordinate system, tone hierarchy, bespoke controls, anti-patterns
│ ├── kit-3d.md # API reference for Recorder3D, primitives, shaders, and runtime
│ └── verify-3d.md # How to interpret and fix [COLLISION], [FLOATING], [TUBE KINK], and ortho views
└── figures/
└── <slug>/ # Your workspace for a figure (e.g. figures/wankel-engine/)
├── model.mjs # Stage 1: Pure math, dimensions, SPEC.features, SPEC.controls, anchors
├── base.mjs # Stage 2: Bedplate, feet, halo, ruler, brass plaque
├── structure.mjs # Stage 3: Pedestals, frame/housing, ports, fasteners
├── subject.mjs # Stage 4: Moving core assembly (rotor, blades, seals, gears)
├── mechanism.mjs # Stage 5: Bespoke controls, instruments, 3D interactables, tubes, updateKinematics()
├── shader.mjs # Stage 6: Custom GLSL shader confined to the active physical medium
├── build.mjs # CLI entry: --test-model, --audit, and full HTML bundle
└── dist/
└── <slug>.html # Compiled self-contained interactive figure
Rule 1 — Never rewrite
kit/: The Three.js camera, cel shaders, raycast drag handler, spring solver, DOM chrome, and debug harness live inkit/. Your figure files only define domain math, 3D parts, kinematics, interactables, and the custom medium shader.Rule 2 — Read the references before writing geometry:
- Read
references/craft-3d.mdbefore Stage 0/1 to design the physical rig, bespoke controls, feature flags, tone hierarchy, and 3D coordinate frame.- Read
references/kit-3d.mdbefore Stage 2 for the exact signatures ofRecorder3Dandkit/primitives.mjs.- Read
references/verify-3d.mdwhenever--audit,inspect.mjs, ordrive.mjsreports a problem.
Stage 0 — Apparatus & Interaction Brief (Align Before Building)
Before creating files or writing Stage 1 code, propose a concise Apparatus & Interaction Brief to the user and align on what the machine does and how the reader plays with it (skip waiting only if the user explicitly says to build immediately without questions):
- Bench-Top Apparatus & Hero Subject: Name the physical mechanism and what single assembly carries the
litaccent. - Direct 3D Manipulation (
reg.interactable): Identify which 3D assemblies or controls the reader can grab and drag directly in the viewport (e.g., dragging a swashplate to tilt/lift it and watch rotor blades feather, sliding a vernier carriage, pulling a linkage lever), including while rotation/simulation is paused. - Domain-Authentic Controls & Telemetry (
SPEC.controls): Choose bench hardware and control-bar sliders that belong to this machine (e.g., 2-axis cyclic stick, collective pitch quadrant, protractor arc, micrometer drum, vernier scale, manometer tube). Never copy another figure's rotary knob, Bourdon pressure gauge, or hydraulic box unless the subject genuinely uses that exact instrument. - View Modes (
SPEC.features): Decide explicitly which optional modes earn their place:explode: Enable (true) only for enclosed concentric machines with stacked axial layers (e.g., engines, gearboxes, pumps). Setexplode: falsefor open mechanical linkages, swashplates, optical benches, or surface rigs where separating parts breaks linkage geometry and adds clutter.cutaway: Enable (true) when an outer casing conceals moving internals; setfalsefor open-frame mechanisms.shader: Enable (true) when a confined physical medium (combustion gas, rotor wake, beam, fluid flow) clarifies the mechanism.
Stage 0 Gate: User confirms or refines the brief (or explicitly instructed an immediate non-interactive build).
The Six-Stage Workflow
Execute these stages in order. Each stage ends with a Verification Gate — run the command, fix any errors, and visually inspect the rendered PNG with your file-reading tool before moving to the next stage.
Stage 1 — Model & Kinematics (model.mjs + build.mjs)
Translate the agreed brief into a pure physical math module before touching Three.js:
- Encode the Physical Metaphor in
figures/<slug>/model.mjs:SPEC.features:{ explode: boolean, cutaway: boolean, shader: boolean, crank: boolean }matching Stage 0.SPEC.interactionHint: Short hint shown in the bottom-left corner telling the user what 3D parts they can drag directly (e.g.,"Drag swashplate, stick, or collective lever").SPEC.controls: Array of domain-specific slider controls ({ id, label, min, max, step, initial, title }).runtime.mjsrenders these in the control bar, smooths them with springs, and passes them intoupdateKinematicsandformatReadout.SPEC.auditPoses&SPEC.drivePoses: Domain-representative poses (extreme control angles, paused manipulation, cutaway/theme checks) used by--auditandscripts/drive.mjs. Omit exploded poses whenfeatures.explode === false.SPEC: All critical dimensions (BASE_W,BASE_D,BASE_H,BASE_Y,topSurfaceY,isoElevDeg: 30,isoAzimDeg: -36,initialZoom,initialPanY,minVertSpan,minHorizSpan).ANCHORS: Named[x, y, z]mounting coordinates on the bedplate (plaqueCenter,rulerCenter, and your figure's bespoke control/instrument anchors). Remember that in canonical isometric (30° elev, -36° azim), the+Zedge is the front apron and-X, +Z/+X, +Zare the unobstructed front corners.- Pure state & kinematic functions (zero Three.js imports) driven by
thetaand the liveparamsfromSPEC.controls. formatReadout({ theta, effectiveRpm, params })returning{ readout, phase }strings for the telemetry bar.
- Scaffold
figures/<slug>/build.mjs(referencefigures/wankel-engine/build.mjsorfigures/helicopter-swashplate/build.mjs) and adjust title, subtitle, initial readout, and legend.
Stage 1 Gate:
node figures/<slug>/build.mjs --test-model
Pass criteria: Exits 0 with all anchors finite and readout/state verified across a full $0 \dots 6\pi$ cycle.
Stage 2 — The Base (base.mjs)
Build the heavy datum bedplate that grounds the entire instrument:
- Create
base.group(stage: "base"). - Add the soft ground shadow (
makeHaloMesh) atBASE_Y - 0.24. - Add
base.plateusingbuildSlabGeometry(BASE_W, BASE_D, BASE_H, { radius: 0.26, bevel: true, bevelSize: 0.025, bevelThickness: 0.025 })withramp: "lo",mountOn: "ground". Note thattopSurfaceY = BASE_Y + BASE_H + 0.025accounts for the top bevel thickness so parts mounted attopSurfaceYsit flush on the top face without sinking in. - Register all bedplate mounting anchors via
reg.anchor(id, x, y, z, baseGroup)so Stages 3 and 5 snap directly tobase.plate. - Add inset perimeter grooves (
makePlanOutline), 4 turned corner feet (base.foot.collar.i+base.foot.pad.i) with top counterbore screws (makeScrewDetail), a graduated scale (base.ruler+makeRulerTicks), and a rivetedbrassspecification plaque (base.plaque) on the visible front apron (+Z) with an engraved tag (reg.tag("tag-plate", ...)).
Stage 2 Gate:
node figures/<slug>/build.mjs --audit
node scripts/inspect.mjs figures/<slug>/dist/<slug>.html --isolate base --ortho --out figures/<slug>/dist/inspect-base.png
Read inspect-base.png to verify the feet sit cleanly underneath the bedplate and the brass plaque + ruler are unobstructed on the front apron.
Stage 3 — The Structure (structure.mjs)
Build the stationary load-bearing supports, main frame or cutaway housing, ports/manifolds, and fasteners:
- Mount support pedestals or gearboxes onto
base.platewith flanged bases, bolt details, tapered columns, and bearing saddles. - Build the main housing or frame (
ramp: "mid",outline: "hi"). Leave the front (+Z) open or fitted with a cutaway cover (cutawayHide: true) so interior mechanisms remain completely visible from the canonical isometric camera whenfeatures.cutawayis enabled. - Add domain-authentic ports, actuator housings, or stationary brackets pointing outward cleanly. Register 3D port/terminal anchors with their local outward port normals (
reg.anchor("plug.leading.tip", 0, 0.68, 0, spGroup, { dir: [0, 1, 0] })) inside each local sub-group so Stage 5 tubes snap to their exact world positions and exit/enter coaxially. - Set
explode: [ex, ey, ez]vectors on removable sub-groups only whenSPEC.features.explodeistrueand the apparatus has enclosed concentric layers meant to separate axially.
Stage 3 Gate:
node figures/<slug>/build.mjs --audit
node scripts/inspect.mjs figures/<slug>/dist/<slug>.html --isolate base,structure --ortho --out figures/<slug>/dist/inspect-structure.png
Read inspect-structure.png (TOP, FRONT, RIGHT, CANONICAL ISO): verify ports point outward, pedestals touch both the bedplate and housing without clipping, and nothing floats.
Stage 4 — The Subject (subject.mjs)
Build the hero internal assembly that commands visual focus:
- Assign
ramp: "hi"to the primary moving body (e.g., rotor body, hub yoke),ramp: "lit"to active working elements (rotor.apexSeal.i,blade.airfoil.*), andramp: "brass"to precision gearing and pivots. - Create nested kinematic groups (
orbit -> spin, orazimuth -> flap -> pitch) so hierarchical motion composes cleanly without Euler gimbal lock. - Pass
insideBoreOfandboreClearanceFnon parts moving inside a bore soaudit3d.mjsmathematically verifies zero wall penetration at every audited pose. - Add machined pockets, face grooves, spar seams, or bolt patterns so motion is unmistakable.
Stage 4 Gate:
node figures/<slug>/build.mjs --audit
node scripts/inspect.mjs figures/<slug>/dist/<slug>.html --isolate subject --ortho --out figures/<slug>/dist/inspect-subject.png
Read inspect-subject.png to confirm the subject's silhouette, bevels, and joints read clearly from every angle.
Stage 5 — Mechanism, Bespoke Controls & Direct 3D Interaction (mechanism.mjs)
Connect the subject to domain-authentic operator controls, telemetry instruments, direct 3D drag interaction, and routing:
- Build domain-authentic controls at their
ANCHORSfrom Stage 0/1 (for example: a 2-axis gimbal cyclic stick, a notched collective pitch quadrant lever, a vernier leadscrew, or a knurled throttle drum). Never copy another figure's knurled knob or manifold block when the machine calls for its own control hardware. - Build domain-authentic telemetry (for example: a curved blade-pitch protractor arc, linear travel scale, manometer column, or dial gauge) positioned forward in
+Zor clear of the main silhouette inCANONICAL ISO. - Register direct 3D manipulation with
reg.interactable(targetId, { cursor, hint, onDrag })on the primary mechanism and/or physical levers so the user can drag 3D parts directly in the viewport viasetParam(...), even when rotation is paused (RPM = 0orPAUSED). - Route all pipes and cables using
reg.tube(id, fromAnchorId, waypoints, toAnchorId, opts):- Query
reg.getAnchorWorldPos(anchorId)andreg.getAnchorWorldDir(anchorId)to build smooth 3D arch waypoints (length / chord >= 1.12) that sweep clear of adjacent parts and enter coaxially along each port's outward normal.
- Query
- Build the drive train and articulated linkages. Pass
cutawayHide: trueon front-side housing covers or shrouds that conceal the internal mechanism at rest whenfeatures.cutawayistrue. - Implement
export function updateKinematics(reg, state)to drive all kinematic groups, linkages, physical controls, telemetry needles, and shader uniforms fromstate.theta,state.params(state.<controlId>), andstate.shaderEnv.
Stage 5 Gate:
node figures/<slug>/build.mjs --audit
node scripts/inspect.mjs figures/<slug>/dist/<slug>.html --ortho --out figures/<slug>/dist/inspect-ortho.png
node scripts/inspect.mjs figures/<slug>/dist/<slug>.html --joints --out figures/<slug>/dist/inspect-joints.png
Read both inspect-ortho.png and inspect-joints.png:
- In
inspect-ortho.png, verify all 4 panels (TOP,FRONT,RIGHT,CANONICAL ISO) show clean silhouettes, smooth cable arches, and unobstructed controls/scales. - In
inspect-joints.png, verify green anchor dots land squarely on their physical joints/ports and engraved HUD tags sit over their plaques/scales.
Stage 6 — Physical Medium Shader & Final Drive (shader.mjs)
Add the custom GLSL shader for the active physical medium (when features.shader is true) and run the full interactive drive test:
- Write
figures/<slug>/shader.mjsexportingbuildShader(reg, kit)and reacting tokit.onThemeChange(...)across all 5 themes (light,paper,dark,blueprint,sepia). - Mount the shader mesh inside the active physical zone with
transparent: true, depthWrite: false, bindinguEnvelopetostate.shaderEnv(Ikey /SHADERbutton). - Restrict fragment alpha (
alpha > 0) strictly to the active physical medium region so the shader fades to zero smoothly before any quad edge or solid wall. - Run the final build and verification suite:
node figures/<slug>/build.mjs
node scripts/drive.mjs figures/<slug>/dist/<slug>.html --out figures/<slug>/dist/
Read figures/<slug>/dist/inspect-drive.png, hero-light.png, hero-dark.png (plus hero-exploded.png if features.explode is true), and mobile-390.png:
- Confirm
console: cleanandscrolls sideways: no. - Check every panel of
inspect-drive.png(populated fromSPEC.drivePosesor the default poses): verify that manipulating each domain control visibly moves the mechanism, physical control hardware, and telemetry indicator, and that hidden disabled features (APART,CUTAWAY,SHADER) do not appear in the UI. - Check
mobile-390.pngto confirm the header, telemetry bar, and footer controls stack cleanly at390pxwidth without overlapping the 3D figure.