One giant arc. No cliffs.
Every track's BPM is detected up front, then the whole library is laid out as an arch: start at the median tempo, climb to 200 BPM, dive to 60, and glide back home. Each transition is a neighbour — never more than ~6 BPM apart — and the final track lands 2.5 BPM from the first, so the set loops forever without a seam. This is the real curve, drawn from the live playlist:
What's playing, always.
A Pillow renderer pulls each track's ID3 tags and embedded album art, wraps long titles, and streams the strip to FFmpeg as raw RGBA frames — the overlay flips to the next track mid-crossfade, right as the new song takes over.
Rendered live by overlay_renderer.py at 2 fps, composited by FFmpeg.
Four processes and a named pipe.
No DAW, no DJ software, no browser sources — just Python feeding FFmpeg through a FIFO, with every stage supervised and able to reconnect if its neighbour restarts.
ID3 tags + album art
44.1 kHz stereo
tee muxer, auto-reconnect
either can drop, the other stays live
Built to run unattended.
Beatmatched crossfades
BPM is detected via onset autocorrelation, the incoming track is time-stretched with
scipy, and the two are blended with an equal-power curve — 4 bars, like a real DJ.
Arch-shaped playlist
The library is pre-analysed once, then ordered as a rise-and-fall arch so tempo drifts, never jumps — including where the cycle wraps around.
A living overlay
Album art, wrapped titles and the Jo Luno crescent, re-rendered the instant the track flips.
Falls back to plain drawtext, then to no overlay, if anything's missing.
Self-healing pipeline
If the network drops, FFmpeg restarts and the playlist blocks on the pipe until it's back. If the playlist dies, the pipe disappears and the stream stops cleanly instead of hanging.
One encode, two platforms
A single x264 encode is tee'd to YouTube and Twitch with onfail=ignore —
one destination failing never takes down the other.
Zero-touch operations
A macOS LaunchAgent starts the stream at login and revives it after crashes. Every component logs to its own timestamped file.