Work · 2026
Spicetify Stats
A Spicetify app that logs what you listen to, locally, and turns it into top lists and a heatmap.
At a glance
- Top artists, top tracks and a GitHub-style heatmap
- A play counts after 30 seconds or half the track length
- Nothing is uploaded, everything stays in localStorage
- Imports your own Spotify data export, duplicates included
- Raw events for 180 days, older months as rollups
- No build step, every chart hand-built as inline SVG
The idea
Spotify shows you Wrapped once a year and nothing the rest of the time. This custom app puts its own entry in the sidebar: top artists, top tracks and a GitHub-style heatmap, over 7, 30 or 90 days or across the whole history.
It comes in two parts. A collector runs as an extension alongside Spotify and writes the log
whether or not the app is open. The app itself only reads what is already there. The single
connection between them is localStorage — nothing is uploaded anywhere.
The real work is in the counting
A track only counts as played once it has run for 30 seconds or half its length, whichever comes first. Skipping through a playlist therefore produces no entries at all.
Listening time is measured from the player’s own playback position, bounded by the time that has actually elapsed. Pausing accumulates nothing, seeking forward does not count as listening, and neither a suspended machine nor a throttled background timer distorts the number.
A track on repeat counts again each time — an hour on loop is an hour of listening — but only
after a full threshold of genuinely new playback. Spotify sometimes fires its songchange event
more than once for the same track, and those duplicates cannot inflate the count.
The play is written the moment the threshold falls, not when the track ends. Quitting Spotify mid-track therefore cannot lose a play that already counted.
Podcasts, local files and ads are not counted.
The space problem
localStorage is small, and a data export spanning several years does not fit into it as raw
rows. Hence two layers: individual plays are kept for 180 days, capped at 20,000 entries.
Anything older is folded into monthly rollups — plays, listening time, the top 100 artists and
tracks — and the raw rows are dropped. “All time” reads both layers.
The cost is visible rather than hidden: the heatmap only knows per-day detail for raw events. Months that have been folded in still count towards totals and top lists, but not in the grid.
Importing the Spotify export
Spotify will send you your own listening history on request. The app reads both shapes it comes in and recognises plays that are already stored — by an exact timestamp, and additionally by seeing the same track within about ten minutes. That second check is necessary because the collector timestamps a play when the threshold is crossed while the export timestamps it when playback ended: the same play, at two different times.
No build step
Spicetify loads the files directly, so there is no npm, no bundler, no TypeScript and no JSX —
custom apps cannot use it, and React elements are built through Spicetify.React.createElement
instead.
As with Zen Mode, every chart is hand-built inline SVG, because
Spotify’s Content-Security-Policy blocks external and data: resources. The colours come purely
from Spotify’s semantic variables, so the dashboard follows whichever theme is active — including
kpop-theme.