This document describes how LedFx handles client identification, metadata synchronization, and state persistence across the network.
A client in LedFx is identified by three distinct pieces of information:
-
Device ID (
device_id):- Source: Generated by the frontend on first load (
crypto.randomUUID()). - Persistence:
localStorage. - Scope: Persistent across browser restarts and tab closures for a specific browser/hardware.
- Format:
web-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx - Purpose: To allow the backend to recognize a returning "device" even if the session has changed.
- Source: Generated by the frontend on first load (
-
Client ID (
client_id/clientId):- Source: Assigned by the Backend upon WebSocket connection.
- Persistence: In-memory (lost on disconnect).
- Scope: Unique per active network connection.
- Purpose: Used for direct messaging and identifying a specific socket session.
-
Client Metadata (
nameandtype):- Source: User-defined or automatically generated.
- Persistence:
sessionStorage. - Scope: Unique per Browser Tab.
- Purpose: Allows multiple tabs in the same browser to act as different entities (e.g., one tab as a "visualiser" and another as a "controller").
| Storage Mechanism | Data Stored | Why? |
|---|---|---|
localStorage |
device_id |
Must survive browser restarts to uniquely identify the physical machine/browser. |
sessionStorage |
name, type, client_id |
Allows tab-specific identities. Opening a new tab creates a fresh session with its own name and purpose. |
| Backend Registry | Full ClientMetadata |
Serves as the single source of truth for the entire network. |
When the frontend connects, the backend immediately sends the client_id assigned to that socket.
- Event:
{ "event_type": "client_id", "client_id": "..." }
Once connected, the frontend sends its known identity to the backend.
- Action:
set_client_info - Payload:
{ "device_id": "web-...", "name": "...", "type": "..." } - Backend Response: Backend updates its internal registry. If a name conflict occurs, the backend may modify the name.
When a user changes their name or type in the UI:
- Action:
update_client_info - Payload:
{ "name": "...", "type": "..." }(Note:device_idis omitted during updates).
If the backend changes a client's info (e.g., during conflict resolution), it notifies the client.
- Event:
{ "event_type": "client_info_updated", "name": "...", "type": "...", "client_id": "..." } - Frontend Action: Updates local store and
sessionStorage.
Whenever any client connects, disconnects, or changes metadata, the backend broadcasts a global refresh.
- Event:
{ "event_type": "clients_updated" } - Client Action: All connected clients perform a
GET /api/clientsto refresh their peer list.
- User opens
http://localhost:3000. - Frontend generates
device_id: "web-abc". - Frontend defaults metadata to
name: "Client-abc",type: "unknown". - WebSocket connects. Backend sends
client_id: "conn-1". - Frontend sends
set_client_infowithdevice_id: "web-abc". - Backend registers "Client-abc" as a new client.
- Client loses Wi-Fi. WebSocket closes.
- Frontend keeps its state in memory and
sessionStorage. - Wi-Fi restores. WebSocket reconnects.
- Backend sends new
client_id: "conn-2". - Frontend sends
set_client_infowith the samedevice_id: "web-abc"and saved metadata. - Backend sees
web-abcalready exists, updates its mapping to the newclient_id, and marks it as active.
- User already has Tab 1 open as "Main Visualizer".
- User opens Tab 2.
- Tab 2 reads
device_id: "web-abc"fromlocalStorage(Shared). - Tab 2 sees empty
sessionStorage. It generates a freshname: "Client-abc-2",type: "unknown". - Tab 2 connects via WebSocket. Backend sends
client_id: "conn-3". - Backend now shows two clients in the list, both sharing the same
device_idbut having uniqueclient_ids and names.
Broadcasts use a targeted system to send data to specific clients without flooding the network.
Targeting Modes:
all: Every connected client.type: Clients of a specific type (e.g., allvisualiserclients).names: Specific clients identified by name.uuids: Specific clients identified by their sessionclient_id.
Example: Syncing Visualizer Configs
{
"type": "broadcast",
"data": {
"broadcast_type": "visualiser_control",
"target": { "mode": "type", "value": "visualiser" },
"payload": { "config": { "brightness": 0.8 } }
}
}