Parity / Beyond
Every area carries a status. Works means the requirement is checked as shipped in the project's requirements file. Partial means part of it works, and the row says which part. Not yet means it is not built. The small gray line under each area names its requirement ids, for anyone who wants to check.
Statuses read from PARITY-TODO.md and .planning/REQUIREMENTS.md on main at 487fa677d7, Fri 2 Oct 2026. They will be re-read at the launch SHA.
Everything you already know.
The screen, the controls and the library you already play with, each with a status below.
Four-deck performance screen
The layout follows the rekordbox 7 Performance screen that working DJs already know: four decks around a central mixer, a waveform stack on top and the library browser underneath, running on your own Mac over your real library. It stays usable on a 1280x720 window without resizing anything.
PERF-UI-01
| Panel | What is in it |
|---|---|
| Waveform stack | Four scrolling rows: three bands, full-height beat and bar ticks from real analysis rows, phrase marks, drag to seek |
| Deck | Header, hot cues A to H, jog dial, loops, pitch fader, stem row, strip waveform, transport |
| Mixer | Trim, three-band EQ, filter, channel faders, crossfader with assign matrix, headphone cue |
| Browser | Playlist tree, track table with an inline preview strip, search, ratings |
Panel list from docs/architecture.md, Layout.
Beat sync and phase lock
BEAT SYNC and MASTER lock tempo and phase. The default mode, BAR, keeps beat 1 on beat 1. If the tempos are too far apart for the pitch range, it syncs at half or double time instead and shows an orange warning when it does. The master hands off automatically. Every beat drawn comes from a real analysis row, never an invented one.
DECKUX-14, DECKUX-17
{ type: 'beat_sync', deck: 2, enabled: false }
Illustration, drawn in HTML for this page. The line under it is the command shape the app's dispatcher accepts for that button.
Master tempo, quantize, hot cues and loops
Master Tempo keeps the key when you change the tempo, using the Signalsmith Stretch time-stretcher; if it fails, the deck stops with an error instead of playing on quietly wrong. Quantize snaps cue, seek and loops to the analyzed beat grid. Hot cues A to H jump. Saving and clearing them is built, and checking it on a real library is still open; saved cues stay in Open DJ's own copy and never reach rekordbox. Empty pads can show auto-cue proposals, clearly marked as proposals.
DECKUX-15, PARITY-TODO.md Working now and Partial tables
| Deck feature | Status | From |
|---|---|---|
| Quantize, beat loops, waveform seek | Works | PARITY-TODO |
| Hot-cue save and clear | Partial | PARITY-TODO |
| Hot-cue bank layout | Partial | PARITY-TODO |
| Key sync across decks | Partial | PR #237 |
| Slip mode | Partial | PR #237 |
Your library, and what it will not touch
In the app, first-run setup imports your rekordbox library, or a folder of music if you have no rekordbox. It makes a copy of rekordbox's library file and only ever reads that copy; the installed Mac app clears the write switch before its engine starts, so it will not write to your rekordbox library, a Pioneer share or a USB export. Its one library write is a djay playlist write-back, which runs only after you confirm it and backs up the playlist first. From a source checkout, writes are off by default; the one exception is undo of an earlier write you allowed. Serato (libraries from before Serato 4.0), Traktor and djay are read by command-line tools from a source checkout. Writers for Serato and Traktor files exist in the code but are tested on synthetic files only, with real-library sign-off deferred, so their status is Partial: do not run them against your real library. Folder import does not yet read tags in the packaged app (PARITY-15 open). Playlists, cues, beat grids, ratings and analysis can sync from rekordbox into djay, from the command line, with a dry run and a conflict report.
The installed Mac app clears the write switch before its engine starts, so it will not write to your rekordbox library, a Pioneer share or a USB export; its one library write is a djay playlist write-back, which runs only after you confirm it and backs up the playlist first. From a source checkout, writing to the live rekordbox database, the Pioneer share or a USB export is off by default. Every code path that could do it is listed in one inventory and refuses while the switch is off. The one deliberate exception is undo: it does write to the live database without the switch, but it can only replay a backup that an earlier, allowed write made, so it cannot be pointed anywhere new. rekordbox and djay writes go through the safety rails: typed confirmation, a running-app check, a backup, a dry run by default, an atomic write, and a read-back with a reversal script; rekordbox writeback is off by default, cleared in the installed app. The Traktor writer has only a dry run and two explicit flags, with no running-app check, backup, atomic write or read-back.
SETUP-01, SETUP-03, OPEN-02a to OPEN-02d, SYNC-01 to SYNC-06, SYNC-ONEWAY-01
| App | Format the adapter handles |
|---|---|
| rekordbox | master.db, copied and then read; rekordbox encrypts it, so the copy is decrypted with pyrekordbox |
| djay Pro | TSAF library |
| Serato DJ Pro | Markers2, BeatGrid, Database V2 (libraries from before Serato 4.0), subcrates |
| Traktor | NML |
Mixer, headphone cue and a library preview
Trim, three-band EQ, filter, channel faders and a crossfader with an assign matrix. Headphone cue runs in practice mode or split-cable mode (a mono room feed through a splitter cable); cue on a second output device is not in the Mac app yet (CUEOUT-22 is open). Click any mini-waveform in the library and that track plays from that point through your headphone cue: a separate preview, not a deck, that never reaches the master bus; in single-output practice mode the cue, preview included, is blended into your main output by the MIX dial.
CUEOUT-01, CUEOUT-02, CUEOUT-15
Preview in 28 ms, after a half-second hover
A cold click on a library preview took 743 ms to start, and most of that wait was decoding, not transfer. Resting the pointer on a row for half a second now decodes it in the background, through the same byte-capped cache the rest of the app already uses, so the click that follows starts in 28 ms. That is one measured run, and the first preview of a session still pays the decode.
Instant controls also have budgets, which are targets rather than measured results: 16 ms to visual feedback and 30 ms p99 to the audible change. The app records input-to-applied time so the budgets can be checked.
CUEOUT-15, LATENCY-01, LATENCY-03, BLOG-NOTES.md
MIDI controllers: Chrome and Edge only, for now
In this alpha the Mac app cannot use a MIDI controller. It is built on macOS's own web view, which has no WebMIDI, so its MIDI panel says MIDI is not supported there. Controllers work today only when you run Open DJ from source and open it in Chrome or Edge (see Build it yourself), and that is how the DDJ-400 has been played live.
What exists: WebMIDI support, a mapping contract, and a learn log that shows every incoming message, mapped or not. Unknown messages never run a command. The table says how far each controller has been checked.
CTRL-01, CTRL-02, CTRL-04
| Controller | How far it has been checked |
|---|---|
| Pioneer DDJ-400 | Played live (README) |
| Pioneer DDJ-FLX4 | Map built from the official AlphaTheta MIDI list. First hardware round Fri 18 Sep 2026: 42 MIDI messages confirmed by a live sniff. Not yet played in a set. |
| Pioneer DDJ-FLX10 | Mapped; no record of it being played live |
| Reloop Mixtour | Mapped; no record of it being played live |
Anything you want it to do.
What parity opens up: a screen a script or an AI agent can drive, a library that tells the truth about itself, and a library format not tied to any one vendor.
Deck and mixer controls are typed commands
One typed bus. Screen, MIDI, keyboard and agent input all go through one validated dispatcher with a queryable history, so a fader moved by hand and a fader moved by an agent are the same event. A control wired straight to the audio engine is treated as a defect, because automation could not see it.
Partial Master volume and master mute are on the dispatcher. Some top-bar controls, such as drawer and overlay open-state, are not yet.
AGENT-01
// mixer { type: 'fader', deck: 1, value: 0.8 } { type: 'eq', deck: 1, band: 'low', value: 0 } { type: 'filter', deck: 2, value: 0.35 } { type: 'channel_cue', deck: 2, enabled: true } { type: 'crossfader', value: 0.5 } // deck { type: 'seek', deck: 1, position_ms: 61000 } { type: 'play', deck: 2, playing: true } { type: 'cue', deck: 3 } { type: 'beat_sync', deck: 2, enabled: true } { type: 'hot_cue_trigger', deck: 1, slot: 'A' } { type: 'stem_mute', deck: 1, stem: 'vocal', muted: true }
Every shape above is a member of the PerformanceCommand union in that file. Commands lists every one. The app's crossfader takes the same 0 to 1 range as the fader docked at the bottom of this page.
Agents drive the installed app, inside rails
The Mac app's payload includes the opendj command line and opendj mcp (AGENT-11, shipped), so an agent can control a running Open DJ without a source checkout. The server enforces its own safety rails rather than trusting the agent: master mute before play, vendor writeback blocked, and destructive library and update calls gated.
AGENT-02 to AGENT-05, AGENT-11
opendj state opendj deck 1 play opendj eq 2 low 0.2 --over 4beats opendj mcp
| Surface | What an agent gets |
|---|---|
GET /api/v1/state/ui-mirror | One JSON document a person can read like the screen, with every operable control marked available or inert |
POST /api/v1/commands | Single, sequence, parallel and ramp orders, with durations in beats, bars or phrases against a deck's beat grid |
opendj mcp | Tools for status, app state, commands, the library, routes and updates |
An honest library view
Open DJ grays out rows whose file is gone, keeps a Missing Tracks folder with a live count, and blocks a deck load from a dead path with a message. A command-line repair tool finds validated matches, shows a dry run first and backs up before any live write.
Why it matters: in the library this project was built against, on Tue 28 Jul 2026, 1,186 of 8,355 tracks had audio on disk. 387 were streaming links, 10 sat on an unplugged drive, and most of the rest were gone.
The repair tool sorts every track into exactly one of seven classes, and refuses to print unless the classes add up to the total. An unplugged drive is not a dead path, and a streaming link is not a broken one.
| Class | What it means |
|---|---|
present | The file is on disk right now. |
relinkable-auto | Exactly one strong candidate, uncontested. |
relinkable-ambiguous | Candidates exist, none safe to apply unattended. |
awaiting-volume | On a drive that is not plugged in. Never relinked. |
streaming | A service link, not a file. Not broken. |
malformed-path | Blank, or a synthetic USB-export path. |
absent-no-audio | Not on disk, and no copy found anywhere indexed. |
Relinking is undoable. On a scratch copy of a real library, a relink changed 172 rows, undo reverted all of them with no conflicts, and a full 8,355-row by 11-column comparison found zero differences outside the update timestamp.
RECON-01 to RECON-05, LIBM-41, docs/library-availability.md
Illustration with placeholder rows. Real repair writes follow the same safety pattern as every vendor write: backup first, dry run by default.
Crash rescue: reopen and keep playing
A crash can stop the music, so the Gig view keeps a small rolling buffer of state snapshots on disk. Reopen within 10 minutes of a crash, a force quit or a power cut, and it restores the Gig view and resumes playback together, from beat stamps advanced by the time you were away, with one toast and an Undo. While a deck is playing, Cmd-Q asks before it quits, and the audio keeps going.
RESCUE-01 to RESCUE-04, INSTALL-21
Gig posture: the set comes first
While you play, nothing else competes with the set. In Gig posture (the app's setting for playing a set, as opposed to Prep), or whenever any deck is playing, background library sync and file downloads wait unless you press Force sync, and background work is held back. The app also reads the machine's cores and memory to pick a performance tier, and says so when it cannot read them instead of guessing.
CLOUDSYNC-14, PERFMODE-03, PERFMODE-01
An open library format with provenance
Fix a BPM by hand and, in a vendor library, it is just a number again: nothing records who set that value or how sure they were, so unless you lock it, the next app's analysis can overwrite it. The open-dj format wraps each analyzed value with its source, so a hand-fixed BPM keeps its source, and a disagreement between two apps is visible in the data instead of silently overwritten. Merging conflicting values is out of scope for this draft. Output is canonicalized with RFC 8785, so two tools that both canonicalize produce byte-identical files.
Open DJ is the reference implementation. The library records where each value came from, in the app; exporting or importing the open-dj format is a source-checkout tool, and the Mac app has no import or export control yet. The spec is CC BY 4.0; the reference code is Apache-2.0.
OPEN-01, OPEN-03
"title": "東京メロディー",
"bpm": {
"value": 120,
"source": "rekordbox",
"modified_at": "2026-02-02T09:10:00Z"
}
From the format's conformance corpus (open-dj/conformance/corpus-0.2/), not from a real library.
python -m apps.open_dj.cli validate <file> python -m apps.open_dj.cli canon <file> python -m apps.open_dj.cli diff <a> <b>
Run these from a source checkout. Run from a pip install, validate hits a known packaging gap (PARITY-TODO.md, L list).
Stems in the deck
Mute or solo prepared stems per deck. The STEM button under a channel turns its EQ dials into stem levels. Preparing stems never blocks a deck load, and a stem set with an unrecognized layout is rejected rather than guessed.
Stems cost memory, and the app counts it: decoded audio takes about 0.34 MB per second of track for each part held, so a stem deck costs a multiple of a plain one. The prefetch and preview caches around it are byte-capped and shed under memory pressure.
Not yet Making stems from the installed app. The code is in, but STEM-39 stays open until a signed build separates a track on a clean Mac, so today stems are produced from a source checkout.
Not yet Separating stems live, while a track plays.
MIXUX-04, MIXUX-06, STEM-39
{ type: 'stem_mute', deck: 1, stem: 'vocal', muted: false }
Illustration. The demucs4 layout has four parts and three controls: instrumental drives bass and other together (docs/architecture.md). A roformer2 stem set has two parts and exposes no drums control, because its instrumental already contains the drums.
Deleted tracks stay deleted
Remove a track and it stays removed. Re-running an import, from rekordbox, a folder or a Spotify list, does not bring it back, and a stale copy of the library cannot resurrect it through sync. Picking the file again on purpose does. A vendor no longer listing a file counts as availability, never as deletion.
LIBM-140, LIBM-141, CLOUDSYNC-30
A set is not a playlist
The playlist is the crate, the place to experiment. A set is a planned order through it, and one playlist can hold several, each a named tab above the track list that counts how often it has been played.
SET-05, PLAY-01
Word-level lyrics under the playhead
The app shows line lyrics, and a word-level lyric lane on the waveform when word timings exist: with the lyrics overlay switched on, the words move under the playhead in time with the track. Making word timings runs the lyrics pipeline from a source checkout today, the same as stems. Line-synced lyrics come from LRCLIB, a free public lyrics service. Alignment is scored by a versioned scorer, so a change that makes timing worse shows up as a number.
LYR-01 to LYR-06, LYRICS-01
Nothingismocked,nothingisinvented.Everybeatcomesfromarealrow.
Illustration using this site's own words, not a song lyric.
Tracks you import straight into Open DJ
A track imported without rekordbox loads and plays. It is analyzed on import, so it gets a beat grid, and its waveform comes from the app's own decode. Still open for these tracks: reading their tags in the packaged app (PARITY-15), vocal bars, genre, and saving hot cues, which today works only for tracks that came from rekordbox.
PARITY-06, PARITY-TODO v1 blocker list
Windows and Linux
macOS on Apple silicon is the only build. Other platforms get a download button only when a tested build exists.
INSTALL-05, CROSS-01, CROSS-02
What is not built.
Listed on purpose. Showing what is missing is part of the house rule.
Live stems during playback
Stems are prepared ahead of time. Separation while a track plays is a design target, not a feature.
Genre-routed separation
Picking the separation model by genre is planned. It depends on a second model landing in the toolbox.
Paid features
Nothing is behind a paywall. Everything is available to everyone.
Effects and sampler
No effects beyond the channel filter, and no sampler. Both are planned controls that show as not built.
Slip mode
Built, and not yet checked off against rekordbox on the parity board.
Two products, one substrate.
The April library toolchain is a live dependency of the performance rig. Each module sits in one of three tiers, from the project README.
| Tier | Meaning | Modules |
|---|---|---|
| Warm | Serving real data to the performance screen today | shared, webui, sync, analysis, tags, stems, vocals, reconcile, adapters, open_dj |
| Wired | The endpoint exists and the screen calls it, but no data has been created yet | smartlists, pairings, spotify, voice |
| Cold | Built and tested, not yet surfaced in the rig | sets, play_analytics, dedup, dj_copilot, cloud, audit, launcher |