DEV Community

Tasleem Akhtar (#Ch)
Tasleem Akhtar (#Ch)

Posted on Originally published at toolfyra.com

Shipping ffmpeg.wasm 0.12 in production without SharedArrayBuffer: every dead end, mapped

I spent a day fighting ffmpeg.wasm so you don't have to. The goal was boring: compress a video inside the browser tab, no uploads, no server bill. The journey was not.

Dead end #1 — ffmpeg.wasm 0.11. It works, until you deploy. The 0.11 core needs SharedArrayBuffer, which needs site-wide COOP/COEP headers. If your site embeds anything third-party (ads, analytics iframes, embeds), those headers silently break them. One video tool nuking your whole site's embeds is a bad trade.

Dead end #2 — cross-origin workers from a CDN. "Just load the worker from unpkg" — no. new Worker('https://unpkg.com/...') throws a SecurityError from any http origin. And the classURL blob workaround people suggest? 0.12.10 ignores it. I tried both, same afternoon.

Dead end #3 — the UMD core in a module worker. This one is sneaky. The 0.12 worker is a module worker: it tries importScripts() (undefined in modules → throws), then falls back to await import(coreURL) and reads .default. A UMD core has no default export, so you get failed to import ffmpeg-core.js with zero useful detail. The fix is one URL: serve the ESM core build, not UMD.

What actually shipped:

  1. Vendor the ESM build same-origin: /assets/vendor/ffmpeg/ (worker, classes, const, errors, utils + esm/ffmpeg-core.js + wasm).
  2. load({ coreURL: '/assets/vendor/ffmpeg/esm/ffmpeg-core.js', wasmURL: '.../esm/ffmpeg-core.wasm' }).
  3. No COOP/COEP. No CDN workers. No blobs.

Total cost: 35 MB of static assets you serve once per user (aggressively cacheable), and every conversion happens on-device. Zero uploads, zero server CPU, zero retention questions.

If you want to see it working rather than read about it: I run the same engine in a browser video compressor (https://toolfyra.com/video-compressor.html), a format converter (https://toolfyra.com/video-converter.html) and an audio converter (https://toolfyra.com/audio-converter.html) — drag a file in, conversion runs in your tab, and nothing ever leaves your machine.

One last gotcha for the road: ESM import() of your vendored files resolves relative to the worker's URL. Keep the vendor directory flat and same-origin and you'll never think about it again.

Top comments (0)