Compress, convert, trim, crop, resize, merge, rotate, reverse, speed change, screenshot, mute, video-to-GIF, audio extract/trim/convert/merge/speed, and media info.
The interesting technical problem: FFmpeg compiled to WASM works, but it tops out around 4 GB, runs single-threaded, and is 10x slower than native browser APIs for the same operations. I wanted to handle 50 GB files and actually use the hardware (theoretically there's no limit at all).
So I built a hybrid engine:
- Most tools use WebCodecs (via MediaBunny) — the browser's native hardware-accelerated video encoder/decoder. H.264, HEVC, VP9, AV1 with GPU acceleration when available. This is what makes it fast. - Three tools (speed, merge, video-to-GIF) still use FFmpeg WASM where WebCodecs can't do the job. - The engine auto-detects which path to use and falls back gracefully — if hardware encoding fails, it retries in software; if WebCodecs can't handle a codec, it routes to FFmpeg.
How 50 GB works in a browser:
Files are streamed through OPFS (Origin Private File System) with chunked I/O. Small files (<256 MB) stay in memory. Larger files write to OPFS-backed storage. The biggest files use StreamTarget for chunked streaming so you never buffer the entire output in RAM. Stale temp files auto-clean after 6 hours, and quota-exceeded errors trigger aggressive cleanup mid-operation.
Other details HN might find interesting:
- Processing runs in a Web Worker with stall detection (180s timeout). If the worker crashes, it falls back to main thread automatically. - Audio extraction with stream copy (no re-encoding) skips disk I/O entirely — zero-copy transmux via BufferTarget. - Codec extensions (MP3, FLAC, AAC, AC3 encoders as WASM) are loaded on demand but can't run in the Worker — esbuild corrupts their inline WASM binaries during double-bundling, so they route to main thread. - 30+ formats, 5.1/7.1 surround audio preserved, 19 languages.
There's a /benchmark page if you want to compare the WebCodecs path vs FFmpeg WASM directly with audio extraction.
Built with Next.js 15, React 19, TypeScript, Tailwind v4, Zustand.
Would love feedback — especially on formats/codecs you'd want supported, or edge cases where processing fails.