Why I Built This
Online media converters are either riddled with ads, require uploads to sketchy servers, or cap you at a handful of free conversions. I wanted a tool that runs entirely in the browser — no server uploads, no file size limits beyond what your machine can handle, and no dark patterns.
Building it also gave me an excuse to explore WebAssembly in a real-world context. Running FFmpeg in the browser is a fundamentally different challenge than calling it on a server.

How It Works
You drop a file onto the page, choose your target format, and hit convert. The processing happens entirely client-side using FFmpeg.wasm — a WebAssembly build of FFmpeg that runs in the browser's sandbox. No file ever leaves your machine.
The app supports all major format conversions: MP4 to MP3, WebP to PNG, WAV to OGG, and dozens more. The UI shows real-time progress during conversion and provides a download link when complete.
The frontend is a Next.js app styled with Tailwind CSS and Shadcn UI. Motion.dev handles the transition animations between upload, processing, and download states.
Key Decisions
Client-side processing over server-side
The entire value proposition is privacy and speed. Uploading a 500MB video to a server, waiting for conversion, and downloading the result is slow and requires trust. Running FFmpeg locally eliminates both problems. The tradeoff is that conversion speed depends on the user's hardware, but for most common operations it's fast enough.
FFmpeg.wasm and its constraints
The WebAssembly build of FFmpeg is powerful but has real limitations. Memory is constrained by the browser's sandbox, multi-threading support varies across browsers, and not every FFmpeg codec is available in the WASM build. I had to test extensively across browsers and document which conversions work reliably.
Minimal UI for a complex operation
Media conversion has a lot of possible options — bitrate, codec, resolution, audio channels. I deliberately kept the interface simple: pick input, pick output, convert. Advanced users can dig into the repo, but for the 90% use case, the defaults work well.
What I Learned
WebAssembly memory management is manual. Unlike server-side FFmpeg where the OS handles cleanup, the WASM version requires explicit memory allocation and deallocation. Failing to free buffers after conversion leads to crashes on large files.
Browser APIs vary more than you'd expect. SharedArrayBuffer (needed for multi-threaded FFmpeg) requires specific CORS headers and isn't available in all contexts. I had to implement fallbacks for single-threaded conversion in environments that don't support it.
Simple UIs are harder to design. Hiding complexity behind a clean interface requires more design thinking than exposing every option. Choosing the right defaults and knowing what to omit was the real challenge.