Rip an audio CD to FLAC and MP3 320, tag it from MusicBrainz, organise it, and play it — entirely in the browser.
There is no server. The disc is read through the File System Access API, FLAC and MP3 encoding run as WebAssembly (libFLAC and LAME) on your own machine, and the library index lives in IndexedDB. Nothing is uploaded: the only network calls are metadata lookups to MusicBrainz and cover art from the Cover Art Archive.
- Chrome, Edge, or another Chromium browser. Safari and Firefox do not implement the File System Access API, which the whole app is built on.
- An HTTPS origin, or
localhost.showDirectoryPickeronly exists in a secure context. Openingindex.htmlfromfile://will not work — the page gets an opaque origin and cannot keep the folder permissions you grant.
./run.sh # build if needed, then serve on http://127.0.0.1:8765
./run.sh --build # force a rebuild firstThen:
- Add folder… in the Library tab, and point it at your music.
- Insert a CD, press Choose disc…, and pick it under Locations.
A library folder is anything containing audio. The scanner walks up to six
levels deep and treats any directory holding audio files as an album, reading
tags from Vorbis comments (FLAC) or ID3 (MP3). It also recognises its own rip
layout — FLAC/<artist>/<album> with a mirror under MP3/ — and pairs the two
copies rather than indexing the album twice.
Several folders can be in the library at once. Each is scanned independently, so rescanning one never disturbs another, and removing one only drops it from the index — files on disk are never touched. The folder marked rips here is where new rips are written.
Chrome drops folder permission on every reload, so a folder may show needs permission until you reconnect it. Pressing play reconnects automatically, since a permission prompt is only allowed during a click.
The index is only an index. Every field it holds is read back out of the files'
own tags or rip.log, so losing IndexedDB costs nothing but a rescan.
frontend/ the app — React, Vite, no backend
src/lib/ disc reading, encoding, tagging, MusicBrainz, the store
backend/ the original Python/FastAPI implementation, kept for its tests
tools/ make_fixture_disc.py — builds a synthetic audio CD for testing
The Python backend predates the move into the browser and is no longer used at runtime; its test suite still covers the disc-ID, TOC and naming logic that the JavaScript ports from.
tools/make_fixture_disc.py builds a directory that is byte-compatible with a
mounted audio CD — a real MusicBrainz TOC with generated tones for audio — so
disc identification, tagging and organisation all exercise the genuine code path.
python tools/make_fixture_disc.py --search "Radiohead OK Computer"Then pick fixtures/Audio CD as the disc.
Some discs open to "this disc's tracks can't be listed". The audio is there and
Chrome is hiding it: the File System Access API only enumerates files whose
names are safe as portable path components, so a directory listing silently
skips anything containing < > : " / \ | ? * — the characters Windows forbids —
and asking for one by name is refused for the same reason.
macOS takes track names from a disc's CD-TEXT when it has any, so a classical
release mounts as 1 Martinů: Symphony No. 1 - I. Moderato.aiff. That single
colon makes every track invisible, while the .TOC.plist beside them, having a
clean name, reads perfectly — which is why such a disc appears to have a table
of contents and no audio.
That filter applies to names the page supplies. A file the user picks in the system dialog arrives by a different route and keeps its real name, so the app offers Select the tracks…: choose all of them (Cmd + A) and ripping proceeds normally. Track numbers come from the leading digits of each filename, which is what the TOC indexes by. The choice is remembered, so a reload does not ask again.
If that ever fails, tools/stage-disc.sh copies the disc under the neutral names
macOS uses when there is no CD-TEXT:
./tools/stage-disc.sh # auto-detects the mounted CD
./tools/stage-disc.sh --dry-run # show the renaming firstThen press Choose disc… and pick the staged folder, which identifies and rips exactly as the disc would — at the cost of a few minutes and a duplicate of the whole disc on disk.