See where your music comes from.
Your Spotify library, plotted on a living globe—every artist pinned to the city they came from, colored by genre, clustered as you explore.
⭐ Star this repo if you like it
![]() Globe view |
![]() Cluster view |
![]() Artist detail card |
- Interactive globe & map — Satellite globe, vector street map, and clean minimal. Zoom from space to street level.
- One-click library pull — Liked Songs and playlists, with smart rate-limit retry.
- Genre-colored markers — Hip-Hop, R&B/Soul, Pop, Indie, Rock, Electronic, Jazz, Reggae, Afrobeats, Latin.
- Clustering & heatmap — Nearby artists group as you zoom; density at a glance.
- Search & filter — Artist, city, country, playlist, or genre—combine freely.
- Artist detail cards — Origin, genre, playlists, and play a liked track in-browser.
- Arcs & auto-spin — Draw paths to your home, or let the globe spin.
- CSV import/export — Exportify-compatible; merge multiple sources.
- Responsive — Desktop, tablet, and mobile.
Every user brings their own Spotify API keys — Spotify apps in Development Mode can only be used by accounts their owner has allowlisted, so a shared key would not work for you anyway. Setup takes about a minute, and the app walks you through it on first launch.
- Python 3.9+
- A free Spotify account (Premium only matters for in-app playback)
git clone https://github.com/myselfsiddharth/Spotify-Music-Map.git
cd Spotify-Music-Map
pip install -r requirements.txt
python app.pyOpen http://127.0.0.1:5000. The app opens on a setup screen that asks for your Spotify API keys — follow the steps below, paste them in, and it moves straight on to Connect with Spotify.
Use
127.0.0.1, notlocalhost. Spotify rejectslocalhostredirect URIs, and the two are different origins to your browser.
The keys identify your copy of the app to Spotify. They are free, take a minute, and nobody else ever sees them.
- Open the dashboard. Go to the Spotify Developer Dashboard and log in with your normal Spotify account. Accept the Developer Terms if prompted.
- Create an app. Click Create app. The name and description can be anything (for example, My Sonic Cartography).
- Add the Redirect URI. In the Redirect URIs field, paste this exactly, then press Add:
The setup screen in the app shows the exact URI for your machine — copy it from there if you run on a different port or host. It must match character for character, including
http://127.0.0.1:5000/api/auth/callbackhttp://and the trailing path. - Select the API. Tick Web API under Which API/SDKs are you planning to use?
- Save, then open your new app and click Settings.
- Copy the keys. The Client ID is shown on the page. Click View client secret to reveal the Client secret. Both are 32-character codes.
- Paste them into the app on the setup screen and click Save keys & continue. The app verifies them with Spotify before accepting them, so a typo is caught immediately.
Your keys are held in this app's session cookie — the same place it keeps your Spotify OAuth tokens. They are never written into the repo, never logged, and never sent anywhere except Spotify. Click Use different Spotify API keys on the sign-in screen to change or remove them later, and see SECURITY.md for exactly how they are stored.
A new Spotify app starts in Development Mode, which is all you need for yourself. If you want other people to sign in to your copy, add each of their Spotify accounts under Settings → User Management in the dashboard (up to 25).
If you would rather configure the keys once on the server — useful for a private deployment or a machine you re-clone often — copy the template and fill it in. When these are set, the app starts at the Spotify sign-in screen instead.
cp .env.example .env
# Edit .env with your Spotify credentialsgunicorn wsgi:app --bind 0.0.0.0:5000Use HTTPS in production so secure session cookies are enforced, and set SPOTIFY_REDIRECT_URI to your public callback URL.
| Symptom | Fix |
|---|---|
| INVALID_CLIENT: Invalid redirect URI | The Redirect URI in your Spotify app doesn't match. Copy it from the app's setup screen and paste it into the dashboard exactly, including the port and /api/auth/callback. |
| Spotify rejected these keys | The Client ID or secret was mistyped or truncated. Re-copy both from Settings on your app's dashboard page. |
| Login works, but the map is empty | Spotify rate-limits new apps aggressively. Wait for the countdown shown in the status bar, or import an Exportify CSV in the meantime. |
| Another account can't log in | Add it under Settings → User Management in your Spotify app. |
| Asked for keys again after restart | Set FLASK_SECRET_KEY in .env, or let the app keep the .flask_secret file it generates in the project root. |
Spotify OAuth --> Fetch Liked Songs + Playlist Tracks
|
Extract Artists
|
Wikidata SPARQL (concurrent batches)
P19 birthplace / P740 formation city
|
Geocode to lat/lng
|
Cache + Render on MapLibre Globe
- Authenticate with Spotify (OAuth 2.0) using your own app keys
- Pull Liked Songs and playlist tracks via Spotify Web API with retry/backoff
- Resolve artist origins via Wikidata SPARQL (birthplace for solo artists, formation city for groups)
- Cache results so return visits are instant
- Render on a MapLibre GL globe with genre-colored clusters
All of these are optional — without them, the app asks for your Spotify keys in the browser and generates a session key on first run.
| Variable | Description |
|---|---|
FLASK_SECRET_KEY |
Signs session cookies. Generated once into .flask_secret if unset |
SPOTIFY_CLIENT_ID |
From Spotify Developer Dashboard; skips the setup screen |
SPOTIFY_CLIENT_SECRET |
From Spotify Developer Dashboard; skips the setup screen |
SPOTIFY_REDIRECT_URI |
Must match your Spotify app settings. Defaults to the host you open the app on |
MUSICBRAINZ_CONTACT_EMAIL |
Contact email for MusicBrainz API (optional) |
PORT |
Server port (default: 5000) |
Keys entered through the setup screen take precedence over SPOTIFY_CLIENT_ID / SPOTIFY_CLIENT_SECRET, and are scoped to one browser session — two people using the same server each use their own Spotify app.
SpotifyMap/
├── app.py # Flask backend (API, Spotify, Wikidata)
├── sonic-cartography.html # Single-page app shell
├── wsgi.py # Gunicorn entrypoint
├── requirements.txt # Python dependencies
├── .env.example # Environment template
├── docs/assets/ # README screenshots & demo GIF
├── static/
│ ├── css/
│ │ └── sonic-cartography.css # Responsive styles & themes
│ └── js/
│ ├── main.js # App bootstrap & state
│ ├── setup.js # First-run Spotify API key screen
│ ├── auth.js # Spotify auth & data fetching
│ ├── map-controller.js # MapLibre globe/map rendering
│ ├── ui.js # Search, filters, panels, detail cards
│ ├── filter-engine.js # Client-side filtering & sorting
│ ├── data-store.js # Reactive data state
│ ├── config.js # Genre palette & constants
│ └── utils.js # Geospatial utilities
└── spotify_origins.py # Standalone CLI tool
- Flask serves the API and static files
- Spotipy handles Spotify Web API with OAuth 2.0
- Wikidata SPARQL resolves artist origins with concurrent batch queries
- Three-layer caching: browser localStorage, server-side JSON, and origins cache
- MapLibre GL JS renders the GPU-accelerated globe with clustering, heatmap, and arcs
- OpenFreeMap provides vector basemaps (no API key required)
- Esri World Imagery powers the satellite globe view
- Vanilla JS (ES6 modules, no framework) keeps it lightweight and fast
- Lucide icons for a clean, modern interface
| Genre | Color | Keywords |
|---|---|---|
| Hip-Hop | #FF7A6B |
hip hop, rap, drill, trap, grime |
| R&B/Soul | #F5B942 |
r&b, soul, neo soul, funk, motown |
| Pop | #FFD166 |
pop, k-pop, singer-songwriter |
| Indie | #9B7BFF |
indie, dream pop, shoegaze, lo-fi |
| Rock | #E85D9E |
rock, metal, punk, grunge, emo |
| Electronic | #4ECDC4 |
house, techno, edm, ambient, trance |
| Afrobeats | #5DD39E |
afrobeat, afropop, amapiano, highlife |
| Latin | #FF9F45 |
reggaeton, salsa, bachata, cumbia |
| Jazz | #7FB2FF |
jazz, bossa, swing, bebop |
| Reggae | #8BD450 |
reggae, dancehall, dub, ska |
| Endpoint | Method | Description |
|---|---|---|
/api/config/spotify |
GET | Whether API keys are set, and the redirect URI to register |
/api/config/spotify |
POST | Verify and save your Client ID / secret for this session |
/api/config/spotify |
DELETE | Forget saved API keys |
/api/auth/login |
GET | Initiate Spotify OAuth |
/api/auth/callback |
GET | OAuth redirect handler |
/api/auth/me |
GET | Current user profile |
/api/auth/logout |
POST | Clear session |
/api/library/sync |
POST | Sync top artists + liked songs |
/api/library/pull-playlists |
POST | Pull all playlists with retry/backoff |
/api/library/playlists |
GET | List playlist metadata |
/api/library/export-csv |
GET | Download Exportify-compatible CSV |
/api/library/import-csv |
POST | Import Exportify CSV files |
/api/library/liked-track |
GET | Look up liked track for an artist |
Sonic Cartography reads and writes Exportify-compatible CSVs. You can:
- Export any playlist as CSV directly from the app
- Import CSVs from exportify.net or any Exportify-compatible source
- Merge multiple CSV imports onto a single map
- Concurrent Wikidata resolution — Up to 4 parallel SPARQL batch queries
- Rate-limit resilience — Automatic retry with Retry-After backoff for Spotify API
- Three-layer caching — Browser localStorage + server JSON cache + origins cache means instant loads on return visits
- Client-side filtering — All search, filter, and sort operations happen in the browser with zero server round-trips
- MapLibre clustering — GPU-accelerated marker grouping for smooth rendering at any scale
Contributions are welcome. Please read CONTRIBUTING.md before opening a PR.
In short:
- Fork, branch from
main, and keep changes focused - Run tests and make sure your solution works (
pytest, then manually verify in the app) - Never commit secrets or personal Spotify/cache data
- Follow the Code of Conduct
Security issues: see SECURITY.md (do not file public issues for vulnerabilities).
- CI — runs tests on every PR
- CodeQL — security analysis for Python and JavaScript
- Dependency review — blocks high-severity dependency changes on PRs
- Dependabot — weekly dependency and Actions updates
- Release Drafter — keeps a draft release notes PR changelog
- Stale bot — closes inactive issues/PRs (security and pinned issues are exempt)
- Labeler — auto-labels PRs by changed paths
- First interaction — greets first-time contributors
- MapLibre GL JS (BSD-3-Clause)
- OpenFreeMap (MIT)
- OpenStreetMap (ODbL)
- Satellite imagery by Esri and partners
- Wikidata for artist origin data
- Lucide icons (ISC)
- Spotipy Spotify client (MIT)
Built by Siddharth



