LAN-first collaborative music playback for offline local networks.
- Phase 1 implemented: local folder indexing, search, queue, and playback.
- Phase 2 implemented: room create/list/join over local coordinator APIs.
- Phase 3 baseline implemented: room-scoped websocket sync with authoritative coordinator queue/playback state.
- Shared protocol and domain types are centralized in workspace packages.
apps/
web/ # React + Vite frontend
coordinator/ # Node.js local coordinator daemon
packages/
shared-types/ # Domain models shared by all apps
protocol/ # Control plane event schema/types- Control plane and data plane are strictly separated in design:
- Control plane:
@droptune/protocolevent envelopes sent via ws. - Data plane: reserved for WebRTC DataChannel file transfer in later phases.
- Control plane:
- No server DB, no cloud dependencies, no auth.
- Local-first browser playback uses
HTMLAudioElement. - Indexed metadata is persisted in IndexedDB (
idb) while file blobs stay in local memory only.
Defined in packages/protocol/src/index.ts:
JOIN_ROOM,LEAVE_ROOM,ROOM_STATEQUEUE_ADD,QUEUE_REMOVE,QUEUE_REORDERPLAY,PAUSE,SEEK,HOST_CHANGE,PLAYBACK_STATEFILE_REQUEST,FILE_AVAILABLE,WEBRTC_SIGNAL
All messages use:
EventEnvelope<TType, TPayload>sentAttimestamp- optional
requestIdfor correlation
apps/web/src/stores:
roomStore: room identity + connection statuslibraryStore: indexed metadata + local file map + search query + hydration/indexing actionsqueueStore: queue add/remove/reorderplaybackStore: playback status/position/duration/current trackpeerStore: local peer identity and display nametransferStore: transfer status placeholder for Phase 5
apps/coordinator/src/index.ts:
- HTTP:
GET /healthGET /roomsPOST /rooms/create
- WebSocket:
- endpoint:
/ws - room join/leave tracking
- room-scoped membership routing
- authoritative queue and playback updates on:
QUEUE_ADD,QUEUE_REMOVE,QUEUE_REORDERPLAY,PAUSE,SEEK,PLAYBACK_STATE,HOST_CHANGE
- room-state snapshots (
ROOM_STATE) emitted after authoritative updates - signaling relay for
FILE_REQUEST,FILE_AVAILABLE,WEBRTC_SIGNAL
- endpoint:
- mDNS advertisement:
- service
_droptune._tcp.local(typedroptune, protocoltcp)
- service
- Host emits
FILE_REQUESTfor missing track. - Source peer replies
FILE_AVAILABLE. - Offer/answer/ICE exchange via
WEBRTC_SIGNALon control plane. - DataChannel opens with fixed chunk protocol.
- Host reconstructs file
Blob, creates object URL, plays viaHTMLAudioElement. - Transfer status updates go through
transferStore.
Current playback statuses:
idle-> no active sourceloading-> source assigned, waiting for metadata/playplaying-> audio runningpaused-> user or host pausedended-> track finished (auto-advances queue locally)error-> source/playback failed
Transition source:
- audio element events (
play,pause,ended,loadedmetadata,timeupdate) - queue actions (
Play now) - explicit controls (
Resume,Pause)
- Folder ingestion:
- primary: File System Access API
showDirectoryPicker - fallback:
<input type="file" webkitdirectory multiple>
- primary: File System Access API
- Metadata extraction:
music-metadata-browserparses title/artist/album/duration- fallback to filename when tags are unavailable
- Supported formats:
- mp3, flac, wav, m4a, aac
- Storage:
- metadata in IndexedDB (
tracksstore) - raw files NOT persisted in IndexedDB during indexing
- metadata in IndexedDB (
From workspace root:
pnpm install
pnpm dev:webCoordinator (in a second terminal):
pnpm dev:coordinatorChecks:
pnpm typecheck
pnpm build- Web app:
- verify IndexedDB data in browser devtools (
droptune-db->tracks) - verify metadata parser fallbacks on malformed files (title should use filename)
- verify audio events update store state (status/position/duration)
- verify IndexedDB data in browser devtools (
- Coordinator:
GET /healthfor daemon statusGET /roomsto verify room registry- inspect ws payloads in devtools/network (all control events are plain JSON)
- use
dns-sd/Bonjour browser tools to confirm_droptune._tcp.localadvertisement
- Phase 4: host-authoritative playback controls and state broadcasts.
- Phase 5: WebRTC chunked file transfer.
- Phase 6: local-first track matching before requesting transfer.
- Phase 7: prefetch next
N=2. - Phase 8: host migration with room snapshot handoff.