Play Hitster with Tidal instead of Spotify.
Hitster is a music guessing card game where each card has a QR code that normally opens a Spotify track. This project replaces Spotify entirely — scan the card with your phone, and the song streams instantly through your Tidal subscription, right in the browser. No Spotify account needed.
This is an unofficial fan project and is not affiliated with, endorsed by, or connected to Hitster or its creators in any way.
Phone camera → QR scan (browser JS)
↓
Flask server (Python)
↓
CSV lookup (songseeker repo)
↓
Tidal search / cache lookup
↓
stream URL → <audio> in browser
The image is built directly from this GitHub repository — no manual cloning needed.
# 1. Copy docker-compose.yml to your server, then:
docker compose up --build -d
# 2. Open to complete the one-time Tidal login
open https://192.168.x.x:6001
# Accept the certificate warning: Advanced → Proceed
# 3. Click "Log in to Tidal", authenticate, done.
# The token is saved to the volume and reused on every restart.
# 4. have fun!docker-compose.yml
See docker-compose.yml file. To update to the latest version:
docker compose build --no-cache && docker compose up -dpip install -r requirements.txt
python app.py
# Data (token, certs, cache) is stored in ./data/ next to app.pyThe app fetches playlists.csv from the songseeker-hitster-playlists community repo at startup. This lists all available Hitster editions (DE, EN, FR, …). Select your edition from the dropdown — the corresponding card CSV is fetched and caching begins automatically.
Click Start Scanner on your phone. The browser accesses the camera directly (no app install). Point it at any Hitster card QR code and the song lookup starts immediately. Supports both normal and inverted QR codes.
The song title and artist are never shown in the browser — only logged on the server side for the game host to see. The browser shows a countdown, then starts playing. This is a guessing game after all.
Searching Tidal by text (artist + title) takes ~0.5 s per card and can be unreliable for songs with ambiguous names. To fix this, the app pre-builds a Tidal ID cache the first time you load a game edition:
data/cache/<edition>-tidal-cache.csv
Card#,TidalID
1,12345678
2,87654321
3, ← NOT FOUND (falls back to live search at play time)
Build process:
- Select a game in the UI
- The card CSV is fetched from the songseeker repo
- A background thread works through every card: searches Tidal for
title + artist, picks the best match, records the Tidal track ID - Progress is shown live as a progress bar in the UI
- Cache is saved to disk — subsequent loads of the same edition are instant
At play time:
- Cache hit →
session.track(id)— fast and reliable - Cache miss → live Tidal search as fallback
Rebuilding the cache (e.g. after a CSV update):
# Docker
docker exec hitster-tidal rm /data/cache/<edition>-tidal-cache.csv
docker compose restart
# Local
rm data/cache/<edition>-tidal-cache.csvThe app uses Tidal's OAuth device flow. On first run a login URL appears in the browser UI. Click it, log in to Tidal in the new tab, and return — the token is cached to disk and reused automatically on every restart. You only need to log in once.
The app generates a self-signed TLS certificate on first run (HTTPS is required for camera access on mobile). Browsers will warn on first visit:
- Safari: Show Details → visit this website → Confirm
- Firefox: Advanced → Accept the Risk and Continue
- Chrome / Android: Advanced → Proceed
You only need to do this once per browser. On iOS, if the camera is still blocked after accepting, go to Settings → General → About → Certificate Trust Settings and enable full trust for the certificate.
| Variable | Default (Docker) | Default (local) | Description |
|---|---|---|---|
FLASK_PORT |
6001 |
6001 |
HTTPS port |
COUNTDOWN_SEC |
3 |
3 |
Seconds before playback starts (0 = instant) |
CACHE_DIR |
/data/cache |
./data/cache |
Tidal ID cache location |
TOKEN_FILE |
/data/tidal_token.json |
./data/tidal_token.json |
Tidal OAuth token |
PLAYLISTS_INDEX_URL |
(songseeker repo) | (songseeker repo) | Override playlists.csv URL |
- Card data: andygruber/songseeker-hitster-playlists
- Tidal API: EbbLabs/python-tidal
- QR decoding: cozmo/jsQR
- Created with claude.ai