|
| 1 | +--- |
| 2 | +layout: post |
| 3 | +title: File Converter with FFmpeg WebAssembly |
| 4 | +date: 2026-08-20 22:12:02 |
| 5 | +excerpt: How I built a browser-based file converter that converts media files client-side using FFmpeg WebAssembly. |
| 6 | +categories: ffmpeg wasm react javascript site browser |
| 7 | +--- |
| 8 | + |
| 9 | +How I built a [File Converter](https://remarkablemark.org/file-converter/) that converts media files in the browser. It runs [FFmpeg](https://ffmpeg.org/) compiled to WebAssembly via [@ffmpeg/ffmpeg](https://github.com/ffmpegwasm/ffmpeg.wasm). |
| 10 | + |
| 11 | +## Motivation |
| 12 | + |
| 13 | +Traditional file converters send files to a server. That raises privacy concerns, requires bandwidth for large files, and costs money to host — which is why many existing services are ad-supported. With FFmpeg.wasm, all processing happens client-side in the browser. |
| 14 | + |
| 15 | +## File Types |
| 16 | + |
| 17 | +The file converter supports the file types: |
| 18 | + |
| 19 | +| Category | Inputs | Outputs | |
| 20 | +| -------- | ----------------------------------- | ------------------------------ | |
| 21 | +| Image | PNG, JPEG, WebP, GIF, BMP, ICO, SVG | PNG, JPEG, WebP, GIF, BMP, ICO | |
| 22 | +| Video | MP4, WebM, MOV, AVI, MKV, GIF | MP4, WebM, MOV, AVI, MKV, GIF | |
| 23 | +| Audio | MP3, WAV, OGG, AAC, FLAC, M4A | MP3, WAV, OGG, AAC, FLAC, M4A | |
| 24 | + |
| 25 | +SVG inputs are rasterized to the chosen bitmap format. |
| 26 | + |
| 27 | +## How It Works |
| 28 | + |
| 29 | +1. **Upload a file** — drag-and-drop or click to upload. It auto-detects the category (image, video, audio) from the MIME type and file extension. |
| 30 | +2. **Select a format** — only compatible output formats are shown for the detected category. |
| 31 | +3. **Controls** — an advanced options panel appears based on the category: |
| 32 | + - **Image**: dimensions, fit mode (contain, cover, stretch, force), quality, transparency preservation, background color. |
| 33 | + - **Video**: dimensions, fit mode, frame rate, CRF quality, audio preservation with bitrate, GIF loop toggle. |
| 34 | + - **Audio**: bitrate, sample rate, channels (mono/stereo). |
| 35 | +4. **Convert** — FFmpeg.wasm processes the file and a progress bar tracks the transcode. |
| 36 | +5. **Download** — the converted file is ready to download with an editable filename. |
| 37 | + |
| 38 | +## Technical Decisions |
| 39 | + |
| 40 | +### Lazy-Loading FFmpeg |
| 41 | + |
| 42 | +FFmpeg core and WASM are loaded from the [jsDelivr](https://cdn.jsdelivr.net) CDN only after the user clicks "Convert". This keeps the initial page load fast since the ~30 MB WASM payload loads on demand, not on page render. The assets are pinned at the specific URLs: |
| 43 | + |
| 44 | +- [https://cdn.jsdelivr.net/npm/@ffmpeg/core@0.12.10/dist/esm/ffmpeg-core.js](https://cdn.jsdelivr.net/npm/@ffmpeg/core@0.12.10/dist/esm/ffmpeg-core.js) |
| 45 | +- [https://cdn.jsdelivr.net/npm/@ffmpeg/core@0.12.10/dist/esm/ffmpeg-core.wasm](https://cdn.jsdelivr.net/npm/@ffmpeg/core@0.12.10/dist/esm/ffmpeg-core.wasm) |
| 46 | + |
| 47 | +Local serving via a Vite plugin was attempted but reverted since Vite intercepts the dynamic `import()` calls inside the FFmpeg worker, which breaks loading. |
| 48 | + |
| 49 | +### Conversion Flow |
| 50 | + |
| 51 | +FFmpeg.wasm runs in a Web Worker with its own virtual file system. The conversion pipeline is: |
| 52 | + |
| 53 | +1. Write the input file to the virtual FS with `ffmpeg.writeFile()` |
| 54 | +2. Execute the FFmpeg command with `ffmpeg.exec()` — args are built from the selected format and options |
| 55 | +3. Read the output file from the virtual FS with `ffmpeg.readFile()` |
| 56 | +4. Wrap the result in a `Blob` and generate a download link with `URL.createObjectURL()` |
| 57 | + |
| 58 | +### MP4 to WebM |
| 59 | + |
| 60 | +The default VP9 encoder hangs in the single-threaded ffmpeg.wasm build. Switching to VP8 with `-deadline good -cpu-used 5` fixes it. |
| 61 | + |
| 62 | +### Large File Limitation |
| 63 | + |
| 64 | +Since FFmpeg.wasm runs entirely in memory, very large files can fail or freeze the tab. The app warns when a file exceeds 100 MB but conversions are not blocked. |
| 65 | + |
| 66 | +## Try It |
| 67 | + |
| 68 | +You can try the [File Converter](https://remarkablemark.org/file-converter/) or check out the [source code](https://github.com/remarkablemark/file-converter). |
0 commit comments