Alle Projekte

Arbeit · 2026

Spicetify Stats

Eine Spicetify-App, die lokal mitschreibt, was gehört wird, und daraus Top-Listen und eine Heatmap baut.

Auf einen Blick

  • Top-Artists, Top-Tracks und eine Heatmap im GitHub-Stil
  • Ein Play zählt ab 30 Sekunden oder der halben Titellänge
  • Nichts wird hochgeladen, alles bleibt im localStorage
  • Import des eigenen Spotify-Datenexports samt Dubletten-Erkennung
  • Rohdaten für 180 Tage, ältere Monate als Rollups
  • Ohne Build-Schritt, jede Grafik von Hand als Inline-SVG

Idee

Spotify zeigt einmal im Jahr Wrapped und den Rest der Zeit nichts. Diese Custom App legt einen eigenen Eintrag in die Sidebar: Top-Artists, Top-Tracks und eine Heatmap im GitHub-Stil, wahlweise über 7, 30 oder 90 Tage oder über die gesamte Historie.

Aufgeteilt ist das in zwei Teile. Ein Collector läuft als Extension mit Spotify mit und schreibt den Verlauf, egal ob die App offen ist. Die App selbst liest nur, was schon dasteht. Die einzige Verbindung zwischen beiden ist der localStorage — hochgeladen wird nichts.

Der eigentliche Aufwand steckt im Zählen

Ein Titel zählt erst als gehört, wenn er 30 Sekunden lief oder die halbe Länge erreicht ist, je nachdem was früher kommt. Durch eine Playlist zu skippen erzeugt damit gar keine Einträge.

Gemessen wird die Hörzeit an der Abspielposition des Players, begrenzt durch die tatsächlich vergangene Zeit. Pausieren sammelt keine Zeit an, Vorspulen zählt nicht als gehört, und weder ein Standby des Rechners noch ein gedrosselter Timer im Hintergrund verzerren den Wert.

Ein Titel in Dauerschleife zählt jedes Mal neu — eine Stunde Loop ist eine Stunde Hören — aber nur nach einer vollen Schwelle wirklich neuer Wiedergabe. Spotify feuert sein songchange-Event gelegentlich mehrfach für denselben Titel; diese Dubletten können die Zahl nicht aufblähen.

Geschrieben wird der Play in dem Moment, in dem die Schwelle fällt, nicht am Ende des Titels. Wer Spotify mittendrin schließt, verliert also keinen Play, der bereits gezählt hat.

Nicht gezählt werden Podcasts, lokale Dateien und Werbung.

Das Platzproblem

localStorage ist klein, und ein Datenexport über mehrere Jahre passt dort nicht als Rohdaten hinein. Deshalb zwei Ebenen: Einzelne Plays werden 180 Tage lang behalten, gedeckelt bei 20.000 Einträgen. Was älter ist, wird zu Monats-Rollups zusammengefasst — Plays, Hörzeit, Top 100 der Artists und Tracks — und die Rohzeilen fallen weg. „Gesamt“ liest beide Ebenen.

Der Preis dafür ist ehrlich sichtbar: Die Heatmap kennt Tagesdetails nur für Rohdaten. Zusammengefasste Monate zählen in Summen und Top-Listen mit, aber nicht im Raster.

Import des Spotify-Exports

Die eigenen Hördaten kann man bei Spotify anfordern. Die App liest beide Formate, die dabei herauskommen, und erkennt bereits vorhandene Plays wieder — über einen exakten Zeitstempel und zusätzlich über denselben Titel innerhalb von etwa zehn Minuten. Das ist nötig, weil der Collector beim Überschreiten der Schwelle stempelt, der Export dagegen beim Ende der Wiedergabe: derselbe Play, zwei verschiedene Zeiten.

Kein Build-Schritt

Spicetify lädt die Dateien direkt, also gibt es kein npm, keinen Bundler, kein TypeScript und kein JSX — Custom Apps können es nicht, React-Elemente entstehen über Spicetify.React.createElement.

Jede Grafik ist wie schon bei Zen Mode von Hand als Inline-SVG gebaut, weil Spotifys Content-Security-Policy externe und data:-Ressourcen blockiert. Die Farben kommen ausschließlich aus Spotifys semantischen Variablen, damit sich das Dashboard dem aktiven Theme anpasst — auch dem kpop-theme.