Cross-Origin Isolation for Ammo.js SharedArrayBuffer
To use the multi-threaded version of Ammo.js (a WebAssembly port of
the Bullet physics engine), browsers require the use of
SharedArrayBuffer. Because of security vulnerabilities like
Spectre, browsers restrict SharedArrayBuffer unless the web
page is running in a Cross-Origin Isolated environment. This article
outlines the specific HTTP headers and configuration steps required to
enable Cross-Origin Isolation and run Ammo.js physics simulations
successfully.
Why Cross-Origin Isolation is Required
Ammo.js utilizes web workers to run physics calculations on separate
threads. To share physics state data between the main thread and these
worker threads efficiently, it relies on
SharedArrayBuffer.
Without proper security headers, browsers will block the
instantiation of SharedArrayBuffer, resulting in console
errors such as
ReferenceError: SharedArrayBuffer is not defined. To unlock
this API, you must prove to the browser that your site does not load
untrusted cross-origin resources in the same browsing context.
Step 1: Configure Server Headers
To enable Cross-Origin Isolation, your web server must deliver the main HTML document with two specific HTTP headers:
Cross-Origin-Opener-Policy: same-origin(COOP)
This isolates your document from other origin windows, preventing them from interacting with your page.Cross-Origin-Embedder-Policy: require-corp(COEP)
This prevents your document from loading cross-origin resources (like scripts, images, or stylesheets) that have not explicitly granted permission via CORS or Cross-Origin Resource Policy (CORP).
Example Server Configurations
Apache (.htaccess)
Header set Cross-Origin-Opener-Policy "same-origin"
Header set Cross-Origin-Embedder-Policy "require-corp"Nginx (nginx.conf)
add_header Cross-Origin-Opener-Policy "same-origin";
add_header Cross-Origin-Embedder-Policy "require-corp";
Node.js Express
app.use((req, res, next) => {
res.setHeader("Cross-Origin-Opener-Policy", "same-origin");
res.setHeader("Cross-Origin-Embedder-Policy", "require-corp");
next();
});Step 2: Handle Third-Party and CDN Resources
Once COEP (require-corp) is active, any script, image,
or WebAssembly file loaded from an external domain will be blocked
unless that external server responds with proper headers.
If you load Ammo.js or other libraries from a CDN, ensure the CDN
response includes: *
Cross-Origin-Resource-Policy: cross-origin
or * Access-Control-Allow-Origin: *
In your HTML, you must also add the crossorigin
attribute to any external tags:
<script src="https://cdn.example.com/ammo.wasm.js" crossorigin="anonymous"></script>Step 3: Verify Cross-Origin Isolation
You can verify if your environment is successfully isolated by
checking the global crossOriginIsolated property in your
browser’s developer console:
if (self.crossOriginIsolated) {
console.log("Cross-Origin Isolation is active. SharedArrayBuffer is available.");
} else {
console.warn("Cross-Origin Isolation is NOT active. Ammo.js multi-threading will fail.");
}Alternative: Service Worker Workaround
If you host your project on environments where you cannot configure server headers (such as GitHub Pages or Netlify’s free tier), you can use a Service Worker workaround.
A local service worker can intercept network requests and append the
COOP and COEP headers to your documents dynamically. Tools like
coi-serviceworker can be integrated into your project to
automate this process in environments with restricted server
control.