Audio similarity service. Scans a music library, extracts a per-track embedding plus musical descriptors (BPM, key, loudness, danceability, onset rate), classifies each track against 400 Discogs styles, and reads file tags (artist, album, title, track number) using Essentia and mutagen. Everything is stored in SQLite and exposed via an HTTP API for similarity queries, style-filtered listings, and playlist generation.
- Python 3.9 - 3.12 (the upstream
essentia-tensorflowwheels don't cover 3.13+ yet) - x86_64 CPU (essentially anything from the last decade) or Apple Silicon
- ~1 GB RAM per worker process during extraction; budget accordingly
- Install
- API (full reference in API.md)
- Configuration
- Running as a service
pipx puts harmonie in its own isolated virtualenv with the harmonie binary on your PATH. The --pre flag is needed because essentia-tensorflow is published as a .dev pre-release.
If your system Python is 3.9 - 3.12:
sudo apt install pipx
pipx ensurepath
pipx install --pip-args='--pre' 'git+https://github.com/mxschll/harmonie.git'If your system Python is 3.13+, grab Python 3.12 via uv and point pipx at it:
curl -LsSf https://astral.sh/uv/install.sh | sh
uv python install 3.12
pipx install --python "$(uv python find 3.12)" --pip-args='--pre' \
'git+https://github.com/mxschll/harmonie.git'Run it:
HARMONIE_LIBRARIES=/path/to/music harmonie serveUpdate with pipx upgrade harmonie.
On first start, harmonie scans your library. Effnet inference is about a second per track on a modern x86 core. Throughput scales roughly with HARMONIE_WORKERS until storage I/O becomes the bottleneck. A 50k-track library on a fast box takes around a day. Subsequent scans are incremental: only files whose size or mtime changed get re-extracted.
Watch progress and trigger another scan:
curl http://localhost:8842/api/v1/scan | json_pp
curl -X POST http://localhost:8842/api/v1/scan | json_ppHarmonie exposes an HTTP API under /api/v1/. See API.md for the full reference.
All settings come from environment variables (or a .env file in the working directory):
| Variable | Default | Purpose |
|---|---|---|
HARMONIE_LIBRARIES |
(none) | Comma- or colon-separated absolute paths to scan |
HARMONIE_DATA_DIR |
platform user-data dir | Where to put harmonie.db. Defaults to ~/.local/share/harmonie on Linux, ~/Library/Application Support/harmonie on macOS. |
HARMONIE_WORKERS |
CPU count | Analysis worker processes. Each worker peaks around 1 GB of RAM during extraction of long files (10+ minute classical recordings, etc.), so set this conservatively on RAM-constrained boxes. |
HARMONIE_SCAN_INTERVAL_HOURS |
24 |
Periodic scan interval (0 disables) |
HARMONIE_SCAN_ON_STARTUP |
true |
Run a scan immediately on boot |
HARMONIE_HOST |
0.0.0.0 |
HTTP bind address |
HARMONIE_PORT |
8842 |
HTTP port |
HARMONIE_API_KEY |
(none) | If set, required in X-API-Key |
HARMONIE_CORS_ORIGINS |
(none) | Comma-separated list to enable CORS |
HARMONIE_LOG_LEVEL |
INFO |
DEBUG, INFO, WARNING, ERROR |
For long-lived deployments, run harmonie under systemd rather than in a shell or tmux. Systemd handles restarts, captures logs, and isn't tied to a login session.
Create /etc/systemd/system/harmonie.service:
[Unit]
Description=harmonie audio similarity service
After=network-online.target
[Service]
Type=exec
User=<your-user>
WorkingDirectory=/home/<your-user>
Environment=HARMONIE_LIBRARIES=/path/to/music
Environment=HARMONIE_DATA_DIR=/home/<your-user>/.local/share/harmonie
Environment=HARMONIE_WORKERS=6
Environment=HARMONIE_PORT=8842
ExecStart=/home/<your-user>/.local/bin/harmonie serve
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.targetReplace <your-user>, paths, and env vars with your own. Then:
sudo systemctl daemon-reload
sudo systemctl enable --now harmonie
sudo systemctl status harmonie
journalctl -u harmonie -fAfter config changes, sudo systemctl restart harmonie.
See DEVELOPMENT.md for architecture, scaling notes, the test workflow, and schema migration guidance.