A real Unity WebGL build served with COOP/COEP, so crossOriginIsolated === true
and SharedArrayBuffer is available — the gate a multithreaded Unity build needs.
Declaring framework=unity on the project is what turns those headers on
(clients/projects.crossOriginIsolated), plus the application/wasm and
application/octet-stream MIME types the Unity loader requires.
Build payload is the example Unity WebGL build from Marinerer/unity-webgl
(Apache-2.0), renamed with a content hash; no Unity licence is needed to use or
swap it.
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]>
2.6 KiB
Unity WebGL on hanzo.app
A real Unity WebGL build served cross-origin-isolated, so crossOriginIsolated === true
and SharedArrayBuffer is available — the gate a multithreaded Unity build needs.
Live: https://unity-webgl.hanzo.app
Declare "framework": "unity" on the project and clients/sites serves
Cross-Origin-Opener-Policy: same-origin + Cross-Origin-Embedder-Policy: require-corp on
the document and Cross-Origin-Resource-Policy: same-origin on every asset, plus the
application/wasm / application/octet-stream MIME types the Unity loader requires for
.wasm and .data. Third-party scripts on such a page must carry
crossorigin="anonymous" or COEP blocks them.
Swap in your own build
Unity's build output is <name>.loader.js, <name>.framework.js, <name>.data,
<name>.wasm. Rename them with a content hash (reef-<sha8>.* here) and point the BUILD
constant in index.html at the new stem — a non-fingerprinted name lets a redeploy serve
the old loader against the new wasm, which hangs silently.
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 and attribution
Apache-2.0. The Build/ and TemplateData/ payloads are the example Unity WebGL build
from Marinerer/unity-webgl (Apache-2.0),
renamed with a content hash. Unity is a trademark of Unity Technologies.