How to Load Multi-Threaded FFmpeg.wasm with COOP/COEP

This article provides a direct guide on running the multi-threaded version of ffmpeg.wasm in the browser. To utilize multi-threading, modern web browsers require the use of SharedArrayBuffer, which is disabled by default for security reasons. Below, you will learn why Cross-Origin Opener Policy (COOP) and Cross-Origin Embedder Policy (COEP) headers are mandatory, how to configure these headers across common server environments, and how to write the JavaScript code to initialize the multi-threaded FFmpeg core.

Step 1: Configure COOP and COEP Headers

Multi-threading in WebAssembly relies on web workers sharing memory via SharedArrayBuffer. Browsers only expose SharedArrayBuffer in a cross-origin isolated context. To enable this context, your web server must deliver the following HTTP response headers for the top-level document and all associated assets:

Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp

Node.js (Express)

const express = require('express');
const app = express();

app.use((req, res, next) => {
  res.setHeader('Cross-Origin-Opener-Policy', 'same-origin');
  res.setHeader('Cross-Origin-Embedder-Policy', 'require-corp');
  next();
});

app.use(express.static('public'));
app.listen(3000);

Nginx

server {
    listen 80;
    server_name localhost;

    location / {
        root /path/to/dist;
        try_files $uri $uri/ =404;
        add_header Cross-Origin-Opener-Policy "same-origin";
        add_header Cross-Origin-Embedder-Policy "require-corp";
    }
}

Vite Dev Server (vite.config.js)

import { defineConfig } from 'vite';

export default defineConfig({
  server: {
    headers: {
      'Cross-Origin-Opener-Policy': 'same-origin',
      'Cross-Origin-Embedder-Policy': 'require-corp',
    },
  },
});

Step 2: Install Required Dependencies

Ensure you have @ffmpeg/ffmpeg and the multi-threaded core @ffmpeg/core-mt installed along with @ffmpeg/util for loading assets as Blob URLs.

npm install @ffmpeg/ffmpeg @ffmpeg/core-mt @ffmpeg/util

Step 3: Initialize the Multi-Threaded FFmpeg Instance

Import the dependencies and load the multi-threaded core using toBlobURL to avoid cross-origin and worker-loading errors:

import { FFmpeg } from '@ffmpeg/ffmpeg';
import { toBlobURL } from '@ffmpeg/util';

const ffmpeg = new FFmpeg();

async function loadMultiThreadedFFmpeg() {
  // Verify isolation before attempting to load
  if (!crossOriginIsolated) {
    throw new Error('COOP/COEP headers are not configured properly. crossOriginIsolated is false.');
  }

  // Define the base URL for the multi-threaded core assets
  const baseURL = 'https://unpkg.com/@ffmpeg/core-mt@0.12.6/dist/esm';

  // Load the core, wasm, and worker scripts via Blob URLs
  await ffmpeg.load({
    coreURL: await toBlobURL(`${baseURL}/ffmpeg-core.js`, 'text/javascript'),
    wasmURL: await toBlobURL(`${baseURL}/ffmpeg-core.wasm`, 'application/wasm'),
    workerURL: await toBlobURL(`${baseURL}/ffmpeg-core.worker.js`, 'text/javascript'),
  });

  console.log('Multi-threaded ffmpeg.wasm loaded successfully!');
}

loadMultiThreadedFFmpeg();

Step 4: Verify Multi-Threading

Open your browser's Developer Tools and verify the setup:

  1. In the Console, type window.crossOriginIsolated and ensure it returns true.
  2. Inspect the Network or Sources tab during FFmpeg execution; you will observe multiple Web Workers being instantiated, executing tasks concurrently across available CPU threads.