A self-hosted, hardware-powered music quiz game. Spotify songs stream to your speakers, players smash physical buzzers, and a live scoreboard runs on any screen in the room.
A real-life music quiz game where players buzz in with physical controllers to guess songs as fast as possible.
Each player gets a physical buzzer and joins the game by scanning a QR code shown on the TV, where they can assign themselves to one of the available devices. At the start of each round, one player acts as the judge and sees the upcoming track on their buzzerβs OLED display. Once ready, the judge starts the round and the song begins playing. The other players have to guess the song title and artist as fast as possible. As soon as someone thinks they know it, they hit their buzzer, which immediately stops the music. The player gives their answer, and the judge decides: if itβs wrong, they press the red button, the round continues, and that player is locked out for the rest of the round. If the answer is correct, the round ends and the player earns a point. Iβve tested this with up to 8 people multiple times, and it consistently turns into a really fun party game since it combines music with competitive gameplay.
A FastAPI server runs on any machine in your local network and streams music via the Spotify API. Each player holds a custom-built ESP32 buzzer unit. When a song plays, the first one to hit the big button locks in their buzz β the judge confirms right or wrong, points get awarded, and the chaos continues.
The game host controls everything from a browser: playlist management, lobby, playback, and scoring. Players join from their phone by scanning the QR code or navigating to the server IP.
SongBuzz/
βββ main.py # FastAPI entry point
βββ requirements.txt
βββ backend/
β βββ game_manager.py # Core game state machine + WebSocket logic
β βββ buzzer_manager.py # Buzzer registration + UDP discovery
β βββ spotify_playlist.py # Smart song queue with balanced playlist distribution
β βββ SpotifyPlayer.py # Spotify playback control via Spotipy
β βββ credintals_template.py # β copy to credintals.py and fill in your keys
βββ frontend/
β βββ index.html
β βββ app.js # Vue 3 app, routing, WebSocket client
β βββ components/
β βββ HomeScreen.js # Host dashboard
β βββ LobbyScreen.js # Player list + playlist pool manager
β βββ PlaybackScreen.js # Live playback + buzz timer
β βββ JudgeScreen.js # Judge view (correct / wrong buttons)
β βββ GameScreen.js # Player view during game
β βββ SongRevealScreen.js # Song reveal after round
β βββ WinScreen.js # End screen with winner
β βββ JoinScreen.js # Player join form (phone-facing)
βββ assets/
β βββ playlists/playlists.json # Persisted playlist pool
β βββ uploads/ # Player avatar images
β βββ winsounds/ # MP3s played on round win
βββ Hardware/
βββ Pinout # Pin reference file
βββ SongBuzz/SongBuzz.ino # Arduino firmware for the buzzer units
βββ 3D_Files/ # Printable enclosure parts (.stl / .3mf)
- Python 3.11+
- A Spotify Premium account
- A Spotify Developer App (for API credentials)
# Clone the repo
git clone https://github.com/yourname/SongBuzz.git
cd SongBuzz
# Create and activate a virtual environment
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
# Install dependencies
pip install -r requirements.txtCopy the credentials template and fill in your app details:
cp backend/credintals_template.py backend/credintals.pyThen open backend/credintals.py and replace client_id and client_secret with your values from the Spotify Developer Dashboard. Make sure the redirect URI http://127.0.0.1:8888/callback is registered in your app settings.
python -m uvicorn main:app --host 0.0.0.0 --port 8000 --reloadThen open http://localhost:8000 in a browser on the host machine. Players connect via http://<your-local-ip>:8000/join from their phones.
Each buzzer is a self-contained ESP32-based device with a display, RGB LEDs, and three buttons. It connects automatically to the game server over WiFi via UDP broadcast discovery β no manual IP configuration needed.
| Component | Details |
|---|---|
| Microcontroller | ESP32 (any dev board with sufficient GPIO) |
| Display | SSD1306 128Γ64 OLED (I2C) |
| LEDs | NeoPixel strip, 8 LEDs |
| Buttons | 3Γ momentary pushbutton (LEFT, BIG, RIGHT) |
GPIO 2 β BIG button (the main buzz button)
GPIO 5 β LEFT button
GPIO 6 β RIGHT button
GPIO 3 β NeoPixel data line (8 LEDs)
GPIO 8 β I2C SCL (OLED display)
GPIO 9 β I2C SDA (OLED display)
OLED I2C address: 0x3C
OLED resolution: 128 Γ 64 px
All buttons are wired between the GPIO pin and GND, using internal INPUT_PULLUP. No external resistors needed.
ESP32 OLED SSD1306
GPIO 8 (SCL) βββββββββ SCL
GPIO 9 (SDA) βββββββββ SDA
3.3V βββββββββ VCC
GND βββββββββ GND
ESP32 NeoPixel Strip
GPIO 3 βββββββββ DIN
5V βββββββββ VCC
GND βββββββββ GND
ESP32 Buttons (Γ3)
GPIO 2 ββ [BIG BTN] ββ GND
GPIO 5 ββ [LEFT BTN] β GND
GPIO 6 ββ [RIGHT BTN]β GND
Install these via the Arduino Library Manager:
WebSocketsby Markus SattlerArduinoJsonby Benoit BlanchonAdafruit GFX LibraryAdafruit SSD1306Adafruit NeoPixel
Original buzzer design (used as a starting point): https://www.thingiverse.com/thing:6755106/files
Parts used (*Affiliate Links β I earn a small commission if you buy through these links):
*PLA Translucent: https://amzn.to/4lZ9CcU
*OLED Display: https://amzn.to/484HW0s
*ESP32 C3 Super Mini: https://amzn.to/48lJ9R8
*LED Ring: https://amzn.to/4cfy5Ye
Open Hardware/SongBuzz/SongBuzz.ino and set your WiFi credentials:
const char* WIFI_SSID = "YourNetwork";
const char* WIFI_PASSWORD = "YourPassword";Flash to the ESP32 via Arduino IDE (board: ESP32 Dev Module). The buzzer will automatically find the server on the local network β no IP address needed.
On startup the server broadcasts SONGBUZZ_SERVER packets via UDP to port 8888 every 3 seconds. When a buzzer receives this packet, it connects to the server via WebSocket at ws://<server-ip>:8000/ws/buzzer/<MAC>. The MAC address serves as the unique buzzer identifier.
The OLED gives the player feedback throughout the game β connecting status, their assigned name, whether they successfully buzzed, and whether their answer was correct or wrong. The server can push arbitrary text and control the NeoPixels (color, brightness) via WebSocket commands.
The Hardware/3D_Files/ folder contains all printable parts:
| File | Description |
|---|---|
Base.stl |
Main enclosure body bottom - PLA |
Cover.stl |
Top cover - PLA |
CoverLid.stl |
Front Lid for cable access and Display - PLA |
Button_Translucent.3mf |
Big buzz button (print in translucent PLA for LED glow) |
Keycaps.3mf |
LEFT / RIGHT button caps - PLA |
Slider_Translucent.3mf |
Slider vor Big Buzz Button - PLA Translucent |
Spring_PETG.stl |
Button return spring - PETG!! |
Stecken.3mf |
Peg for MX Switch of Big Buzzer - PLA |
- Start the server and open the host dashboard in a browser
- Add Spotify playlists to the pool via the Lobby screen (paste URL or URI, optionally give it a custom name)
- Players join from their phones and pick up their buzzer units
- The host starts the game β a random song from the pool begins playing on the active Spotify device
- Players buzz by hitting the big button; the first buzz locks everyone else out
- The judge (via the Judge view) confirms correct or wrong β wrong answers eliminate the player for that round
- Points are awarded and the next round begins
- At the end, the Win Screen shows the final standings with a celebration sound
SongBuzz uses a pre-planned queue rather than picking songs randomly on the fly. Before each round, the queue is built by:
- Grouping all available tracks by source playlist
- Separating tracks into "fresh" (not yet played this evening) and "already played" per playlist
- Shuffling each group independently (fresh tracks always come first)
- Interleaving all playlists in round-robin order, shuffling each group of one-per-playlist
This ensures balanced representation across all playlists while keeping the order unpredictable. Songs played earlier in the evening automatically sink to the back of the queue even across multiple rounds, so repetition is minimized without being completely eliminated.
For testing without hardware, virtual_buzzers.py simulates buzzer connections via the terminal. Run it separately alongside the server.
Β© 2025 sidelabsyt. This project is licensed under Creative Commons Attribution-NonCommercial 4.0 International (CC BY-NC 4.0).
You are free to: share, copy, modify, and build upon this project β as long as you give appropriate credit and do not use it for commercial purposes.