Describe a song in plain words — instruments, mood, tempo, era, where you heard it — and get its name.
A community index of how songs sound. Nobody uploads audio: your browser reads your files, identifies each one by acoustic fingerprint (AcoustID + MusicBrainz), and boils it down to a 2 KB vector (LAION CLAP, run in the browser with WebGPU). Only the vector and the artist / title / album text are sent. Searching turns your sentence into the same kind of vector and ranks every stored song against it.
What the server stores, and what it never stores: web/legal.html.
web/— the site.app.js(search + folder scan),worker.js(Chromaprint via WASM + CLAP via transformers.js),tones.js(the two-tone technicolor palette),water.js(a port of hyprwater),wasm/(the fingerprinter).server/— FastAPI. SQLite, a brute-force vector index in RAM, the CLAP text tower on ONNX Runtime (CPU). Runs on a Raspberry Pi.fingerprint-wasm/— Chromaprint (rusty-chromaprint) compiled to WebAssembly.deploy/— the Pi install script and systemd unit.
cd server && python -m venv .venv && .venv/bin/pip install -r requirements.txt
VIBEFIND_DATA=../data ACOUSTID_KEY=<your key from acoustid.org/new-application> .venv/bin/python -m uvicorn app:app --port 8080
Open http://localhost:8080. The text model (~240 MB) downloads on the first search; the browser downloads the audio model (~143 MB) on the first scan.
On a Raspberry Pi: deploy/pi-install.sh, then the unit in deploy/describesong.service.
server/tests/run_all.sh # everything, ~1 min; --offline skips the live part
Two kinds, on purpose:
- Deterministic parts are unit-tested —
test_names.py(is this word a name? 40 cases) andtest_braces.py(what do the braces under the bar say? 38 cases). They exec the real functions fromapp.pyagainst a fake catalogue. Seconds, no network. - Ranking is asserted as properties, not ranks —
live_invariants.pyhits the live site and checks what must hold at any index size: every row above a divider is by the named artist, the number never climbs down a page, word order does not change a name search, every top row of a "female singer" search carries the voice badge,&pages without repeats, the old domain redirects, API answers are gzipped. Exact ranks are not asserted anywhere, because the index grows every hour and they drift with it. - Descriptions are generated, not hand-picked —
live_descriptions.pybuilds 160 descriptions from the site's own sound vocabulary (mood + genre, tempo + genre, instrument + mood + era, hyphenated spellings too) and asserts none of them is read as a name. The hand-written cases are the ones somebody thought of; this finds the ones nobody did — it found two on its first day. bench_search.pyis the trend line: 18 described targets and where each lands. Read it after a change; do not make it pass.