Ambient lighting controller for a USB-connected RGB LED strip mounted behind a monitor.
The strip shipped with a bundled vendor application that was slow, unreliable, and offered almost no control over how the lights behaved. There were no public source files, no SDK, and no protocol documentation - just a closed binary talking to the device over USB HID.
I reverse-engineered the device's HID protocol by capturing and analyzing the USB traffic between the original app and the strip. From that I recovered the command set:
- per-section RGB writes (
0x86) with a packed full-frame variant for low-latency updates, - global brightness (
0x87), - animation speed (
0x8a), - report-id
0framing with asum % 256checksum.
With the protocol understood, I wrote a replacement from scratch.
A native controller in two parts:
daemon/- a Rust background service that captures the screen, samples the audio output, and drives the LED strip over HID.app/- an Electron tray UI for choosing modes and tuning parameters. The UI writes aconfig.json; the daemon watches it and applies changes live.
- Ambient - averages edge regions of the captured screen onto the strip (bias lighting).
- Music - reacts to system audio: WASAPI loopback capture feeds an FFT, and the spectrum drives color and intensity across selectable palettes.
- Fusion - combines screen ambient with audio reactivity.
- Solid - a single static color.
- Screen capture uses Windows.Graphics.Capture (WGC), which keeps working through full-screen games where Desktop Duplication / DXGI tends to drop out.
- Audio is captured via WASAPI loopback and transformed with rustfft to get the live frequency spectrum.
- Configurable capture FPS, brightness floor, and a power limit to cap total current draw.
- Daemon: Rust -
windows-capture(WGC),wasapi,rustfft,hidapi,tray-icon,winreg,serde. - UI: Electron (
main.js+preload.js+ a vanilla HTML/CSS/JS renderer). - Platform: Windows.
Daemon:
cd daemon
cargo build --releaseUI (development):
cd app
npm install
npm startPackage the installer (bundles the release daemon as an extra resource):
cd app
npm run distThe runtime config lives at %APPDATA%\lumen\config.json; daemon logs to %TEMP%\lumen.log.