# LANE 3 — Web smash toy: thriftgod-based FP demo, 3GOD props, Rapier physics **Owns:** `web/` (new dir in the destroyulator monorepo). **For:** an Opus 4.8 session. **Runs on:** M3 Ultra (serve locally); deploy target later is the digalot.fyi VPS. ## Objective A **shareable, instant-link web toy** — not a port of the Godot game, a different product in the same universe. Walk around a room first-person, grab records, and smash the place, in the browser. Built on thriftgod's proven FP-interior code, physics via Rapier (WASM), props pulled live from the 3GOD depot. This is the streamer-bait / "try it in one click" front door. ## What already exists (study, then fork — DON'T edit thriftgod) - `~/Documents/thriftgod` (local) — LIVE Three.js first-person game (digalot.fyi). Copy the patterns, don't modify it. Reusable pieces: - `web/index.html` (~129k) — pointer-lock WASD FP controller, seeded procedural interior, 3D pickup/inspect interaction, **GLTFLoader + DRACOLoader** wiring (~line 1233, fixture kind `'glb'`). This is your movement + loading foundation. - `web/dig.js`, `web/cards.js` — interaction + item UI patterns. - Three.js r0.175 via unpkg importmap, no build step (keep this — zero-tooling is a feature). - `~/Documents/Destroyulater/game/scripts/dig.js` reference (recordgod) — procedural Web Audio "fwip/thunk"; reuse the synth approach for smash SFX (mirror Godot's `Juice.gd` material voices). - 3GOD depot: `https://digalot.fyi/3god`, `GET /api/list` → shelf JSON; GLBs by slug. **Read-only.** ## Deliverables (all under `web/`) ### 1. `web/index.html` + `web/main.js` — the app - Fork thriftgod's pointer-lock FP controller + a simple room (floor, walls, a couple of racks). - Load props from 3GOD by slug via GLTFLoader (start with `record`, `crate`, `milk-crate-stack`, `vintage-cash-register`, and the Lane-2 printer once it lands). Fallback to a local `web/assets/` copy if the depot is unreachable, so the demo never hard-fails offline. ### 2. `web/physics.js` — Rapier (WASM) destruction - Use `@dimforge/rapier3d-compat` (or `-simd`) via CDN/ESM. One rigid body per smashable prop, colliders auto-sized from the GLB bounding box (mirror Godot's `_body_local_aabb` approach). - **Support-graph cascade** — port the Godot idea: frozen (fixed) bodies hold up dynamic ones; destroying a support switches its dependents to dynamic. Same "knock the legs, it pancakes" feel. - **Pre-fractured swap:** when a prop has a `.fractured.glb` (Lane 2 contract #2), on smash hide the intact mesh and spawn each `chunk_*` as a short-lived dynamic body inheriting velocity. Brittle kinds (vinyl/glass) also shatter on hard impact. Pool + despawn debris (JS GC is the ceiling — cap active bodies, sleep/remove settled ones). ### 3. `web/juice.js` — feel layer - Screenshake (camera trauma), a brief hitstop (scale the physics step / freeze a few frames), per-material particle bursts (Points or instanced quads), and **procedural Web Audio** smash SFX per material (wood crunch, cardboard rip, vinyl crack+warble, glass tinkle, steel ring) — same recipe as Godot `Juice.gd::_synth`. Combo counter in the DOM HUD. ### 4. Record ritual (lite) + share hook - A simplified version of Lane 1's ritual: click a record to pull it (drag to slide the disc out), then throw/smash. Keep it snappy for web. - A "share your mess" button: screenshot the canvas (`toDataURL`) + a smash-count caption. ## Serve + acceptance ```sh cd web && python3 -m http.server 8099 # then open http://localhost:8099 ``` - Loads with no console errors; FP movement + pointer lock work; at least one 3GOD prop loads and smashes with particles + sound; holds ~60fps with a roomful of props on the M3 (test in Chrome AND Safari — Safari is the weakest WASM/WebGPU target). - Cross-origin note: fetching GLBs from digalot.fyi needs CORS on 3GOD; if blocked, mirror the handful of props into `web/assets/` and load locally for now (flag it for the depot owner). ## Gotchas - Don't import a bundler unless necessary — thriftgod's no-build importmap approach is the norm here. - Rapier is WASM: `await RAPIER.init()` before creating the world; keep physics off the render thread if it janks (Web Worker + SharedArrayBuffer needs COOP/COEP headers — only if needed). - The 3GOD SSRF/open-mode issue (see PLAN.md) is a WRITE-side problem; this lane only reads, so it's unaffected — but don't add any write calls to the depot from the web client. ## Definition of done A playable `web/` smash toy: walk, grab a record, smash props (fractured where available) with juice, in Chrome + Safari, props from 3GOD (or local mirror). Committed + pushed to `origin main` under `web/`. Post a localhost screenshot/gif.