What does this place sound like?
Music Map is a collaborative, emotional atlas of the world where people pin songs to places. Walk through the streets of Naples and discover someone pinned Napule e' by Pino Daniele on the lungomare. Find the lo-fi track a stranger left on that tiny cafe in Trastevere where you had the best espresso of your life.
Every pin is a memory. Every place has a soundtrack.
Live App • Getting Started • Privacy • Contributing
- Explore -- open the map and browse thousands of songs pinned around the world. No account needed.
- Sign in -- log in with Google or Apple to unlock personal features. We store zero personal data (see Privacy Architecture).
- Pin a Song -- paste a Spotify, YouTube, or Apple Music link, drop a pin on the map, and leave a short memory about why that song belongs there.
- Your Map -- see all your pins in one place. Your soundtrack, your memories.
- Share -- every pin has a unique link with a beautiful social preview. Send it anywhere.
- Full-viewport interactive map with CartoDB tiles (light and dark)
- GeoJSON clustering that scales from 10 to 100,000 pins
- Spotify, YouTube, and Apple Music metadata parsing (no API keys needed)
- Google and Apple Sign-In with zero-knowledge authentication
- Privacy by design -- admins cannot link pins to real people
- Personal listening map ("My Pins") for authenticated users
- Landing page with value props for new visitors
- Glassmorphism UI with smooth animations and micro-interactions
- Mobile-first bottom sheets, touch-friendly pin placement
- Dark mode with system preference detection and manual toggle
- Italian and English localization
- Server-side rendered pin pages with Open Graph meta tags
- Reverse geocoding via OpenStreetMap Nominatim
- Full-text search across songs, artists, and cities
- Rate limiting and content moderation
- Complete privacy policy (IT/EN)
- Random pin with cinematic fly-to animation
Music Map is designed so that even the project operators cannot link pins to real people. Here's how:
- OAuth with Google/Apple requests only the
openidscope -- no email, no name, no profile photo. - The provider returns a technical
sub(subject identifier). - This
subis immediately hashed with SHA-256 + a secret pepper before storage. - The pepper is a Cloudflare Worker Secret -- not in the database, not in the source code, not in any logs.
| Table | Contains | Does NOT contain |
|---|---|---|
users |
Random UUID, hashed auth subject, creation date | Email, name, profile, OAuth tokens |
pins |
Coordinates, song link, optional display name, optional user UUID | IP address, real name, email |
A full database dump reveals only random UUIDs and hex hashes. Without the pepper (which exists only as an ephemeral Worker Secret), the hashes cannot be reversed. An administrator with complete infrastructure access cannot determine which real person created which pins.
The entire codebase is open source -- verify these claims yourself by reading src/lib/auth.ts and schema-v2.sql.
| Layer | Technology |
|---|---|
| Framework | Astro 4 with hybrid rendering |
| UI | React 18 (islands architecture) |
| Map | MapLibre GL JS + react-map-gl |
| Tiles | CartoDB Positron (light) / Dark Matter (dark) |
| Styling | Tailwind CSS 3 |
| Database | Cloudflare D1 (SQLite) with FTS5 |
| Cache / Sessions | Cloudflare KV |
| Storage | Cloudflare R2 |
| Auth | OAuth 2.0 (Google, Apple) with Web Crypto API |
| Deployment | Cloudflare Pages + Workers |
src/
components/
map/ Map engine (MapView, markers, clustering, animations)
ui/ Reusable primitives (GlassPanel, BottomSheet, VinylSpinner)
MusicMapApp.tsx Main orchestrator -- owns all application state
TopBar.tsx Search, theme/language toggles, auth menu
SongCard.tsx Pin detail view with embedded player
PinForm.tsx Multi-step pin creation flow
FAB.tsx Floating action button
LandingOverlay.tsx Scroll-based landing page for new visitors
LoginPrompt.tsx Auth modal when unauthenticated user tries to pin
MyPinsPanel.tsx Personal pin collection panel
PrivacyContent.tsx Privacy policy page content (IT/EN)
ShareButtons.tsx Social sharing (WhatsApp, Telegram, X, copy link)
SearchPanel.tsx Search results dropdown
PinCounter.tsx Animated global pin counter
EmptyState.tsx Friendly empty state message
hooks/
useTheme.ts Dark/light mode with localStorage
useLanguage.ts IT/EN with localStorage
useGeolocation.ts Browser geolocation API
useAuth.ts Authentication state + login/logout
pages/
api/auth/ OAuth routes (Google, Apple, logout, session check)
api/pins/ CRUD endpoints (list, create, search, random, trending, mine)
api/song-info.ts Spotify/YouTube/Apple Music metadata parser
api/count.ts Global pin count
pin/[slug].astro Shared pin page with OG tags (SSR)
privacy.astro Privacy policy page
index.astro Home page (SSR, auth-aware)
lib/
auth.ts Auth: hashing, sessions, cookies, user management
db.ts D1 database queries
api.ts Client-side API helpers
song-parser.ts Song URL parsing + metadata fetching
geocode.ts Reverse geocoding
rate-limit.ts IP-based rate limiting
profanity.ts Content moderation
constants.ts Shared constants
i18n/
translations.ts UI translations (IT/EN)
privacy.ts Privacy policy full text (IT/EN)
middleware.ts Session resolution on every request
types/ Shared TypeScript interfaces
styles/ Global CSS with glassmorphism utilities
schema.sql D1 database schema v1
schema-v2.sql Schema migration: users table + user_id on pins
wrangler.toml Cloudflare deployment configuration
- Node.js 18+
- npm 9+
- Wrangler CLI (for local D1)
git clone https://github.com/amargiovanni/music-map.git
cd music-map
npm installnpm run db:init
npm run db:migrate:v2
npm run db:seed # loads ~25 sample pins across Italy + a dev usercp .dev.vars.example .dev.varsEdit .dev.vars and set at least AUTH_PEPPER (a random 64-char hex string). Google and Apple OAuth credentials are optional for local testing — use Dev Login instead.
npm run devOpen http://localhost:4321 in your browser.
In development mode, a Dev Login button appears on the landing page and in the login prompt. It creates a local session without going through real OAuth — perfect for testing authentication, "My Pins", and pin creation.
The Dev Login endpoint (/api/auth/dev/login) is automatically disabled in production builds.
npm run buildnpx wrangler d1 create music-map-db
npx wrangler kv namespace create KV
npx wrangler r2 bucket create music-map-r2Replace the placeholder IDs with the real ones from the commands above.
npx wrangler d1 execute music-map-db --remote --file=schema.sql
npx wrangler d1 execute music-map-db --remote --file=schema-v2.sqlnpx wrangler secret put AUTH_PEPPER
npx wrangler secret put GOOGLE_CLIENT_ID
npx wrangler secret put GOOGLE_CLIENT_SECRET
npx wrangler secret put APPLE_CLIENT_ID
npx wrangler secret put APPLE_TEAM_ID
npx wrangler secret put APPLE_KEY_ID
npx wrangler secret put APPLE_PRIVATE_KEYGoogle Cloud Console:
- Authorized redirect URI:
https://your-domain.com/api/auth/google/callback
Apple Developer Portal:
- Return URL:
https://your-domain.com/api/auth/apple/callback
Push to GitHub for automatic deployment via Cloudflare Pages, or:
npx wrangler pages deploy dist/| Method | Endpoint | Auth | Description |
|---|---|---|---|
GET |
/api/pins?bounds=s,w,n,e |
No | Pins within a bounding box |
GET |
/api/pins?near=lat,lng&radius=5km |
No | Pins near a location |
GET |
/api/pins/:slug |
No | Single pin by slug |
GET |
/api/pins/search?q=query |
No | Search by song, artist, or city |
GET |
/api/pins/random |
No | Random pin |
GET |
/api/pins/trending |
No | Most recent pins |
POST |
/api/pins |
Optional | Create a new pin (user_id linked if authenticated) |
GET |
/api/pins/mine |
Yes | All pins by the authenticated user |
GET |
/api/song-info?url=... |
No | Parse Spotify/YouTube/Apple Music metadata |
GET |
/api/count |
No | Total pin count |
GET |
/api/auth/google/login |
No | Start Google OAuth flow |
GET |
/api/auth/apple/login |
No | Start Apple OAuth flow |
POST |
/api/auth/logout |
Yes | End session |
GET |
/api/auth/me |
No | Check authentication status |
- The map IS the app. Everything floats over it. No chrome, no sidebar navigation, no page transitions.
- Mobile first. Touch targets are large, gestures are natural, bottom sheets feel native.
- Emotionally warm. Glassmorphism with warm amber accents, DM Sans typography, subtle animations.
- Privacy is not optional. Zero-knowledge auth by default. No email, no tracking, no analytics. Open source for full verifiability.
- Low friction. The map is always visible. Signing in unlocks personal features but is never required to explore.
- Apple Music integration
- User authentication (Google + Apple)
- Personal pin collections ("My Pins")
- Privacy policy
- Landing page
- Playlist mode -- play all songs pinned in a neighborhood
- Heatmap layer showing music density
- Genre filter on the map
- Time travel slider -- see pins from different months/years
- Musical walk -- generate a route through nearby pins
- PWA with offline support
Contributions are welcome! Please read the Contributing Guide before submitting a pull request.
This project is licensed under the MIT License.
Every place has a soundtrack. What does yours sound like?