self-hosted ยท one listener ยท est. 2012 in the data

A private pressing of everything you play.

An acetate is the one-off reference disc a mastering engineer cuts before an album goes to press โ€” a personal cut of the real thing. This is that, for a listening life: every play since 2012, archived on hardware I own, analyzed by neural networks that have listened to the entire library, and turned back into playlists, maps, and answers.

155,831
real plays archived
14
years of history
24,163
tracks the models listened to
0
cloud services involved

What it is

archive

Append-only backups

Playlists and liked songs are content-hashed and snapshotted on change. A deleted playlist โ€” or a song that quietly un-likes itself โ€” leaves a trace forever.

history

Every play, kept

The GDPR export reaches back to 2012; a 15-minute poller keeps it current with no gaps. A canonicalization pass collapses duplicate track IDs so your favorites don't split their counts.

stats

A private Wrapped

Year, quarter, month or week โ€” top tracks, artists, genres, discovery rate, listening clock. In July, without asking anyone.

robots

Playlists that build themselves

Monthly liked archives, top-played rotations, lost gems. Proposed and maintained by robots that can never touch a playlist a human made.

ears

Models that listened

Every track is embedded from actual audio โ€” a genre-shaped space (discogs-effnet) and a description-shaped one (CLAP). Genre stops being metadata and becomes a property of the sound.

map

Hyperspace

The library as a 3D point cloud โ€” UMAP over the embeddings, HDBSCAN finding the continents. Taste, it turns out, has geography.

Why it exists

In late 2024, Spotify turned off the APIs that made this hobby possible for new apps. The message was clear: if you want analysis, compute it yourself โ€” from your own listening history, open data, and the audio itself.

So acetate does. It runs on a small box at home. Spotify can't delete it, redesign it, or decide the feature I use isn't worth maintaining.

Inside

The acetate home page: 14 years of listening in numbers, listening hours by year
the front page โ€” live status bar, hero stats, plays by year
The Wrapped page: a year in review, computed locally
Wrapped for any period, computed locally
Hyperspace: the library as a 3D point cloud, colored by cluster
Hyperspace โ€” clusters the algorithm found on its own

The architecture, honestly

Everything sits on one SQLite file. That's not a compromise โ€” it's a thesis: at personal scale, a single WAL-mode file outruns any database you'd deploy, backs up with cp, and will still open in thirty years.

Sources

Spotify APIrecently-played poll ยท 15 min GDPR exportevery play since 2012 Deezer30s preview audio Last.fmtags ยท co-listening MusicBrainzrecording MBIDs
โ†’

one sqlite file

play_eventsthe listening log *_snapshotsappend-only backups recording_statscanonical per-song rollup embeddingsfloat32 blobs, 1280-d recording_stylesdiscogs400 top-8
โ†’

Consumers

dashboardstats ยท wrapped ยท gems playlist robotsmonthly ยท gems ยท smart chat agentSQL + similarity tools Hyperspacethe 3D map

The map

Hyperspace colored by dominant Discogs style
Fourteen years of taste, in three dimensions.
a curated cut of the map, live on this site โ€” fly it yourself โ†’

Run it yourself

acetate is a uv-managed Python app: FastAPI + HTMX dashboard, APScheduler jobs, one SQLite file, Docker Compose on whatever box you own. Nothing about it is specific to my library โ€” that's just the copy it grew up around.

The open-source repo is coming soon. The code was built in the open with my own listening data wired all the way through, and separating the instrument from the specimen deserves a careful pass, not a Friday-night force-push. Until then, the journey post is the documentation: the architecture, the model choices, and the graveyard of what not to build.

The full story

How a playlist-backup script became a lab notebook: the genre saga (four acts, one heartbreak), teaching machines to hear and to read, the dependency-hell trophies, and the graveyard of roads not taken โ€” with real spectrograms, real cosine matrices, and real numbers from the real database.

How I made acetate โ†’