DEV Community

Convertola
Convertola

Posted on

Converting video and audio to MP3 in the browser with ffmpeg.wasm: notes from building one

Most "convert to MP3" websites follow the same pattern: you upload a file, a server runs FFmpeg on it, and you download the result. That works, but it means every recording, voice note or home video takes a trip to someone else's machine. While building Convertola (disclosure: it's our project), we went the other way and ran FFmpeg inside the browser with WebAssembly. Here are the parts that turned out to matter.

1. Pick the single-threaded core unless you really need threads

ffmpeg.wasm ships a single-threaded core and a multi-threaded one. The multi-threaded build depends on SharedArrayBuffer, which browsers only expose on cross-origin isolated pages. That means serving Cross-Origin-Opener-Policy: same-origin and Cross-Origin-Embedder-Policy: require-corp, and those headers can break third-party embeds, fonts or analytics that aren't served with the right CORP/CORS headers.

For audio-only jobs the single-threaded core is usually fast enough, and it runs on a plain page with no special headers. We chose it and haven't regretted it.

2. Load the engine lazily

The core .wasm file is several megabytes. Loading it on page load slows down everyone, including visitors who only came to read. Load it when the user actually picks a file (or hovers the upload area), and show a clear "preparing converter" state so the first conversion doesn't look frozen.

3. The FFmpeg command is the easy part

Once a file is written to ffmpeg.wasm's in-memory file system, the conversion is the same command you'd run on a desktop:

ffmpeg -i input.mp4 -vn -c:a libmp3lame -b:a 192k output.mp3
Enter fullscreen mode Exit fullscreen mode
  • -vn drops the video stream, so a video becomes an audio-only MP3.
  • -c:a libmp3lame selects the MP3 encoder.
  • -b:a sets the bitrate. Offering a small set of choices (128, 192, 256, 320 kbps) is friendlier than a free-form field.

One gotcha: files with several audio tracks (common in MKV) will use FFmpeg's default stream selection. If you want predictable behaviour, map the first audio track explicitly with -map 0:a:0.

4. Memory is your real limit

Everything lives in the tab's memory: the input file, FFmpeg's working buffers and the output. Large inputs can crash a tab, especially on phones. Set an honest file-size cap, check file.size before writing anything to the virtual file system, and delete files from it (deleteFile) after each job so repeated conversions don't pile up.

5. Progress and cancel matter more than raw speed

Users tolerate a conversion that takes a while if they can see it moving. ffmpeg.wasm emits progress events you can bind to a progress bar. Equally important is a working Cancel button: terminating the instance and reloading it is simpler and more reliable than trying to stop a running job halfway.

6. Be upfront about licensing

FFmpeg and its builds carry GPL/LGPL obligations depending on how they're compiled. Credit FFmpeg and ffmpeg.wasm on the page, state the license of the core you ship, and link to the source.

7. The privacy story is the product

The biggest benefit isn't technical. When the file never leaves the device, there's nothing to delete later and nothing to explain in a retention policy. That's an easy promise to keep, and an easy one for users to understand.

If you want to see how this feels in practice, Convertola's free MP3 converter is built exactly this way: pick a video or audio file, choose a bitrate, and the MP3 is created on your own device.

Have you shipped ffmpeg.wasm in production? I'd be curious how others handle large files on mobile.

Top comments (0)