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:
- Vendor the ESM build same-origin:
/assets/vendor/ffmpeg/(worker, classes, const, errors, utils +esm/ffmpeg-core.js+ wasm). -
load({ coreURL: '/assets/vendor/ffmpeg/esm/ffmpeg-core.js', wasmURL: '.../esm/ffmpeg-core.wasm' }). - 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)