Nerd notes · for people who care how software behaves at 2 a.m.

Why StentorDecks is technically stubborn

Not a feature bingo card. A tour of the boring engineering we refused to skip — the kind commercial DJ software often buries under accounts, subscriptions, and “it sounded fine in the demo.”

Written by the builder · July 2026 · every claim maps to code & specs in the public repo
Prefer booth language first? Start with Booth notes (DJ + under-the-hood for each homepage card).

The stance

Most DJ software is a storefront that happens to mix. StentorDecks is the opposite: a booth instrument that happens to be free.

We did not try to win on pad count, cloud crates, or a subscription ladder. We tried to win on the moments where commercial apps quietly shrug:

Those are not edge cases. Those are Saturday night. So we wrote requirements first, locked them, and built toward them — with tests that do not need a controller in CI, and hardware checks marked honestly when only the booth can decide.

If the code and the spec disagree, the spec wins. If the spec is wrong, we stop and fix the spec — we don’t “improve” it silently in a rush.

Soft takeover — because the PA is not a video game

Every continuous control — faders, EQ, pitch, master, cue blend — goes through soft takeover. When the screen and the physical knob disagree (after a load, after you clicked the UI), the hardware stays “dead” until it crosses the software value. Then it locks on.

You see a hollow pickup mark. You move through it. You are live again. No sudden +12 dB gift to the room.

A lot of software either snaps the engine to the knob (scary) or leaves you guessing why nothing happens (annoying). We chose the third path: visible, deliberate, PA-safe.

One brain, two hands

Mouse drag and MIDI message hit the same store actions. There is no “UI path” and “controller path” that can drift apart after a late-night hotfix.

That sounds obvious. It is also where DJ apps rot: one team owns the skin, another owns the MIDI DLL, and soft takeover only works on Tuesdays. StentorDecks keeps the store as the single source of truth — engines react to state; hardware never writes audio parameters behind the store’s back.

SYNC with manners

StentorDecks is manual-first. SYNC is assistance, not autopilot.

We spent more words on SYNC in the requirements than some products spend in their entire “sync” marketing bullet. That is intentional.

You cannot load into a playing deck. Full stop.

Every load path — button, double-click, MIDI — goes through one method. If the deck is playing, the load is rejected, the control shows a lock, and you get a visible flash. No override in v1.

Loading also resets the deck to a known clean state (FX pads off, kills released, sync cleared, cue reset) so a new track never inherits last tune’s flanger surprise. Pitch and EQ stay where your hands left them when it is safe; filter/wet adopt hardware when known so you are not forced into a full relearn circus every load.

Determinism is a feature. Mid-set software should be boring in the good way: same inputs, same outcomes, no mystery state.

Click-free audio (the 15 ms rule)

Gain and parameter changes use ramps (setTargetAtTime / linear ramps, ≥ 15 ms). We do not slam .value mid-playback and hope Chromium is polite.

Channel faders use a proper position→dB curve you can tune (linear / smooth / sharp + shape). EQ has finer control near center and a soft edge at the extremes. Master boots around 30% so a cold start does not punish the room. There is a safety limiter after master — for accidents, not as a loudness war cheat code.

Headphone cue is a real cue bus (PFL), not “hope the soundcard driver invents it.” Plan A uses four channels on the RMX2 (booth + phones); Plan B covers separate devices when life is messier.

A library that remembers who a file is

Folders on disk are the crates. The app never moves, renames, or deletes your music.

Identity is not “whatever path string we saw last Tuesday.” We key on path + size + mtime, plus a partial content hash, so a rename or move keeps analysis. Analyse once per content. Duplicates in two folders stay two rows — because crates are real places, not a cloud abstraction.

BPM, key, beatgrid, waveform peaks, loudness — persisted in SQLite. Background analysis never blocks the mix. Camelot on the big numbers. Session “already played” marks so you stop looping the same tune by accident.

Broken MP3s are a booth fact, not a character flaw

Real libraries are full of damaged VBR rips. Chromium sometimes truncates them. We built resilient decode with seam crossfades so stitch points do not tick. In Library you can Check MP3 and Write a fixed WAV sibling — always a new file named (Fixed by SD). The original is never overwritten. No ffmpeg dependency; no “upload to our cloud to repair.”

That feature exists because the builder still owns those files. So do you.

What we deliberately left out

Technical quality is also knowing what not to fake in v1:

Commercial apps often ship twenty half-finished toys. We would rather ship two decks that behave under pressure.

Open source as a technical feature

You can read the audio graph, the MIDI decode, the soft-takeover state machine, the scanner identity rules. MIDI tests run from recorded fixtures — CI never needs the RMX2. Hardware acceptance is labelled honestly: ready for verification, not rubber-stamped by a mock.

~200 automated tests. Typed IPC. Spec documents that agents and humans are supposed to obey. Auto-update from GitHub Releases with a real feed (latest.yml), not a mystery CDN.

Is every commercial product “worse”? No. Some are excellent at scale, hardware ecosystems, and polish money can buy. What we claim is narrower and sharper:

For a free, offline, open-source two-deck booth app aimed at real hardware and declining eyesight at one metre — we sweated the parts that keep a set from dying. You can audit that claim.

Still here? You’re our kind of nerd. Download it, break it, file an issue — or just play a set.

↓ Download StentorDecks for Windows
Read the source on GitHub →  ·  DJ docs & developer reference →