When you need it
Godot threaded exports, Emscripten builds with pthreads and other code that uses SharedArrayBuffer need a cross-origin isolated page. Most games don't: start with a single-threaded export and enable threads only when your game needs them.
How PEERLESS provides it
Mark the build Threaded in the upload form. The game's origin is served with:
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
Cross-Origin-Resource-Policy: cross-originIsolation also requires the page around the game to be isolated. Store pages are not (they show trailers and creator media), so threaded games open in the full player page at /play/<game>, which is. Players press Play on the store page and land in the player.
What it means for your assets
Under require-corp, every cross-origin resource your game loads must opt in:
- Send
Cross-Origin-Resource-Policy: cross-originfrom your asset or API host, or - Serve it with CORS (
Access-Control-Allow-Origin) and load it withcrossorigin.
Files inside your build need nothing extra.
Test locally
Serve your export with the same headers to reproduce the hosted environment. Check crossOriginIsolated in the console; it must be true.
import { createServer } from "node:http";
import { readFile } from "node:fs/promises";
import { extname, join } from "node:path";
const types = { ".html": "text/html", ".js": "text/javascript", ".wasm": "application/wasm", ".pck": "application/octet-stream" };
createServer(async (request, response) => {
const path = join("build", decodeURIComponent(new URL(request.url, "http://x").pathname.replace(/\/$/, "/index.html")));
try {
const body = await readFile(path);
response.writeHead(200, {
"Content-Type": types[extname(path)] || "application/octet-stream",
"Cross-Origin-Opener-Policy": "same-origin",
"Cross-Origin-Embedder-Policy": "require-corp",
});
response.end(body);
} catch {
response.writeHead(404).end();
}
}).listen(8080);