Open-source experiment

I gave an AI a pair of turntables.

Not a music generator. It plays real songs and performs them with real DJ moves: scratches, loop rolls, spinbacks, crossfader chops. Every move is a tool call, decided live by Jev in about 150 milliseconds.

Source code
Justin Bieber – Baby × Jay Sean – Down · performed by Jev
141ms
median decision, this set
0
missed beats in 10 live calls
12
DJ moves · 6 scratch styles
2bars
lookahead, or it misses the move
How it works

Code keeps the beat.
The model makes the calls.

Coding agents get tools to read and edit files. This one gets tools to work the decks. A fast model answers one typed question per phrase, and the engine turns the answer into sound on the next downbeat.

01 · PREP

Read the records

Demucs splits each song into drums, bass, vocals and other. beat-this finds every beat and downbeat. Whisper finds the hook and the exact word to scratch.

02 · DECIDE

Ask Jev

Every couple of bars, one call with four typed questions: move, next song, scratch style, hype. Answers come back with probabilities in ~150 ms.

03 · PERFORM

Work the decks

A bar-by-bar engine: beat-locked time-stretch, key shifting, a scratch synth that models the platter and crossfader, EQ kills, filters, echo, reverb.

04 · SHOW

Draw the hands

Every platter wobble, knob turn and fingertip in the video comes from what the engine actually did, recorded 30 times a second.

The toolbox

Twelve moves, one decision at a time

Code decides which moves are legal right now (phrase position, what just happened, what's playing). Jev picks one. If the answer comes back later than the two-bar window, the DJ misses the move.

deck.scratch_in(word, style)

Scratch the next song's hook over the beat, then slam it in on the 1.

mixer.crossfader_chops()

Flip between both decks every half beat, landing on the new hook.

deck.roll(1 → 1/8)

A loop roll that shrinks with a rising filter and noise, into the drop.

deck.spinback()

Rewind the record backwards, then drop the next song.

deck.brake()

Slow the platter to a stop, like cutting power to the turntable.

fx.echo_out()

Throw the last beat into a tempo-synced echo and cut it clean.

mixer.drop_gap()

One beat of silence, then the next song hits with a sub boom.

deck.scratch_fill(style)

A flashy scratch on the current hook to close a phrase.

mixer.filter_dip() · fx.echo_throw()

Small accents inside a phrase, or ride: let it play.

Sets

Same decks, different brains

Any model can sit at the decks. LLMs are smart but slow: at ~1 s a decision, they sometimes miss the window. Jev is built for exactly this kind of fast, narrow call.

Not Like Us × Get Low

Jev · 140 ms median · 0 late · same key, same 101 BPM

One More Time × Espresso · Jev

Jev · 168 ms median · 0 late of 10

One More Time × Espresso · Claude

Claude Haiku 4.5 · 1.1 s median · 1 late: a 4.3 s call missed its move
Mac app

Decks, live on your Mac

A native, real-time version: two decks in Liquid Glass, Jev deciding every move live as the music plays. Load your own files, your Music library, Creative Commons tracks, or find a song by name or Spotify link.

Download Decks for macOS ↓

macOS 26 or later, Apple Silicon or Intel. 2 MB. Nothing else to install: no API key, no Homebrew. Jev works out of the box, and the song finder sets itself up on first launch.

First launch: the app isn't notarized yet, so right-click Decks → Open → Open. You only need to do this once.

# or build it from source
$ brew install xcodegen
$ git clone https://github.com/yask123/ai-dj
$ cd ai-dj/mac && xcodegen
$ open Decks.xcodeproj   # ⌘R
Open source

Put it on your own decks

# macOS or Linux, Python 3.12
$ git clone https://github.com/yask123/ai-dj && cd ai-dj
$ brew install ffmpeg rubberband yt-dlp
$ uv venv --python 3.12 .venv
$ uv pip install -r requirements.txt
$ echo "sk-or-…" > .openrouter_key
$ .venv/bin/python server.py
# → localhost:8765 · pick two songs, pick a brain, drop it

One OpenRouter key covers Jev (typesafe/jev-1.13) and every LLM you want to race it against. A set costs a fraction of a cent.

First-time songs take ~45 s on an Apple Silicon Mac: download, stems, beat grid, hook detection. After that they're cached and a new set takes seconds.

The live booth (/dj/booth) is the same app, running on my Mac. It's open when the Mac is awake.

View on GitHub ↗