Files
zeekayandHanzo Dev e50f4c59ee feat: Rapier3D rigid-body sandbox template (Rust->WASM sim, three.js renderer)
Fracture Yard: Rapier owns the simulation, three.js only draws it. Physics state
lives in WASM and is read back once per frame, so the JS side stays a renderer
and the sim stays deterministic. Live HUD reports body count and per-step solver
cost; CCD on the projectile so a fast ball cannot tunnel the tower.

Why this exists: static-SPA builders cannot ship a game. Proving hanzo.app can
meant finding what actually stopped it, and three things did:

  1. POST /v1/projects/:slug/deploy carries the entire build in ONE request body
     and the public edge caps a body at 4 MiB (measured: 4194304 accepted,
     4198400 gets the opaque fasthttp "Error when parsing request"). Godot emits
     a 43 MB engine wasm; Unity 22 MB + 5.7 MB of data. The artifact lane cannot
     express a game at all, so these ship through the release lane instead: stage
     to the object store, then POST /v1/sites/:slug/publish promotes it
     server-side with no body.

  2. Engine loaders emit NON-fingerprinted filenames (index.js, <name>.loader.js)
     and clients/sites.CacheControlFor only marks a content-hashed name
     immutable. A redeploy therefore leaves the CDN and the browser running the
     OLD loader against the NEW wasm. That is not a visible error: emscripten
     prints "still waiting on run dependencies" forever and the canvas stays
     300x150. Every template here fingerprints its own asset names so the case
     cannot recur.

  3. A cross-origin-isolated document (COEP: require-corp) BLOCKS a plain
     cross-origin script, so the shared a.hanzo.ai runtime needs
     crossorigin="anonymous" on exactly the pages that opt into isolation.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-27 23:11:17 -07:00

1.9 KiB

Fracture Yard — Rapier3D physics sandbox

A real rigid-body sandbox: Rapier3D (Rust compiled to WebAssembly) owns the simulation and three.js only draws it. Click to fire a heavy CCD-enabled ball at a 112-block tower, press R to rebuild. The HUD reports live body count and per-step solver cost.

Live: https://rapier-physics.hanzo.app

The split is the lesson: physics state lives in WASM and is read back once per frame, so the JS side stays a renderer and the simulation stays deterministic and fast.

Build

npm install && npm run build

Deploy to hanzo.app

An engine build is far bigger than the 4 MiB the public edge accepts in one request body, so games ship through the release lane, not the artifact lane: put the built tree in your object space, then publish it server-side.

# 1. declare the engine — `unity`, `unreal`, `godot` make clients/sites serve the
#    site cross-origin-isolated (COOP/COEP) so multithreaded WASM gets SharedArrayBuffer
curl -X POST https://api.hanzo.ai/v1/projects -H "Authorization: Bearer $TOKEN" \
  -H "X-Org-Id: $ORG" -H "Content-Type: application/json" \
  -d "{\"slug\":\"my-game\",\"name\":\"my-game\",\"framework\":\"godot\"}"

# 2. stage the build to s3.hanzo.ai under <org>/_stage/my-game/
# 3. promote it into an immutable content-addressed release + flip the pointer
curl -X POST https://api.hanzo.ai/v1/sites/my-game/publish -H "Authorization: Bearer $TOKEN" \
  -H "X-Org-Id: $ORG" -H "Content-Type: application/json" -d "{\"source\":\"_stage/my-game\"}"

Fingerprint your asset names. The site server serves a content-hashed filename immutable and everything else with a plain max-age. A redeploy that reuses the same asset name leaves browsers and the CDN running the OLD loader against the NEW binary — for a WASM app that is a silent, permanent hang with no error, not a visible failure.

License

MIT (this sandbox). Rapier is Apache-2.0, (c) Dimforge. three.js is MIT.