No description
- TypeScript 91.6%
- CSS 4.5%
- Shell 2.2%
- Nix 0.7%
- Just 0.5%
- Other 0.5%
| data/config | ||
| docs | ||
| frontend/lightsync | ||
| scripts | ||
| .dockerignore | ||
| .envrc | ||
| .gitignore | ||
| biome.json | ||
| compose.yaml | ||
| Dockerfile | ||
| flake.lock | ||
| flake.nix | ||
| justfile | ||
| pnpm-lock.yaml | ||
| pnpm-workspace.yaml | ||
| README.md | ||
LightSync
A browser app that listens to music, finds the beat, and sends a cheap sound-to-light board one clean pulse per beat on its AUX input. The board stays in its music mode and steps its chase on each pulse.
How
git clone https://codeberg.org/buckfae/LigthSync.git && cd LigthSyncdocker compose up -d. The first start builds the image and needs internet.- Open http://localhost:8080
Why
- A sound-to-light board detects beats with a bass-energy threshold. That works on dry four-on-the-floor kicks. It fails on rock and metal, because distorted bass and guitars keep the low band full all the time.
- LightSync does the beat detection in software (a predictive beat tracker) and gives the board the easiest possible input: a short low tone on each beat, silence in between.
- On the synthetic test songs, a model of such a board scores F = 0.48 on raw
rock and 0.15 on raw metal. Fed with the LightSync signal it scores 1.00 and
0.99 (
just eval).
flowchart LR
src["Music source<br/>AUX / USB / mic / tab / system audio / files"] --> ls["LightSync<br/>beat tracker + pulse synth"]
ls -- "pulses only" --> board["Light board AUX in"]
ls -. "optional: music, monitor mix" .-> pa["PA / headphones"]
Use it
- Chrome or Edge only: only Chromium has system audio capture and output
device selection (
AudioContext.setSinkId). - Open the app on
localhostonly. Chromium allows audio capture only in a secure context, and a LAN address is not one. So the container listens on loopback only. - The Setup guide button lists the setups for a venue: line in from a mixer, Spotify on the laptop, one jack for PA and board, room microphone, headphone monitor.
- Put the board route first (lowest latency).
- The browser keeps the settings per address. Settings made on
just dev(port 5173) do not show on port 8080, so calibrate on the address you use at the venue. docker compose up -dneeds no internet once the image exists.docker compose up -d --buildalways needs internet, also when nothing changed. So after agit pull, rebuild before you leave.- The container restarts with Docker, for example after a reboot.
docker compose downstops it.
Keys
| Key | Action |
|---|---|
| Space | tap tempo (in tap mode it drives the clock, in beat mode 3+ taps set the tempo) |
| M | mute the signal |
| T | test pulse |
| L | lock the tempo |
| D / H | tempo × 2 / ÷ 2 |
| P | shift the phase of "every 2nd / 4th beat" |
Detection modes
| Mode | For | Latency |
|---|---|---|
| Beat tracker (default) | rock, pop, metal, dance | predictive: can fire early with a negative offset |
| Kick | songs with a clear kick | reactive, ~30 ms late |
| Onsets | accents, stops | reactive, busy |
| Tap | anything, as a fallback | predictive |
Presets set the tempo range and band weights per genre (Rock / Pop, Metal / Punk, Dance, Ballad).
Commands
For development, enter the dev shell with nix develop (or direnv allow),
then run just install.
| Recipe | Does |
|---|---|
just dev |
Vite dev server at http://localhost:5173 |
just build |
static build |
just serve |
serve the static build |
just check |
biome, tsc, Vitest (the gate) |
just e2e |
Playwright, headless, with fake audio devices |
just eval |
F-measure table: every mode on every synthetic song |
How it works
frontend/lightsync/src/dsp/— the signal chain, pure TypeScript. It runs in an AudioWorklet in the app, and in Node for the tests andjust eval.- Spectral flux in three bands, each band normalized by its running mean.
- Beat tracker after BTrack (Stark 2011): cumulative beat strength signal, comb filter tempo estimate with a Viterbi tempo transition, and a phase check that pulls the tracker off the offbeat.
- Pulse synth, silence gate, divider, offset, and a model of a cheap board for the dashboard.
src/audio/— the Web Audio graph: sources, the worklet, one chain per output route. Extra output devices get their own AudioContext.src/demo/— synthetic songs with known beats (rock, metal, half-time, ballad, tempo change, break). The tests and the Demo source use them.docs/agent-notes/2026-09-25-lightsync/— problem, research, design and test concept.