Heuristic:Tensorflow Tfjs WASM Cross Origin Isolation
| Metadata | |
|---|---|
| Source | Repo, Doc |
| Domains | Optimization, WASM |
| Date | 2026-02-10 |
Overview
Configure Cross-Origin Isolation HTTP headers to enable WASM multi-threading support, which provides significant performance improvements (up to 3-10x faster).
Description
Starting from Chrome 92, SharedArrayBuffer (required for WASM multi-threading) is only available when the page is cross-origin isolated. Without proper headers, the WASM backend falls back to SIMD-only or vanilla binary, losing multi-threading performance gains.
Usage
Use this heuristic when deploying the WASM backend in production and multi-threading performance is desired. Check if tfjs-backend-wasm-threaded-simd.wasm is loaded in the browser Network tab to verify threading is active.
The Insight
- Action: Set two HTTP headers on the main document:
- Cross-Origin-Opener-Policy: same-origin
- Cross-Origin-Embedder-Policy: require-corp
- Value: Enables multi-threading in WASM backend (e.g., MobileNet V2 goes from 34.6ms SIMD-only to 12.4ms SIMD+threads on desktop)
- Trade-off: Cross-origin resources must also be served with appropriate CORS/CORP headers. May require infrastructure changes.
- Additional: For WASM files from third-party servers, use CORS (Access-Control-Allow-Origin: * with crossorigin attribute) or CORP header
- Thread control: Use setThreadsCount() before tf.setBackend('wasm') to control thread pool size (defaults to logical CPU cores)
Reasoning
From tfjs-backend-wasm/README.md: The backend auto-detects SIMD and multi-threading support. Three WASM binaries exist:
| Binary | Features | Use Case |
|---|---|---|
| tfjs-backend-wasm.wasm | Vanilla (no SIMD, no threads) | Fallback for older browsers |
| tfjs-backend-wasm-simd.wasm | SIMD only | Browsers without SharedArrayBuffer |
| tfjs-backend-wasm-threaded-simd.wasm | SIMD + multi-threading | Full performance (requires cross-origin isolation) |
From flags: WASM_HAS_MULTITHREAD_SUPPORT tests SharedArrayBuffer transferability via MessageChannel.
Code Evidence
From tfjs-backend-wasm/src/flags_wasm.ts:44-64
ENV.registerFlag('WASM_HAS_MULTITHREAD_SUPPORT', async () => {
if (ENV.get('IS_NODE')) {
return false;
}
try {
// Test if SharedArrayBuffer can be transferred via MessageChannel
new MessageChannel().port1.postMessage(new SharedArrayBuffer(1));
return WebAssembly.validate(new Uint8Array([...]));
} catch (e) {
return false;
}
});
Server Configuration (Express.js)
// Enable cross-origin isolation for WASM multi-threading
app.use((req, res, next) => {
res.setHeader('Cross-Origin-Opener-Policy', 'same-origin');
res.setHeader('Cross-Origin-Embedder-Policy', 'require-corp');
next();
});
Server Configuration (Nginx)
# nginx.conf
add_header Cross-Origin-Opener-Policy "same-origin";
add_header Cross-Origin-Embedder-Policy "require-corp";
Thread Count Configuration
import {setThreadsCount} from '@tensorflow/tfjs-backend-wasm';
// Set thread count BEFORE initializing the backend
setThreadsCount(4);
await tf.setBackend('wasm');
await tf.ready();
JS Minification Warning
When using terser or similar minifiers, turn off the "typeofs" optimization. This transform changes typeof foo == "undefined" into foo === void 0, which causes SIMD+threading workers to throw "_scriptDir is not defined" at runtime.
// terser configuration
{
compress: {
typeofs: false // CRITICAL: Disable for WASM threading compatibility
}
}