Project
MusicMixer
A private audio studio that converts and edits media entirely in your browser.
Input
Media on your device
Transform
FFmpeg.wasm inside a Web Worker
Output
Local, downloadable files
Processing
100% on-device
Output formats
9
Media uploads
None
MusicMixer began as a Discord bot, escaped into a Flask website, disappeared when its architecture became too expensive and fragile to keep alive, and returned six years later as a private audio studio that runs entirely in the browser.
The feature list changed over time. The useful idea did not: give MusicMixer a piece of media, make a few understandable choices, and get the version you actually wanted.
From bot to browser
- 01In 2020, MusicMixer launched as a Discord bot that could play, modify, and download audio.
- 02The bot became a Flask website: upload a file, let Python run FFmpeg or yt-dlp, then download the result.
- 03Unreliable downloads, paid-hosting pressure, and constant server-side file handling made that version unsustainable.
- 04In 2026, I rebuilt the workflow as a static, installable web app with FFmpeg running locally in the browser.
The original bot grew out of Taco Bot, my first project with real users. It could change pitch and speed, boost bass, and turn a normal song into something ungodly. Under the silliness was an instinct I still follow: take a capable tool that normally lives in a terminal, put a friendlier face on it, and let people make something.
Moving to Flask solved Discord-specific friction, but introduced a more important systems problem. Every job sent media to a server, launched FFmpeg or yt-dlp through a subprocess, wrote temporary files, and depended on cleanup working correctly. YouTube downloads broke unpredictably, and PythonAnywhere eventually restricted the yt-dlp calls the product relied on. Maintaining the feature became a negotiation with hosting, storage, and a moving external platform.
The rewrite
Today's MusicMixer keeps the workflow and replaces the architecture. It can:
- convert among MP3, WAV, FLAC, AAC, OGG, Opus, M4A, WebM, and MP4;
- extract audio, trim clips, split by markers or equal intervals, and merge tracks in order;
- change pitch, speed, bass, treble, gain, sample rate, bitrate, and channels;
- normalize loudness, add fades, clean up voice recordings, compress dynamics, add echo, and reverse a track; and
- run a sequential batch queue with cancellation, recovery, individual output downloads, and ZIP export.
How local processing works
- 01The user selects local audio or video files and chooses an operation or preset.
- 02MusicMixer stores the media in the browser's Origin Private File System and the job metadata in IndexedDB.
- 03A dedicated Web Worker compiles the recipe into a deterministic FFmpeg command plan.
- 04FFmpeg WebAssembly processes each queued job without blocking the interface or uploading the media.
- 05MusicMixer returns one or more downloadable outputs and preserves interrupted jobs as restartable work.
The worker uses multithreaded FFmpeg when the browser supports cross-origin isolation, with a single-thread fallback for compatibility. A service worker caches the application shell and version-pinned FFmpeg engine, so active jobs can continue through a network interruption and the installed app can work offline.
NoteWhy a browser audio studio still needs limits
Browser processing trades server exposure for local constraints. WebAssembly is slower than native FFmpeg, browsers can suspend a closed tab, and available memory and storage vary by device. MusicMixer rejects files at the documented 2 GB WebAssembly boundary and warns before workflows with high estimated working storage or an expensive video transcode.
Those limits are surfaced in the product instead of hidden behind a spinner.
What I deliberately left out
The hosted app does not import media from YouTube. yt-dlp needs a native runtime, and downloading media carries real permission and platform-rule questions. If that feature returns, it belongs in an optional desktop edition that runs locally and asks the user to confirm they are authorized to download the media.
That decision captures the difference between the first MusicMixer and this one. The original tried to promise everything. The rewrite is more useful because its trust boundary is explicit.
What I take from it
MusicMixer taught me Python, APIs, asynchronous code, deployment, FFmpeg, and what happens when software leaves your laptop. Rebuilding it taught me a more subtle lesson: preserving the best part of an old product does not mean preserving its architecture.
Start with the user promise. Keep that. Everything underneath it is allowed to grow up.
The studio is live at musicmixer.shauryav.com, and the source is available on GitHub.
Related work