Reference

The protocol

Everything between the bridge and the renderer is newline-free JSON over a plain WebSocket. It's deliberately boring, so you can write your own renderer against it.

Messages

Bridge → renderer. Every message carries a type and a unix ts. The renderer is receive-only.

hello

Sent once on connect.

json
{ "type": "hello", "app": "vbuddy-bridge", "version": "0.1.0" }
track

Traktor's broadcast metadata changed — i.e. a new track went live.

json
{
  "type": "track",
  "deck": "A",
  "artist": "Overmono",
  "title": "So U Kno",
  "filename": "Overmono - So U Kno",
  "ts": 1753712345.12
}
clock

Once per beat, derived from 24 PPQN MIDI clock.

json
{
  "type": "clock",
  "bpm": 130.02,
  "beat": 2,
  "bar": 41,
  "playing": true
}
cue

A mapped hotcue note fired.

json
{ "type": "cue", "deck": "A", "index": 3, "note": 38, "channel": 0 }
transport

MIDI start / stop / continue.

json
{ "type": "transport", "playing": false }

Clip matching

When a track message arrives, the renderer scores every file in your indexed folder and plays the best match above the threshold.

  1. 1. Exact filename match on Artist - Title (extension ignored).
  2. 2. Exact match on Title alone.
  3. 3. Normalised token overlap — punctuation, brackets, "(Original Mix)" and remix suffixes stripped.
  4. 4. No match above 0.55 → the fallback clip plays.

Hotcue mappings always win over auto-sync while their clip is playing.

CLI flags

FlagDefaultPurpose
--host0.0.0.0WebSocket bind address
--ws-port8765WebSocket port the renderer connects to
--meta-port8000Port Traktor broadcasts to
--midiMIDI input port name (see --list-midi)
--cue-base36MIDI note of Deck A hotcue 1
--cue-channelanyRestrict hotcue notes to one MIDI channel
--oscForward events to host:port for Resolume / VDMX
--no-metaoffRun clock-only, skip the broadcast listener
--list-midiPrint available MIDI inputs and exit

Write your own renderer

The feed is a plain WebSocket, so anything that speaks WS can consume it — openFrameworks, TouchDesigner, a Processing sketch, or twelve lines of JavaScript.

javascript
const ws = new WebSocket("ws://localhost:8765");

ws.onmessage = (event) => {
  const msg = JSON.parse(event.data);

  if (msg.type === "clock") {
    setTempo(msg.bpm);
    if (msg.beat === 0) onDownbeat();
  }

  if (msg.type === "track") {
    loadClipFor(msg.filename);
  }

  if (msg.type === "cue") {
    fireLoop(msg.deck, msg.index);
  }
};

Limits worth knowing

  • Metadata is deck-agnostic. Traktor's broadcast stream reports the audible mix, so the bridge tags tracks as Deck A. Hotcue events do carry a real deck, based on the note range you mapped.
  • MIDI clock has no bar position. Bar counting starts at the last transport start message, so re-sync by restarting Traktor's clock if the downbeat drifts off your visuals.
  • Folder access is browser-scoped. The renderer uses the File System Access API — Chrome and Edge only. Other browsers fall back to a manual multi-file picker each session.