lieberorg All apps

A2 · Side A · Listening

haystack

Finds the needle.

Music discovery for one Spotify account, on an always-on Linux box. It learns your taste from the audio itself, scouts new tracks by how they sound, and puts the best in a weekly Discoveries playlist.

Short straw-coloured dashes stacked in columns into a mound, labelled Candidates, by score, and rising to Discoveries at the right. The dashes at the right end and a few in the middle are amber.
A week's candidates, stacked by score. The amber ones are the batch: the best, plus a few from the middle for exploring. It's a picture, not real numbers.

What it does

haystack learns your taste from how your music sounds, not from its tags. It runs every track in your library, and every song you've liked on Spotify, through Essentia's Discogs-EffNet model, in about nine 30-second windows each.

Each morning it gathers candidates from Deezer and ListenBrainz and does the same with their 30-second previews. A candidate scores by its closest match among the music you like, less a penalty for sounding like what you've turned down.

On Monday mornings the best go into a private Discoveries playlist: 15 by default, though the batch size is yours to set in its config. It takes one per artist, with about a fifth of them spent exploring. What you do next teaches it: your likes on Spotify and your Verdicts in needle. A discovery still unliked when the next batch comes counts as a no, and a liked one that isn't in your library goes on a To Rip playlist.

Its week

Daily, 03:15
library Scans your music and embeds what's new or changed.
Daily, 05:00
harvest Gathers and embeds candidates, then scores them again.
Hourly
sync Reads your plays and likes, settles outcomes and updates the playlists.
Mondays, 08:00
batch Clears Discoveries, then adds the new batch.
Always on
api needle's API, and the Spotify writes after each Verdict.

How it works

Python, with Essentia for the audio and SQLite for the rest. It runs in a Docker container, where one process serves needle's API and starts each job on the schedule in its config. Or, without Docker, as systemd user units under linger. Either way it carries on with nobody logged in, and the timed jobs run at the lowest CPU priority.

Your library is read-only to it: files are opened read-only and tags are only ever read. Previews stay in memory, piped into ffmpeg and never written to disk, and Deezer gets at most one request every 1.1 seconds.

It has no screen of its own. needle is where you hear each batch and judge it, and haystack makes every Spotify write.

Credits

Runs on
An always-on Linux server or VPS, for one Spotify account. needle is its screen.
Install
The Docker image on GitHub (linux/amd64), with Docker Compose. Or from source with pip, under systemd user units.
Built with
Python, Essentia, ffmpeg and SQLite
Latest
0.9.0, 23 October 2026
Licence
AGPL-3.0-or-later
Models
Essentia's Discogs-EffNet and MusiCNN models, by the Music Technology Group at Universitat Pompeu Fabra, under CC BY-NC. haystack downloads them at setup and never ships them, and using them commercially needs a licence from the MTG.
Libraries
Essentia (AGPL-3.0, with TensorFlow under Apache-2.0), numpy (BSD-3-Clause), requests (Apache-2.0) and mutagen (GPL-2.0-or-later), installed by pip. ffmpeg (LGPL or GPL, by build) decodes the audio.
Services
Deezer's public API for candidates and previews, for private, non-commercial use under its terms. ListenBrainz, by the MetaBrainz Foundation, for similar recordings. The Spotify Web API, through your own Spotify app. haystack is not affiliated with, or endorsed by, any of them.
Source
kurtlieber/haystack on GitHub. Its NOTICE lists every licence.