Consume Lane 2's props + close the review gaps in the browser toy. props.js: all 16 depot slugs added to CATALOG (material/kind per prop; printer is the boss). loadFractured() now (a) caches its result per id — including the null miss — so a prop with no fractured sibling stops re-fetching + 404-ing on EVERY spawn (the record. fractured.glb spam), and (b) returns the real chunk container (the fractured_root wrapper Blender exports), so physics.js's direct-children chunk swap actually fires. main.js: loadHeroProps() places the printer BOSS + office set dressing (desk, crt-monitor, filing-cabinet, water-cooler) with their fractured templates. The boss soaks 4 hits then bursts into its 16 GLB chunks (kept non-shatterable so a topple can't break it early). Pointer-lock fallback (shareability fix): if the lock request is refused — iframes/embedded hosts, where the toy used to dead-end on the title screen — we fall back to DRAG-TO-LOOK (hold-drag rotates, a tap still smashes) so the shareable link always plays. Assets: office-printer / crt-monitor / filing-cabinet mirrored into web/assets/ (offline fallback); record/crate/rack.fractured.glb generated + mirrored so the record ritual smashes into real chunks. The three store fractured GLBs were also pushed to the 3GOD shelf. Verified in-browser via ?debug: printer aimSmash→"printer" then 16 chunks; thrown record → 8 chunks; debris drains 16→0; record.fractured.glb serves 200 (no 404); drag-look rotates the camera when pointer lock is unavailable. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|---|---|---|
| .. | ||
| assets | ||
| index.html | ||
| juice.js | ||
| main.js | ||
| physics.js | ||
| props.js | ||
| README.md | ||
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 prop depot.
Zero build step. Three.js + Rapier load over an importmap, exactly like thriftgod.
Run it
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 localassets/mirror so the demo never hard-fails offline. The depot servesAccess-Control-Allow-Origin: *, so cross-origin loading works. - Fracture format (contract #2): a smashable with a sibling
<name>.fractured.glb(root children namedchunk_*) 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) soawait 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).