- Workshop (WORKSHOP_CARTRIDGE): five-stage cartridge assembly at Deck A — seat/square, crimp four tag-wires, torque screws, ride-the-arm counterweight balance, needle-drop diagnostic with per-fault audio + scope. Relay-synced (per-field co-op merge: held screw + carried wire survive remote state). - Glow-Up (G1-G5): 32px atlas with per-voxel variants, selective LED bloom (quality-gated), screen-print decal system + party flyers, mixer/PCB worldgen density pass, record groove-sheen side texture. - 10 confirmed multi-agent review fixes, incl. co-op screw-stomp soft-lock, ride-snap collider-identity (magnet feet / eaten record-fling), workshop SFX exact-match map (rca_seated hijack), beam ride colliders to the head, double-crimp guard, WIRING INCOMPLETE diagnosis mode, skate-abort timer, completedState wiring, per-tick material churn, trackingHeavy platter drag. - Crossfader playtest fix: slew-limited sled (3.4 v/s) + ribbed grip caps with amber index — the sled reads as a heavy handle, not a teleporting wall. - Demo harnesses: machinesDemo hold-key wiring, playerDemo seesaw phase continuity, audioDemo incomplete fault button. - Workshop sync: exact 1.0 screw endpoint gets its own emit signature. Verified: typecheck + vite build clean, live solo quest smoke, two-client co-op relay smoke (simultaneous torque, no rewind, exact convergence). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
5.0 KiB
Lane E — FX HANDOFF (src/fx/**)
Owned by Lane E. See src/audio/HANDOFF.md for the full lane summary.
Public API
const fx = new FxSystem({
scene, // THREE.Object3D to add fx to (Lane A's scene)
setEmissiveBoost, // Lane A's injected hook (v:number)=>void
getLevels: () => audio.getLevels(),
});
fx.update(dt); // each fixed tick
FxSystem self-wires to the bus (audio:beat, signal:repair, game:win,
platter:state, block:break) and adds all its own objects to scene.
What's in here
particles.ts—Burst(round-robin, finite-life pool: break puffs, win confetti, 45-rpm rim sparkle) andDustField(continuous brownian motes). Both preallocate typed arrays and mutate them in place — zero per-frame allocation; a shared canvas glow texture, no external assets.overlays.ts— emissive sprite overlays we own (never touching Lane A/C materials):VuBank(mixer VU towers, LED-segment stacks keyed togetLevels()),SignalTrace(a bright sprite that runs the signal-path polyline on each repair),PatchBlink(patch-bay socket blink).layout.ts— all overlay anchor points derived fromsrc/core/constants.tsLAYOUT(VU towers,SIGNAL_PATH, patch bay, dust box, deck rims).FxSystem.ts— orchestrator: the global LED emissive pulse (base steps up per repair, pulses on beats), rim sparkle, and the win visual timeline (1.8 s dark → needle-drop flash + confetti → living-booth steady state).
Notes for integration
- The only material Lane E touches is via the injected
setEmissiveBoost. - VU/trace/patch positions come from
LAYOUT; if Lane C's real LED towers sit elsewhere, adjustVU_TOWERSinlayout.ts(see friction #5 in the audio handoff). update(dt)is safe atFIXED_DT; the win visual timeline advances on thedtyou pass (keep calling it every tick so it doesn't stall mid-sequence).
Round 2 — Headshell Workshop
- Screw confetti:
FxSystemauto-tosses a burst of brushed-steel screws when the workshop completes — it reacts tosignal:repairwithnode === 'stylus'(Lane D callsquest.repair('stylus')). Also exposed as a publicfx.screwBurst(x, y, z)for explicit use. - The oscilloscope scope block is Lane D's (
src/machines/workshop/, its own canvas draw). Lane E does NOT render a scope — an earliersrc/fx/scope.tswas removed to avoid two scopes.
Glow-Up G3 + G5 — decals (src/fx/decals.ts)
Screen-print decals + the record groove texture. Built once, purely cosmetic.
import { DecalSet, recordSideTexture } from './fx/decals';
const decals = new DecalSet(); // no deps
scene.add(decals.group); // ~13 plane meshes (< 25 draw-call budget)
decals.dispose(); // frees geometries + canvas textures
- G3 bakes text/markings onto
CanvasTextures applied to thin planes floating0.05proud withpolygonOffset(no z-fight, verified at grazing angle). One canvas per gear piece keeps the count low. Positions come fromLAYOUT(core/constants.ts) and the worldgen anchors (MIXER/DECK_A/DECK_B/PATCH/UNDER) — nothing hardcoded. Print set: mixerCH 1–4+ fader ticks +TURNCRAFT-800+ crossfaderA↔B; each deckSTART·STOP/33/45, a pitch+/−ladder, and a "Quartz Direct Drive" plinth-front script; patch-bayPATCH BAY+01–12socket numbers; aFUSE 250Vlegend; and four procedural party flyers on the booth walls. - Flat (
up) faces flip each glyph 180° in place so labels read from the player's front-edge viewpoint (tick marks / positions stay put).
G5 — recordSideTexture(): THREE.CanvasTexture ← Lane D wires this
Signature: recordSideTexture(): THREE.CanvasTexture — no args; returns an
8×128 sRGB canvas texture, RepeatWrapping (S+T) + NearestFilter already set,
near-white base with fine dark groove lands so it multiplies cleanly over
the tier's existing vinyl tint.
A fine light/dark groove-ring texture for the vinyl tier side cylinders.
Rings run horizontally (constant around the circumference, varying up the
height). This lane does not edit platter.ts.
Exact merge point — src/machines/platter.ts, the sideMat around L121–124
(where sideMat = blockMaterial(vinylId, { opacity … }) is built and applied to
the outer + mid vinyl tier side cylinders):
import { recordSideTexture } from '../fx/decals';
// after: const sideMat = blockMaterial(vinylId, { opacity: ... });
sideMat.map = recordSideTexture();
sideMat.map.repeat.set(1, Math.max(1, tierHeightVox / 4)); // ~4 grooves/voxel of height
sideMat.needsUpdate = true;
// NearestFilter + RepeatWrapping are already set on the texture. No geometry or
// platter-logic change — purely a `material.map` assignment.
It's a standalone generator (no side effects); safe to call once and share the texture across both platters. Only the platter wiring is left — everything else here is live and browser-verified.