Personal Project · August 2026
DJNext
A next-track suggester for DJing off TIDAL. Paste a playlist link, every track gets a Camelot key and BPM, and whatever is playing ranks the rest of the playlist by how well it mixes.
About DJNext
Mixing live means answering the same question every four minutes: of the couple hundred tracks in the playlist, which ones will actually beatmatch and stay in key with what is playing right now? Rekordbox and Serato answer it for files on a hard drive. My library lives in TIDAL, so I was doing it from memory. DJNext does the bookkeeping instead: pick the track you are playing and the rest of the playlist re-ranks around it, with a set timeline showing where you have been and an up-next slot previewing where you could go.
How It Ranks
Every candidate gets one score from two parts: harmonic compatibility on the Camelot wheel (same key, relative major/minor, energy-boost diagonals) and tempo distance in percent, with half and double time treated as mixable. Tracks past an 8% stretch are hidden, played tracks sink, and a 60 BPM ballad sorts next to the 120 BPM tracks it actually mixes with.
Δ is the BPM difference in percent, taking the best of ×½, ×1, and ×2 time. w is the key weight, 0.6 by default. Played tracks score ×0.5.
Getting the Data
The hard part is that TIDAL shows BPM and key in its own apps but does not give them to third-party developers. So DJNext builds its own: music databases first, and where they come up short it downloads the track's 30 second preview clip and measures tempo and key from the audio itself, with essentia compiled to WebAssembly, running in your browser. A key is only written when two detection methods agree. Nothing is stored server-side: your playlist, results, and set history stay in your browser.
From Localhost to Here
Version one was a single Python file on localhost that read a CSV export of my playlist. It grew signal analysis, then a real connection to the TIDAL API, and then it made more sense as something anyone could open. The ranking logic on this site is the same code, ported line for line.

