Skip to content

AI AutoMixer

Architecture

The AutoMixer is a Utility AI (not a state machine). Multiple intents can fire simultaneously across different domains. The engine ticks at 50 ms (20 Hz) — at 170 BPM that's ~5.7 ticks per beat.

AI Modes

ModeBehavior
OFFEngine disabled
CRUISERuns continuously. Any user interaction kills the AI permanently
ASSISTPauses when user interacts. Resumes after configurable inactivity delay

Tick Loop

Each tick:

  1. Check mode (OFF = skip, ASSIST = check pause/resume)
  2. Clear ghost fields from previous tick
  3. Compute Blackboard (all deck/master state)
  4. Score all 20 intents via intent.evaluate(blackboard)
  5. Sort by score descending
  6. Arbitrate: fire each intent unless its domain is locked by a higher-scoring exclusive intent
  7. Execute winners, marking affected controls as "ghost fields"

Ghost Fields

When the AI manipulates a control, it's marked as a ghost field (e.g., "A.eq.low", "crossfader"). UI knobs with ghost fields glow purple, showing the user which controls the AI is touching. Ghost fields are stored in a global Set outside Zustand to avoid 50 ms re-render storms.

Blackboard

Variables computed once per tick from the Zustand store and audio engine:

Deck Roles

VariableDescription
masterDeckLouder deck or only playing deck (default A)
incomingDeckThe other deck

Timing

VariableDescription
masterCurrentBeatCurrent beat position
masterBeatInPhrasePosition within 16-beat phrase (0–15.99)
masterOnDownbeatBeat mod 4 < 0.5
beatsToOutroMasterBeats remaining to track end
deadAirImminent< 8 beats remaining

Spectral

VariableDescription
bassClashBoth decks' low EQ > -10 dB
midClashBoth decks' mid EQ > -6 dB
incomingBassKilledIncoming EQ low < -15 dB
masterHasFilterabs(colorFx) > 0.3

Harmonic

VariableDescription
masterKey / incomingKeyCamelot notation
isHarmonicMatchCamelot ±1 (compatible keys)

Phase

VariableDescription
isPhaseAlignedWithin ±50 ms
phaseDeltaMsSigned, positive = incoming behind

The Blackboard has an optional Wasm fast path — if the Rust module is loaded, 18 floats are packed into a Float64Array and computed in Wasm.

All 20 Intents

Safety Domain

IntentScoreTriggerAction
Dead Air Prevention1.0< 8 beats to end, no loopEngages 4-beat auto-loop
Phase Drift Correction0.6–0.9Both playing, > 10 ms driftProportional pitch nudge (0.5–2%) then restore
Red Line Limiter0.92Both volumes > 0.8, EQ boostedReduces master volume to 0.7
EQ Amnesia Recovery0.7–0.85Bass killed > 1.6s while soloGradually restores bass +2 dB/tick

Spectral Domain

IntentScoreTriggerAction
Drop Swap0.9Both playing, bass killed, phrase boundarySwap bass: master → -26 dB, incoming → 0 dB
Sub Rumble Control0.7Bass clash detectedIncoming low → -15 dB
Hi-Hat Layering0.4Incoming bass killed, highs < -2 dBIncoming high → 0 dB
Vocal Space Carving0.5Mid clash detectedIncoming mid → -8 dB
Isolator Sweep0.35Solo, EQ flat, near phrase boundarySweeps all EQ to -20 dB, snaps back at boundary

Dynamics Domain

IntentScoreTriggerAction
Filter Washout0.1–0.8Bass killed, within 16 beats of phraseRamps master colorFx to 0.7 (HPF)
LPF Mud Dive0.05–0.5Bass killed, no filterRamps master colorFx to -0.5 (LPF underwater)
Pre-Drop Silence0.950.3–1.0 beats to phrase boundarySets BOTH volumes to 0
Filter Wobble0.551–8 beats to phrase, no filterOscillates colorFx ±0.2 every half-beat
Build-Up Tension0.654–16 beats before incoming drop, no filter activeProgressive HPF sweep (colorFx 0.15→0.85) proportional to countdown
Ghost Note Echo0.48Master vol ≤ 0.45 and incoming > 0.6 (fade-in moment)Bass kill proportional to fade depth, hi-mid boost +4 dB, 1/4-note colorFx tremolo

Rhythm Domain

IntentScoreTriggerAction
Loop Roll Buildup0.75Within 16 beats, incoming readyCascade: loop(4) → loop(2) → loop(1) → loop(0.5) → exit
Teaser Stab0.45Incoming silent, bass killed, every 4th downbeatFlashes incoming volume to 1.0 for quarter-beat

Structure Domain

IntentScoreTriggerAction
Outro Riding0.38–64 beats to end, no clashNo-op: claims domain to prevent premature transitions
Double Drop Align0.2–0.6Both have drops, > 32 beats leadMicro playback rate nudge (±0.5%) to converge drops
Key Clash Defense0.8Keys not harmonically compatibleKills incoming mids to -20 dB; ramps master HPF

