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:

  1. Cross-Origin-Opener-Policy: same-origin (COOP)
    This isolates your document from other origin windows, preventing them from interacting with your page.
  2. 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.