Skip to content

Technical architecture

Overview

Tech stack

ComponentTechnologyRole
Desktop frameworkTauri 2.x (Rust)Native window, IPC, packaging
InterfaceVue.js 3 + Composition APIReactive UI
State managementPiniaGlobal state
Internationalisationvue-i18nFR / EN / ES / JA
CommunicationWebSocket (tokio-tungstenite)OBS overlay + Stream Deck
Unit testsVitestComposables, stores
E2E testsPlaywrightFull flows

The four components

1. Main application (Tauri)

The heart of the system. The user manages their setlist, configures decks and controls playback here. Any change triggers a WebSocket broadcast to all connected clients.

Built with Vue.js 3 on the UI side and Rust on the backend. Tauri bridges the two via typed IPC commands.

2. OBS Overlay

A simple HTML file (obs-overlay/index.html) added as a Browser Source in OBS Studio. It connects to the app's WebSocket and updates the display on each PLAYING_UPDATE message. No external server required — everything goes through localhost.

3. Stream Deck Plugin

A JavaScript plugin for Elgato Stream Deck. Each button represents a deck. It connects to the same WebSocket and sends commands back (TOGGLE_DECK, PLAY_TRACK, STOP_DECK). Buttons update dynamically (artwork, title, playing/idle state).

4. dlp-player npm widget

A standalone JavaScript package (npm publication pending). It reads a playlist JSON exported from DLP and synchronises track display with a media player (<video>, <audio>, YouTube or SoundCloud iframe). Two display modes: list (tracklist beside the player) and overlay (superimposed at the bottom). Zero dependency on the desktop app — works entirely client-side.

The WebSocket server

The Rust backend starts a WebSocket server on localhost:9876 when the app launches. It handles:

  • Multiple connections — OBS Overlay + Stream Deck can be connected simultaneously
  • Broadcast — when a track changes, all clients receive PLAYING_UPDATE
  • Config sync — the Stream Deck plugin sends GET_DECKS_CONFIG on startup to retrieve the configured decks

Protocol

Any WebSocket client can connect — no authentication, the server is local only.

javascript
const ws = new WebSocket('ws://localhost:9876')

ws.onmessage = (event) => {
  const { type, payload } = JSON.parse(event.data)
  if (type === 'PLAYING_UPDATE') {
    // Update the display
  }
}

Outgoing messages (App → Clients)

PLAYING_UPDATE — Sent when the currently playing tracks change.

json
{
  "type": "PLAYING_UPDATE",
  "payload": {
    "tracks": [
      {
        "id": "uuid",
        "title": "Track Title",
        "artist": "Artist Name",
        "image": "data:image/jpeg;base64,... or URL",
        "deckName": "Vinyl 1",
        "qrCodeUrl": "https://..."
      }
    ]
  }
}

DECK_CANDIDATES — Response to GET_DECK_CANDIDATES. Provides available tracks for a given deck.

json
{
  "type": "DECK_CANDIDATES",
  "payload": {
    "deckId": "deck-1",
    "currentlyPlaying": { "id": "...", "title": "...", "artist": "...", "image": "..." },
    "lastPlayed":       { "id": "...", "title": "...", "artist": "...", "image": "..." },
    "nextUnplayed":     { "id": "...", "title": "...", "artist": "...", "image": "..." }
  }
}

DECKS_CONFIG — Response to GET_DECKS_CONFIG. Lists all configured decks.

json
{
  "type": "DECKS_CONFIG",
  "payload": {
    "decks": [
      { "id": "deck-1", "name": "Vinyl 1", "deckType": "vinyl", "image": "data:image/png;base64,..." },
      { "id": "deck-2", "name": "CDJ 1",   "deckType": "cd",    "image": "data:image/png;base64,..." }
    ]
  }
}

CONFIG_UPDATE — Sent when the overlay visual configuration changes (theme, display options, animations).

Incoming messages (Clients → App)

MessagePayloadDescription
GET_DECKS_CONFIG{}Request the list of configured decks
TOGGLE_DECK{ "deck_id": "deck-1" }Toggle a deck on/off
GET_DECK_CANDIDATES{ "deck_id": "deck-1" }Request candidate tracks for a deck
PLAY_TRACK{ "track_id": "uuid" }Start playing a specific track
STOP_DECK{ "deck_id": "deck-1" }Stop a deck

Deck types

ValueDescription
vinylVinyl turntable
cdCD player / CDJ
controllerDJ controller
softwareSoftware (Rekordbox, Serato, Traktor...)

Version management

The version is defined once in package.json and automatically synced to tauri.conf.json and Cargo.toml via a sync-version.js script, called automatically before each build and tauri via npm prebuild / pretauri hooks.