destroyulator/web/README.md
Monster Robot Party ca8b22c3c0 Lane 3: browser smash toy in web/ — FP record store, Rapier destruction, 3GOD props
A shareable one-click web toy (not a port of the Godot game): walk a record store
first-person, pull a record, and wreck the place in the browser. Built on
thriftgod's pointer-lock FP-interior patterns, zero build step (three r0.175 +
@dimforge/rapier3d-compat over an importmap).

- physics.js: Rapier WASM world, one body per prop, support-graph cascade
  (smash a rack → its crates pancake), pre-fractured swap (chunk_* bodies) with
  generative-shard fallback until Lane 2 ships fractured GLBs, capped debris pool.
- props.js: GLTFLoader/DRACOLoader; loads props live from the 3GOD depot
  (/a/<file>, CORS-open) with a local web/assets/ mirror fallback so it never
  hard-fails offline. Read-only against the depot.
- juice.js: camera-trauma screenshake, hitstop, per-material particle bursts and
  procedural Web-Audio smash voices (wood/cardboard/vinyl/glass/steel), combo HUD.
- main.js: FP controller, record-store room, grab/smash raycasting, record ritual
  (pull → held → throw → shatter), "share your mess" canvas screenshot.

Assets mirrored offline: record/crate/rack (from the Godot lane) + cashbot/
council-bin (from 3GOD). Verified in-browser: props load, smashing spawns shards +
particles + combo, rack cascade pancakes its crates, record ritual and share work.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 20:14:32 +10:00

57 lines
2.7 KiB
Markdown

# DESTROYULATOR — web smash toy (Lane 3)
A shareable, **one-click browser toy**: walk a record store first-person, pull a
record, and wreck the place. Not a port of the Godot game — a different product in
the same universe, built on thriftgod's proven FP-interior patterns and wired to the
same [3GOD](https://digalot.fyi/3god) prop depot.
**Zero build step.** Three.js + Rapier load over an importmap, exactly like thriftgod.
## Run it
```sh
cd web && python3 -m http.server 8099
# then open http://localhost:8099
```
Chrome and Safari both work (Safari is the weakest WASM target — it's tested).
Append `?debug` to expose a `window.DESTROY` handle for driving the sim without
Pointer Lock (embedded frames / smoke tests).
## Controls
- **WASD / arrows** — move · **mouse** — look (click to grab pointer lock)
- **click** — smash whatever you're looking at
- **E** (or click) on a record — pull it out of the bin; **click** again to throw it
- **Esc** — pause · **📸 share your mess** — screenshot + smash-count caption
## Architecture
| File | Role |
|---|---|
| `index.html` | Importmap (three r0.175 + `@dimforge/rapier3d-compat`), HUD, boot |
| `main.js` | FP controller, room, prop placement, interaction, record ritual, share, loop |
| `physics.js` | Rapier world, one body per prop, **support-graph cascade**, **pre-fractured swap**, debris pooling |
| `props.js` | GLTFLoader/DRACOLoader; loads props live from 3GOD `/a/<file>` with a `assets/` mirror fallback |
| `juice.js` | Screenshake, hitstop, particle bursts, per-material Web-Audio smash voices, combo counter |
| `assets/` | Offline mirror: `record/crate/rack` (from the Godot lane) + `cashbot/council-bin` (from 3GOD) |
### Shared contracts (see `../LANES/PLAN.md`)
- **Props** load from the 3GOD depot at runtime (`GET /api/list`, GLB at `/a/<slug>`),
falling back to the local `assets/` mirror so the demo never hard-fails offline.
The depot serves `Access-Control-Allow-Origin: *`, so cross-origin loading works.
- **Fracture format** (contract #2): a smashable with a sibling `<name>.fractured.glb`
(root children named `chunk_*`) is swapped in on smash — each chunk becomes a
short-lived dynamic body. Until Lane 2 ships those, brittle props use generative
shards. `props.loadFractured()` already probes for them.
- **Read-only** against the depot. No write calls (the depot's open-mode/SSRF issue
is a write-side concern — see PLAN.md).
## Notes
- Rapier is `-compat` (WASM inlined as base64) so `await RAPIER.init()` needs no extra
fetch and **no COOP/COEP headers**.
- Active physics bodies are capped and settled debris is despawned, so a long smashing
session stays bounded (JS GC is the ceiling).