Technical architecture
Overview
Tech stack
| Component | Technology | Role |
|---|---|---|
| Desktop framework | Tauri 2.x (Rust) | Native window, IPC, packaging |
| Interface | Vue.js 3 + Composition API | Reactive UI |
| State management | Pinia | Global state |
| Internationalisation | vue-i18n | FR / EN / ES / JA |
| Communication | WebSocket (tokio-tungstenite) | OBS overlay + Stream Deck |
| Unit tests | Vitest | Composables, stores |
| E2E tests | Playwright | Full 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_CONFIGon startup to retrieve the configured decks
Protocol
Any WebSocket client can connect — no authentication, the server is local only.
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.
{
"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.
{
"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.
{
"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)
| Message | Payload | Description |
|---|---|---|
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
| Value | Description |
|---|---|
vinyl | Vinyl turntable |
cd | CD player / CDJ |
controller | DJ controller |
software | Software (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.