Intent Domains & Exclusivity

Intents are grouped into 5 domains: safety, spectral, dynamics, rhythm, structure. Within each domain, exclusive intents lock the domain — lower-scoring intents in the same domain are blocked. Non-exclusive intents can stack freely.

MIDI

Device Detection

Uses navigator.requestMIDIAccess({ sysex: false }). Hot-plug/unplug handled via onstatechange.

CC Mapping (Learn Mode)

  1. UI component calls window.__MIXIMIDILEARN__() to enter learn mode
  2. Next incoming CC or Note message is captured
  3. Mapping stored: { portId, type: 'cc'|'note', channel, control, action }

Mappable Actions

CC Actions (Continuous)

ActionParametersRange
CROSSFADER0–1
MASTER_VOL0–1
HEADPHONE_MIX0–1
HEADPHONE_LEVEL0–1
DECK_GAINdeck A/B-12 to +12 dB
DECK_VOLdeck A/B0–1
DECK_EQ_HIGHdeck A/BScaled by EQ range
DECK_EQ_MIDdeck A/BScaled by EQ range
DECK_EQ_LOWdeck A/BScaled by EQ range
DECK_FILTERdeck A/B-1 to +1 (bipolar)
DECK_PITCHdeck A/B±8% (hardcoded)

Note Actions (Toggle)

ActionParametersDescription
DECK_PLAYdeck A/BToggle play/pause
DECK_SYNCdeck A/BToggle sync
DECK_CUEdeck A/BToggle CUE
GROOVEBOX_PADdeck, voiceTrigger drum pad

Known Issue

DECK_PITCH range is hardcoded to ±8%. A TODO comment mentions adding configurable pitch range, but it's not implemented.

MIDI Clock

Output

  • 24 ppqn (standard MIDI clock)
  • Look-ahead scheduler: checks every 10 ms, schedules 25 ms ahead with hardware timestamps
  • Sends to ALL available MIDI outputs

Input

  • Receives clock ticks, averages last 24 ticks to compute external BPM
  • Exposes externalBpm and hasExternalClock

Skins

Built-in Skins (3)

SkinDescription
midnightDark blue theme (default)
freeteknoWarm rust/earth tones
carbonNeutral gray

Custom Skins (17 bundled)

Acid, Aqua, Arcade Invaders, Blackfluo, Bloodmoon, Casino, Dune, E-Ink, Gold, Hologram, Industrial, Matrix, Nordic, Synthwave, Vaporwave, White, and Freetekno (duplicate of built-in — will be resolved when custom skins migrate to external repository).

Custom Skin Format

A folder containing two files:

skin.json

json
{
  "id": "my-skin",
  "name": "My Custom Skin",
  "dotColor": "#ff0088"
}

skin.css

CSS file with custom property overrides. Injected as a <style> element. Basic security: rejected if it contains <script, javascript:, or expression(.

Loading Custom Skins

Click the folder icon in the topbar skin selector to open a directory picker. The skin is loaded, validated, and stored in localStorage for persistence.

Groovebox

Overview

4-voice drum machine with 16-step sequencer, per-voice synthesis, and MIDI input.

Voices

All voices are pure WebAudio synthesis — no sample files required.

VoiceMethodDurationDetails
KickSine pitch-sweep250 ms150 → 50 Hz exponential, exp(-12t), gain 0.9
SnareNoise + sine180 msWhite noise exp(-20t) at 0.5 + 200 Hz sine exp(-30t) at 0.4
HatHP filtered noise80 msFirst-order difference filter, exp(-50t), gain 0.35
PercTwo detuned sines100 ms800 Hz + 1127 Hz (metallic), exp(-35t)

Custom samples can be loaded via drag-and-drop onto sequencer rows.

Sequencer

16 steps per pattern. Each step is a boolean (on/off) per voice. Per-voice volume (0–1, default 0.8). Swing 0–0.5 applied to odd steps.

Scheduling: 25 ms timer, 50 ms look-ahead, 16th note resolution.

Per-Voice Mixer

ControlRangeDescription
Pan-1 to +1Stereo position
Muteon/offSilences voice
Soloon/offSolos voice (mutes others)
Volume0–1Per-voice gain

Bus Routing

Groovebox voices → per-voice gain/pan/mute → Bus output → DeckChannel.input
→ EQ → ColorFX → Fader → Crossfader → Master

The groovebox routes through the standard deck channel, so deck EQ, effects, and crossfader all apply.

MIDI Input

Receives GROOVEBOX_PAD events from the MIDI manager. Velocity (0–1) is applied as gain. Polyphonic — multiple voices can trigger simultaneously.

Stub Feature

The 8 FX pad buttons in the Groovebox UI (LPF, HPF, DLY, RVB, GATE, DIST, FLG, STT) are purely visual toggles. No actual audio FX processing is connected to the groovebox audio graph. Clicking them changes React state only.