# LANE C — World Builder (the Booth) **Read first:** `docs/DESIGN.md` (especially §3–§4), `docs/CONTRACTS.md`, everything in `src/core/`. **You own:** `src/worldgen/**`, `src/demo/worldgenDemo.ts`, `demo-worldgen.html`. **You may not touch anything else.** ## Mission Author the entire diorama in voxels: `buildBooth(world: IVoxelWorld): void` writes every static block of the DJ booth — plywood shell, tabletop, two turntable plinths, the mixer massif, cable canyon, patch-bay wall, and the PCB Depths under the table. This is the level-design lane: you are building a *place*, and the inspiration photo (plywood console, silver 1200s, black 4-channel mixer, blue records, wall patch bay, visible hinges) is your mood board. Deterministic output: same world every run (seedable PRNG, fixed seed). **What you do NOT build:** the platter, record disc, tonearm, fader caps/sleds, and buttons — those are moving machines (Lane D). You build the *sockets* they sit in: the platter well (a recessed circular pit around each spindle), the tonearm base mount, empty fader slots, button recesses. Use `LAYOUT` from constants for every position Lane D also needs. ## Provides - `buildBooth(world: IVoxelWorld): void` — main entry, composed of exported sub-builders so they're individually testable: `buildShell`, `buildDeck(A|B)`, `buildMixer`, `buildCableCanyon`, `buildPatchBay`, `buildUnderTable`. - `SPAWN: Vec3` — export the player spawn (on Deck A's plinth top, near the platter well, facing the mixer). ## Consumes `IVoxelWorld` interface only. Prefer `fillBox` when available (Lane A adds it; code against the interface plus an optional duck-typed `(world as any).fillBox?.(…) ?? loop` fallback so your demo's simple mock still works). ## Tasks & structure specs Work top-down; keep each builder a pure function of (world, LAYOUT, prng). A tiny toolkit first (`src/worldgen/tools.ts`): `box`, `hollowBox`, `cylinderY` (disc/annulus fill), `line3` (thick voxel line), `spline` (Catmull-Rom sampled → spheres stamped along it, for cables), `mulberry32` PRNG. ### C1. Shell & tabletop - Plywood floor at world bottom, walls (rimThickness) up to `Y_BOOTH_WALL_TOP`, back wall at `backWallZ`. `ply_edge` striping on every exposed wall top/cut edge (that laminate look). Two chrome `screw`+ `brushed_alu` hinge plates on the back wall like the photo. - Tabletop slab at `Y_TABLETOP` spanning inside the rim, with: a rectangular vent slot behind each deck (the way down to PCB Depths), and a 6×6 open hatch near the front-left with a plywood step-stair down into the crate. ### C2. Decks A & B (steel-grey mesas) - Plinth: filled `steel_grey` box from tabletop to `Y_PLINTH_TOP` over the LAYOUT footprint, 2-voxel `rubber` feet at corners, a `brushed_alu` top face border. Rounded-ish corners (chamfer 2–3 voxels). - **Platter well**: recessed circular pit centered on the spindle, radius `PLATTER.radius + 2`, depth 6 below plinth top — leave it EMPTY (Lane D's rotating platter fills it). Chrome 1-voxel spindle stub at center bottom. - Deck-top furniture (all voxel, non-moving): start/stop button recess (4×4×1 pit, front-left), two small round 33/45 button recesses beside it, **pitch fader canyon** — an 18-long, 3-wide, 4-deep slot on the right side with `led_green` center-detent dot at its midpoint, tonearm base mount — a 10×10 `brushed_alu` raised pedestal at back-right with a 3-voxel chrome post stub, and a `strobe_dot` + `led_red` strobe lamp pillar at front-left corner (the classic Technics pop-up light). - Deck B only: drifts of `dust` blocks banked on top and around; its glass dust cover half-open — a `glass` slab canopy over the rear half at y ≈ `Y_PLINTH_TOP`+18, hinged at the back wall side, forming the crystal cave (2-voxel-thick, walkable on top). ### C3. Mixer Massif - `matte_black` body over LAYOUT.mixer footprint, tabletop → `topY`+40 (i.e. face plate at y=67... use `LAYOUT.mixer.topY`); `speaker_mesh` side-vent ladders (climbable = 1-voxel ledges every 2), `brushed_alu` face plate border. - Face plate furniture: 4 channel strips, each with a 3×3 grid of knob *stems* (1-voxel `knob_black` stubs — caps are Lane D machines) and a **fader alley**: 14×3 slot, 5 deep. Front edge: the **crossfader tram** — a 40-long, 4-wide, 6-deep slot running left–right; fill its center with a plug of 12 `dust` blocks (the quest jam). Back edge: two **VU towers** — 12-tall columns, alternating `led_green`×8 / `led_amber`×3 / `led_red`×1, plus a `matte_black` casing open on the front face. - Rear face: master **power switch recess** (Lane D machine) and a fuse-door slot leading down a chimney into the PCB Depths fuse room. ### C4. Cable Canyon (z 140–200) - 4–6 cables, each a 2×2 `cable_black` tube along a drooping Catmull-Rom spline from gear rear panels (RCA heights on deck/mixer backs) over the canyon floor to the patch-bay wall; two of them sheathed `cable_red` / `cable_white` near the ends with `rca_gold` plug heads (3×3×4). - One cable is THE quest cable: it ends 8 voxels short of the patch bay, its gold plug resting on the canyon floor, aligned with an empty socket. - Canyon floor: tabletop plywood with dust drifts, a few vinyl slab off-cuts leaning on walls (walkable ramps), power-strip block (matte_black box with `led_amber` pilot light) feeding cables. ### C5. Patch Bay (back wall) - Over `LAYOUT.patchBay`: a `brushed_alu` panel proud of the wall by 2, with a 2×6 grid of round `rca_gold` sockets (3-voxel-diameter rings, 2 deep); most plugged with cable-colored plugs, one conspicuously EMPTY with an `led_red` blink block above it (the quest socket — Lane D puts the interactive socket machine here; you build the hole). - Two sockets are real doorways: 3-deep tunnels opening into a small room *inside the wall* with copper busbars and a `led_blue` glow — a secret. ### C6. PCB Depths & the crate (under-table, y 0–40) - Under each deck and the mixer: rooms with `pcb_green` floors, `copper` trace paths inlaid (1-wide runs connecting solder-blob clusters), solder stalagmites, capacitor pillars (cylinders: `matte_black` body, `brushed_alu` top with cross-scored cap), resistor beams bridging gaps. LED sprinkles (`led_green/amber`, sparse, per DESIGN's "dim until repaired" — just place them; Lane E handles dimming). - **Fuse room** under the mixer: fuse box on the wall — a `brushed_alu` frame holding one blackened fuse (use `rubber` block flanked by `chrome` caps to read as "blown"), and a parts bin nearby: an open plywood tray holding a fresh fuse (`glass` block with `chrome` caps), plus the **stylus block** (single `chrome` block on a felt pad, spotlit by one `led_blue`) — the two quest pickups. Coordinate: place pickups exactly at positions exported as `QUEST_POS` (export a const with stylus/fuse/plug/socket world coords so Lane D reads positions from you at integration — put it in `src/worldgen/questPositions.ts`, derived from LAYOUT, no magic numbers elsewhere). - **Record crate** (front-left, under the hatch): plywood cavern holding 8–10 giant vinyl slabs (2-thick discs on edge, alternating `vinyl_black` / `vinyl_blue` with `label_cream` centers), gaps walkable, one leaning slab as a ramp back up. ### C7. Set dressing pass - The booth should feel *used*: dust in corners, one screw block half-out, scattered `label_cream` sticker blocks on the shell, cable stubs. Restrained — this is a tidy booth, per the photo. ## Demo (`demo-worldgen.html`) Build the full booth into a simple array-backed mock world, render with a naive `InstancedMesh`-per-block-id visualizer (flat colors from `blocks.ts` tints — fine at this scale), OrbitControls, per-zone teleport buttons, and a stats line: total non-air voxels, per-id counts, build time (< 500 ms). Validation asserts (console.error on fail): platter wells empty, spawn stands on solid, quest positions non-air-adjacent-reachable (has an adjacent air voxel), world edges sealed (no non-air outside bounds… i.e. no writes OOB). ## Acceptance - [ ] `buildBooth` writes only through `IVoxelWorld`, deterministic across runs - [ ] Every zone from DESIGN §4 exists and is visually recognizable in the demo - [ ] All LAYOUT-adjacent geometry derives from `constants.ts` (grep-proof: no duplicated literal for spindle/mixer coords) - [ ] Platter wells, fader slots, button recesses left empty for Lane D - [ ] `questPositions.ts` exports all quest coords; validation asserts pass - [ ] Build < 500 ms; `npm run typecheck` clean; `HANDOFF.md` written ## Out of scope Anything that moves (Lane D), real meshing (Lane A), lighting/audio (E), player (B).