Inspiration

Silent disco is one of my favourite hackathon events. I wanted to turn the badge into something you can dance and have more fun with. And this way you can use your own headphones making the event more affordable, easier to organize, and more comfortable. The badge has a ring of LEDs, buttons and Bluetooth, so I wanted to see whether it could be the light show while phones handled the sound.

What it does

Silent Disco turns the 2026 Hacker Badge into a silent disco light show. Attendees put on headphones, open a web page on their phone, and pick one of three channels. Each channel is a real music playlist. The badge's LEDs continuously breathe in that channel's color, pink, orange, or purple, so everyone can see at a glance what "station" someone's on, even though only they can hear it. Phones stay in sync with each other on exactly which track and which second of it is playing, so a room full of people on the same channel are genuinely listening together, not just running independent loops. And if you're on Android Chrome, you can tap "Follow my badge" to pair your phone with your badge over Bluetooth. From then on, pressing Left/Right on the badge switches your phone's channel, Up/Down controls its volume, and the A button mutes it, all without ever touching your phone. The badge runs ESP-IDF/FreeRTOS on the ESP32-C3, driving the WS2812B LEDs over RMT and a small LVGL UI on the ST7789 screen. The web app is a Node/Express server with a WebSocket-based master clock. Phones estimate their offset to the server using the same round-trip math NTP uses, then use that synced clock to compute exactly where they should be in their channel's playlist. Durations and titles are read automatically from whatever audio files I drop into a folder. Playback uses the Web Audio API with a persistent gain node per channel, so all three channels are always playing in the background. Switching is just a smooth crossfade, never a restart. "Follow my badge" runs a tiny custom BLE GATT service on the badge, with Web Bluetooth on the phone side. This meant carefully re-tuning NimBLE's memory footprint. I read through ESP-IDF's actual Kconfig source to justify every buffer size instead of guessing, since a real BLE connection needs a very different memory shape than the simple broadcast approach I started with.

Challenges I ran into

The badge has zero audio hardware, so the whole architecture has to treat the badge, which handles the light, and the phone, which handles the sound, as two independent systems that never talk to each other by default. That shaped almost every design decision. Getting Web Bluetooth to actually discover the badge took some real debugging. Chrome's device picker only matches service UUIDs that are present in the advertisement itself, and a 128-bit UUID doesn't fit in a legacy BLE advertisement packet alongside a device name, so I had to split the advertising and scan-response payloads. Memory was tight too. LVGL plus a live BLE connection pushed the badge close to its flash and RAM limits, so I had to trim NimBLE's security stack, connection buffers, and MTU by hand, verified against the actual SDK Kconfig rather than through trial and error.

What's Next

Adding the full three-channel experience with lookahead patterns for builds, drops and breakdowns. Multiple beacons around a large room, all driven from the same server clock, to extend coverage. Letting Android users optionally sync their music channel to their badge's channel with Web Bluetooth. Live DJ sets, with the LEDs delayed to match the stream's latency.

Built With

Share this project:

Updates

Submission history