# karaagexc/songless architecture

> Architecture diagram of the GitHub repository karaagexc/songless, generated by GitDiagram from its file tree, README and a sample of source files (last updated 2026-09-29). It is an AI-made overview: check details against the source.

- Interactive diagram: https://gitdiagram.com/karaagexc/songless
- Repository: https://github.com/karaagexc/songless

## Overview

Songless is a browser-based music guessing game with Daily, Unlimited, and timed Blitz play. The player selects a mode and filters or imports a playlist; the game obtains a track through its catalog/API services, plays short listening windows, evaluates guesses, then records results and offers shareable output. The server also supports discovery, search, streaming, artwork, announcements, and a separately maintained shared catalog with refresh and classification. UI-to-API details are only partially sampled, so unsampled route wiring is shown only where README or sampled calls establish it.

Main entry points: [`src/App.tsx`](https://github.com/karaagexc/songless/blob/master/src/App.tsx) (Game orchestration), [`src/services/musicApi.ts`](https://github.com/karaagexc/songless/blob/master/src/services/musicApi.ts) (Music API client), [`server/catalogDailyRefresh.ts`](https://github.com/karaagexc/songless/blob/master/server/catalogDailyRefresh.ts) (Daily catalog refresh), [`server/catalogStore.ts`](https://github.com/karaagexc/songless/blob/master/server/catalogStore.ts) (Catalog storage)

## Diagram (Mermaid)

```mermaid
flowchart TD

subgraph group_game["Game experience"]
  node_page["Game page<br/>[index.tsx]"]
  node_gameplay["Game orchestration<br/>[App.tsx]"]
  node_controls["Game UI"]
  node_filters["Era and genre"]
  node_artist["Artist picker"]
  node_playlistui["Playlist import UI"]
  node_results["Results and stats UI<br/>[StatsModal.tsx]"]
  node_onboarding["First-time guide<br/>[FirstTimeGuide.tsx]"]
end

subgraph group_browser["Browser services"]
  node_musicapi["Music API client<br/>[musicApi.ts]"]
  node_queue["Catalog queue<br/>[catalogQueue.ts]"]
  node_audio["Audio playback<br/>[audioService.ts]"]
  node_history[("Local stats and history<br/>[statsService.ts]")]
  node_playlist[("Playlist sessions<br/>[playlistService.ts]")]
  node_sharing["PNG sharing<br/>[shareSignal.ts]"]
  node_identity["Song identity<br/>[songIdentity.ts]"]
end

subgraph group_api["API and providers"]
  node_searchroute["Search route<br/>[search.ts]"]
  node_artworkroute["Artwork route<br/>[artwork.ts]"]
  node_streamroute["Audio stream route<br/>[stream.ts]"]
  node_playlistroute["Playlist route<br/>[playlist.ts]"]
  node_announcements["Announcement route<br/>[announcements.ts]"]
  node_musiccatalog["Provider catalog<br/>[musicCatalog.ts]"]
  node_spotifyart["Spotify artwork<br/>[spotifyArtwork.ts]"]
  node_refreshroute["Refresh route<br/>[catalog-refresh.ts]"]
end

subgraph group_catalog["Shared catalog"]
  node_catalogbrowse["Catalog browse<br/>[catalogBrowse.ts]"]
  node_store[("Catalog storage<br/>[catalogStore.ts]")]
  node_sources["Catalog sources<br/>[catalogSources.ts]"]
  node_enrichment["Catalog enrichment"]
  node_reconcile["Catalog reconciliation"]
  node_refresh["Daily catalog refresh"]
  node_coverage["Catalog coverage<br/>[catalogCoverage.ts]"]
end

node_player(("Player"))
node_supabase[("Supabase")]
node_spotify["Spotify"]
node_itunes["iTunes"]

node_player -->|"opens"| node_page
node_page -->|"loads client-only"| node_gameplay
node_player -->|"plays and guesses"| node_gameplay
node_gameplay -->|"requests candidate"| node_queue
node_gameplay -->|"requests songs"| node_musicapi
node_musicapi -->|"browses"| node_catalogbrowse
node_gameplay -->|"controls playback"| node_audio
node_gameplay -->|"records results"| node_history
node_gameplay -->|"saves session"| node_playlist
node_gameplay -->|"creates share image"| node_sharing
node_gameplay -->|"renders"| node_controls
node_gameplay -->|"renders"| node_filters
node_gameplay -->|"renders"| node_artist
node_gameplay -->|"renders"| node_playlistui
node_gameplay -->|"renders"| node_results
node_gameplay -->|"renders"| node_onboarding
node_gameplay -->|"matches tracks"| node_identity
node_history -->|"canonicalizes"| node_identity
node_playlist -->|"deduplicates tracks"| node_identity
node_musicapi -->|"requests search"| node_searchroute
node_searchroute -->|"calls search"| node_musiccatalog
node_musiccatalog -->|"queries provider"| node_itunes
node_playlist -->|"imports playlist"| node_playlistroute
node_playlistroute -->|"fetches playlist"| node_spotify
node_musicapi -.->|"requests audio"| node_streamroute
node_audio -.->|"loads preview"| node_streamroute
node_musicapi -.->|"requests artwork"| node_artworkroute
node_artworkroute -->|"resolves artwork"| node_spotifyart
node_spotifyart -->|"fetches artwork"| node_spotify
node_gameplay -.->|"checks updates"| node_announcements
node_queue -->|"selects catalog songs"| node_catalogbrowse
node_catalogbrowse -->|"reads snapshot"| node_store
node_refreshroute -->|"triggers refresh"| node_refresh
node_refresh -->|"imports sources"| node_sources
node_refresh -->|"enriches metadata"| node_enrichment
node_refresh -->|"reconciles catalog"| node_reconcile
node_refresh -->|"publishes snapshot"| node_store
node_sources -.->|"discovers candidates"| node_spotify
node_enrichment -.->|"resolves metadata"| node_itunes
node_store -.->|"persists catalog"| node_supabase
node_coverage -->|"analyzes snapshot"| node_store
node_refreshroute -.->|"uses shared lease"| node_supabase

click node_page "https://github.com/karaagexc/songless/blob/master/pages/index.tsx"
click node_gameplay "https://github.com/karaagexc/songless/blob/master/src/App.tsx"
click node_controls "https://github.com/karaagexc/songless/blob/master/src/components/BlitzSetupModal.tsx"
click node_filters "https://github.com/karaagexc/songless/blob/master/src/components/DecadeGenreModal.tsx"
click node_artist "https://github.com/karaagexc/songless/blob/master/src/components/ArtistPickerModal.tsx"
click node_playlistui "https://github.com/karaagexc/songless/blob/master/src/components/PlaylistImportPanel.tsx"
click node_results "https://github.com/karaagexc/songless/blob/master/src/components/StatsModal.tsx"
click node_onboarding "https://github.com/karaagexc/songless/blob/master/src/components/FirstTimeGuide.tsx"
click node_musicapi "https://github.com/karaagexc/songless/blob/master/src/services/musicApi.ts"
click node_queue "https://github.com/karaagexc/songless/blob/master/src/services/catalogQueue.ts"
click node_audio "https://github.com/karaagexc/songless/blob/master/src/services/audioService.ts"
click node_history "https://github.com/karaagexc/songless/blob/master/src/services/statsService.ts"
click node_playlist "https://github.com/karaagexc/songless/blob/master/src/services/playlistService.ts"
click node_sharing "https://github.com/karaagexc/songless/blob/master/src/utils/shareSignal.ts"
click node_identity "https://github.com/karaagexc/songless/blob/master/src/utils/songIdentity.ts"
click node_searchroute "https://github.com/karaagexc/songless/blob/master/pages/api/search.ts"
click node_artworkroute "https://github.com/karaagexc/songless/blob/master/pages/api/artwork.ts"
click node_streamroute "https://github.com/karaagexc/songless/blob/master/pages/api/stream.ts"
click node_playlistroute "https://github.com/karaagexc/songless/blob/master/pages/api/playlist.ts"
click node_announcements "https://github.com/karaagexc/songless/blob/master/pages/api/announcements.ts"
click node_catalogbrowse "https://github.com/karaagexc/songless/blob/master/server/catalogBrowse.ts"
click node_musiccatalog "https://github.com/karaagexc/songless/blob/master/server/musicCatalog.ts"
click node_spotifyart "https://github.com/karaagexc/songless/blob/master/server/spotifyArtwork.ts"
click node_store "https://github.com/karaagexc/songless/blob/master/server/catalogStore.ts"
click node_sources "https://github.com/karaagexc/songless/blob/master/server/catalogSources.ts"
click node_enrichment "https://github.com/karaagexc/songless/blob/master/server/catalogEnrichment.ts"
click node_reconcile "https://github.com/karaagexc/songless/blob/master/server/catalogReconcile.ts"
click node_refresh "https://github.com/karaagexc/songless/blob/master/server/catalogDailyRefresh.ts"
click node_coverage "https://github.com/karaagexc/songless/blob/master/server/catalogCoverage.ts"
click node_refreshroute "https://github.com/karaagexc/songless/blob/master/pages/api/catalog-refresh.ts"

classDef toneNeutral fill:#f8fafc,stroke:#334155,stroke-width:1.5px,color:#0f172a
classDef toneBlue fill:#dbeafe,stroke:#2563eb,stroke-width:1.5px,color:#172554
classDef toneAmber fill:#fef3c7,stroke:#d97706,stroke-width:1.5px,color:#78350f
classDef toneMint fill:#dcfce7,stroke:#16a34a,stroke-width:1.5px,color:#14532d
classDef toneRose fill:#ffe4e6,stroke:#e11d48,stroke-width:1.5px,color:#881337
classDef toneIndigo fill:#e0e7ff,stroke:#4f46e5,stroke-width:1.5px,color:#312e81
classDef toneTeal fill:#ccfbf1,stroke:#0f766e,stroke-width:1.5px,color:#134e4a
class node_page,node_gameplay,node_controls,node_filters,node_artist,node_playlistui,node_results,node_onboarding toneBlue
class node_musicapi,node_queue,node_audio,node_history,node_playlist,node_sharing,node_identity,node_supabase toneAmber
class node_searchroute,node_artworkroute,node_streamroute,node_playlistroute,node_announcements,node_musiccatalog,node_spotifyart,node_refreshroute toneMint
class node_catalogbrowse,node_store,node_sources,node_enrichment,node_reconcile,node_refresh,node_coverage toneRose
class node_player,node_spotify,node_itunes toneIndigo
```

## Components

### Game experience

- [Game page](https://github.com/karaagexc/songless/blob/master/pages/index.tsx): `pages/index.tsx`
- [Game orchestration](https://github.com/karaagexc/songless/blob/master/src/App.tsx): `src/App.tsx`
- [Game UI](https://github.com/karaagexc/songless/blob/master/src/components/BlitzSetupModal.tsx): `src/components/BlitzSetupModal.tsx`
- [Era and genre](https://github.com/karaagexc/songless/blob/master/src/components/DecadeGenreModal.tsx): `src/components/DecadeGenreModal.tsx`
- [Artist picker](https://github.com/karaagexc/songless/blob/master/src/components/ArtistPickerModal.tsx): `src/components/ArtistPickerModal.tsx`
- [Playlist import UI](https://github.com/karaagexc/songless/blob/master/src/components/PlaylistImportPanel.tsx): `src/components/PlaylistImportPanel.tsx`
- [Results and stats UI](https://github.com/karaagexc/songless/blob/master/src/components/StatsModal.tsx): `src/components/StatsModal.tsx`
- [First-time guide](https://github.com/karaagexc/songless/blob/master/src/components/FirstTimeGuide.tsx): `src/components/FirstTimeGuide.tsx`

### Browser services

- [Music API client](https://github.com/karaagexc/songless/blob/master/src/services/musicApi.ts): `src/services/musicApi.ts`
- [Catalog queue](https://github.com/karaagexc/songless/blob/master/src/services/catalogQueue.ts): `src/services/catalogQueue.ts`
- [Audio playback](https://github.com/karaagexc/songless/blob/master/src/services/audioService.ts): `src/services/audioService.ts`
- [Local stats and history](https://github.com/karaagexc/songless/blob/master/src/services/statsService.ts): `src/services/statsService.ts`
- [Playlist sessions](https://github.com/karaagexc/songless/blob/master/src/services/playlistService.ts): `src/services/playlistService.ts`
- [PNG sharing](https://github.com/karaagexc/songless/blob/master/src/utils/shareSignal.ts): `src/utils/shareSignal.ts`
- [Song identity](https://github.com/karaagexc/songless/blob/master/src/utils/songIdentity.ts): `src/utils/songIdentity.ts`

### API and providers

- [Search route](https://github.com/karaagexc/songless/blob/master/pages/api/search.ts): `pages/api/search.ts`
- [Artwork route](https://github.com/karaagexc/songless/blob/master/pages/api/artwork.ts): `pages/api/artwork.ts`
- [Audio stream route](https://github.com/karaagexc/songless/blob/master/pages/api/stream.ts): `pages/api/stream.ts`
- [Playlist route](https://github.com/karaagexc/songless/blob/master/pages/api/playlist.ts): `pages/api/playlist.ts`
- [Announcement route](https://github.com/karaagexc/songless/blob/master/pages/api/announcements.ts): `pages/api/announcements.ts`
- [Provider catalog](https://github.com/karaagexc/songless/blob/master/server/musicCatalog.ts): `server/musicCatalog.ts`
- [Spotify artwork](https://github.com/karaagexc/songless/blob/master/server/spotifyArtwork.ts): `server/spotifyArtwork.ts`
- [Refresh route](https://github.com/karaagexc/songless/blob/master/pages/api/catalog-refresh.ts): `pages/api/catalog-refresh.ts`

### Shared catalog

- [Catalog browse](https://github.com/karaagexc/songless/blob/master/server/catalogBrowse.ts): `server/catalogBrowse.ts`
- [Catalog storage](https://github.com/karaagexc/songless/blob/master/server/catalogStore.ts): `server/catalogStore.ts`
- [Catalog sources](https://github.com/karaagexc/songless/blob/master/server/catalogSources.ts): `server/catalogSources.ts`
- [Catalog enrichment](https://github.com/karaagexc/songless/blob/master/server/catalogEnrichment.ts): `server/catalogEnrichment.ts`
- [Catalog reconciliation](https://github.com/karaagexc/songless/blob/master/server/catalogReconcile.ts): `server/catalogReconcile.ts`
- [Daily catalog refresh](https://github.com/karaagexc/songless/blob/master/server/catalogDailyRefresh.ts): `server/catalogDailyRefresh.ts`
- [Catalog coverage](https://github.com/karaagexc/songless/blob/master/server/catalogCoverage.ts): `server/catalogCoverage.ts`

### Other components

- **Player**
- **Supabase**
- **Spotify**
- **iTunes**

## Connections

- Player → Game page: opens
- Game page → Game orchestration: loads client-only
- Player → Game orchestration: plays and guesses
- Game orchestration → Catalog queue: requests candidate
- Game orchestration → Music API client: requests songs
- Music API client → Catalog browse: browses
- Game orchestration → Audio playback: controls playback
- Game orchestration → Local stats and history: records results
- Game orchestration → Playlist sessions: saves session
- Game orchestration → PNG sharing: creates share image
- Game orchestration → Game UI: renders
- Game orchestration → Era and genre: renders
- Game orchestration → Artist picker: renders
- Game orchestration → Playlist import UI: renders
- Game orchestration → Results and stats UI: renders
- Game orchestration → First-time guide: renders
- Game orchestration → Song identity: matches tracks
- Local stats and history → Song identity: canonicalizes
- Playlist sessions → Song identity: deduplicates tracks
- Music API client → Search route: requests search
- Search route → Provider catalog: calls search
- Provider catalog → iTunes: queries provider
- Playlist sessions → Playlist route: imports playlist
- Playlist route → Spotify: fetches playlist
- Music API client → Audio stream route: requests audio
- Audio playback → Audio stream route: loads preview
- Music API client → Artwork route: requests artwork
- Artwork route → Spotify artwork: resolves artwork
- Spotify artwork → Spotify: fetches artwork
- Game orchestration → Announcement route: checks updates
- Catalog queue → Catalog browse: selects catalog songs
- Catalog browse → Catalog storage: reads snapshot
- Refresh route → Daily catalog refresh: triggers refresh
- Daily catalog refresh → Catalog sources: imports sources
- Daily catalog refresh → Catalog enrichment: enriches metadata
- Daily catalog refresh → Catalog reconciliation: reconciles catalog
- Daily catalog refresh → Catalog storage: publishes snapshot
- Catalog sources → Spotify: discovers candidates
- Catalog enrichment → iTunes: resolves metadata
- Catalog storage → Supabase: persists catalog
- Catalog coverage → Catalog storage: analyzes snapshot
- Refresh route → Supabase: uses shared lease
