# Multi-Client Setup

Exaplay 3 supports multi-client deployments where one **master** PC coordinates playback and content across multiple **client** PCs connected on the same network. This enables large-scale projection setups, multi-screen installations, and distributed show control.

## Architecture

```
┌─────────────────────────┐
│   Master PC             │
│   Exaplay Engine        │
│   (port 8123)           │
│                         │
│   ◆ Discovers clients   │
│   ◆ Manages screens     │
│   ◆ Syncs playback      │
│   ◆ Pushes files        │
└──────┬──────────────────┘
       │  HTTP + WebSocket
   ┌───┴───┐
   │       │
┌──▼──┐ ┌──▼──┐
│ PC2 │ │ PC3 │   ← Client PCs
│     │ │     │      running Exaplay
└─────┘ └─────┘
```

### Roles

| Role       | Description                                                                                                                                                   |
| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Master** | The PC running the main Exaplay UI where the operator works. Controls playback, assigns screens to remote displays, and pushes content.                       |
| **Client** | A remote PC also running Exaplay Engine. Receives playback sync commands and renders content on its local displays. Designated via the `Client Mode` setting. |

### Client Mode

An Exaplay instance can be designated as a **client** by enabling **Client Mode**. This can be done in two ways:

1. **From the client's own Settings**: `Settings → System → Client Mode`
2. **From the master's Network tab**: Select the remote system in the NETWORK tab, then toggle the **Role** checkbox in the inspector panel.

When client mode is enabled:

* The instance is marked as a "client" in the master's **NETWORK** tab (green badge).
* The master's **Copy Files** operation automatically transfers the `.vpp` project file and all sync folder media to this client.
* After file transfer, the master triggers `project.load` so the client automatically loads the master's project.
* The client does not need to maintain its own `.vpp` file — it receives everything from the master.

### Display Name

Each remote system can be given a custom **display name** for easier identification:

1. **From the client's own Settings**: `Settings → System → Display Name`
2. **From the master's Network tab**: Select the remote system, then type a name in the **Name** field in the inspector panel.

The display name is stored on the remote system's configuration and persists across sessions. When set, it replaces the OS hostname in system cards and the inspector title.

The master's NETWORK tab also shows a **sync status summary** on each system card:

* **✓ synced** — all files were transferred successfully
* **⟳ copying…** — transfer is in progress
* **⚠ partial** — some files transferred, some failed
* **✗ failed** — all transfers failed

## Getting Started

1. Start Exaplay on all PCs. Ensure they are on the same network.
2. Open the **NETWORK** tab on the master PC.
3. Remote instances appear automatically under **Systems** (auto-discovery).
4. Select a remote system and toggle the **Role** checkbox to designate it as a client. Optionally set a **Name** for easy identification.
5. Click **Copy Files** to transfer media and the project to all clients.
6. Configure playback sync as described below.

## Step 1: Remote Display Discovery

The master PC automatically discovers all remote Exaplay instances on the network. Their displays appear in the **OUTPUTS** tab alongside local outputs:

* **Sidebar**: Remote outputs are grouped by host under a "Remote Outputs" section.
* **Layout Map**: Remote displays appear with a dashed purple border and a "remote" badge.
* **Screen Assignment**: Remote displays can be assigned to screens just like local ones.

### How It Works

1. Each Exaplay engine broadcasts a UDP discovery beacon on port **45454** every 5 seconds containing its hostname, IP address, HTTP port, and version.
2. All Exaplay engines on the same subnet listen on port 45454 and maintain a list of discovered peers (entries expire after 30 seconds of silence).
3. The master queries `GET /monitor/instances` which returns the local instance plus all remotely discovered instances.
4. For each online remote, it fetches `GET /data?values&type=exaObj&path=project.system` to retrieve the remote's output-list.
5. Remote displays are merged into the local display list with metadata (`_remote`, `_remoteHost`, `_remoteHostname`, `_remoteInstanceId`).

## Step 2: Remote Display Assignment

In the **OUTPUTS → Screens & Fields** section, when assigning displays to a screen:

* Local displays toggle their `owner` field locally and save via the local engine.
* Remote displays toggle their `owner` field on the **remote** engine via HTTP POST to `http://<remote>:<port>/data`.

This means each engine manages its own display assignments, but the master UI provides a unified view.

## Step 3: Playback Synchronisation

Enable **Playback Sync** in the **NETWORK** tab to broadcast transport commands to all connected clients:

| Command   | Description                                          |
| --------- | ---------------------------------------------------- |
| **Play**  | All sync targets start playback                      |
| **Pause** | All sync targets pause                               |
| **Stop**  | All sync targets stop                                |
| **Seek**  | All sync targets jump to the specified time position |

### Configuration

* **Enable/Disable**: Toggle the "Playback Sync" button in the Network tab.
* **Target Selection**: Use the per-client checkboxes to include or exclude specific clients from sync.
* **Persistence**: Sync settings are saved to `localStorage` and persist across sessions.

### Protocol

Sync commands are sent as HTTP POST to each target's `/data` endpoint:

```json
{
  "values": {
    "sync-action": "play",
    "sync-composition": "composition-uid",
    "sync-timestamp": 1711037094913,
    "sync-latency": 12,
    "sync-master-time": 42.5
  }
}
```

| Field              | Description                                                                                           |
| ------------------ | ----------------------------------------------------------------------------------------------------- |
| `sync-action`      | Transport command: `play`, `pause`, `stop`, or `seek`                                                 |
| `sync-composition` | UID of the target composition                                                                         |
| `sync-timestamp`   | Master's wall-clock timestamp (ms since epoch)                                                        |
| `sync-latency`     | Estimated one-way network latency to this client (ms)                                                 |
| `sync-master-time` | Master's current playback position (seconds) — allows the client to seek to the correct frame on play |
| `sync-time`        | *(seek only)* Target position in seconds                                                              |

### Frame-Accurate Sync

When Playback Sync is enabled, the master automatically:

1. **Measures network latency** to each sync target using a ping/pong round-trip (timing a GET request). Latency is maintained as a rolling average of the last 5 samples.
2. **Runs a heartbeat** every 2 seconds that re-measures latency to each target and computes sync quality:

   | Quality        | Criteria                           | Indicator |
   | -------------- | ---------------------------------- | --------- |
   | **in-sync**    | Latency ≤ 40 ms (1 frame @ 25 fps) | 🟢 `●`    |
   | **soft-drift** | Latency 41–120 ms (1–3 frames)     | 🟡 `◐`    |
   | **hard-drift** | Latency 121–1000 ms                | 🟠 `○`    |
   | **lost**       | Latency > 1 s or unreachable       | 🔴 `✗`    |
3. **Includes per-target latency** in every transport sync payload so clients can compensate for network delay when starting playback.

Each sync target in the NETWORK tab shows its sync quality badge and measured latency (e.g. `● 8ms`).

### Composition Switch Sync

When the master switches the active composition, the change can be broadcast to all sync targets via the `composition-switch` action:

```json
{
  "values": {
    "sync-action": "composition-switch",
    "sync-composition": "composition-uid",
    "sync-composition-name": "My Timeline",
    "sync-timestamp": 1711037094913
  }
}
```

### Cue Trigger Sync

When the master triggers a cue within a CueList, the trigger is broadcast to all sync targets via the `cue-trigger` action:

```json
{
  "values": {
    "sync-action": "cue-trigger",
    "sync-composition": "composition-uid",
    "sync-cue": "cue-uid",
    "sync-cue-name": "Intro",
    "sync-timestamp": 1711037094913
  }
}
```

## Version Mismatch Detection

The NETWORK tab automatically detects when client PCs are running a different version of Exaplay than the master. Version mismatches are shown with coloured badges:

| Severity  | Meaning                                                           | Badge           |
| --------- | ----------------------------------------------------------------- | --------------- |
| **Major** | Different major version (e.g. v3.x vs v2.x) — may be incompatible | 🔴 `⚠ version`  |
| **Minor** | Different minor version (e.g. v3.2 vs v3.1) — features may differ | 🟡 `⚠ version`  |
| **Patch** | Same major.minor, different patch — safe                          | 🟢 (no warning) |

Version mismatch badges appear on both system cards and in the inspector panel's Version field.

## Remote Health Monitoring

The master periodically polls each online remote for performance data (FPS). Health is classified and shown on system cards:

| Status       | Criteria                              | Indicator     |
| ------------ | ------------------------------------- | ------------- |
| **Healthy**  | FPS ≥ 90% of target (22.5+ at 25 fps) | 🟢 `● 25 fps` |
| **Degraded** | FPS 50–90% of target                  | 🟡 `◐ 18 fps` |
| **Critical** | FPS < 50% of target                   | 🔴 `✗ 8 fps`  |

The inspector panel shows FPS and status label when a remote is selected.

## Restart All Clients

The **Restart All** button in the Active Sync banner reloads the project on all online client-mode systems. This is useful after making changes to the project on the master. It:

1. Fetches the master's current project path via `GET /project?req=tree`.
2. Sends `POST /project { req: "project.load", path }` to each online client.
3. Only targets systems with client mode enabled (not standalone remotes).

## Step 4: File Synchronisation (Active Sync)

The **NETWORK** tab provides two file sync modes:

### Manual Copy

Click **Copy Files** to transfer all files from the sync folder — along with the current `.vpp` project file — to every online remote:

* Files are read from the master engine via `GET /op/filesys/read`.
* The current project's `.vpp` path is resolved via `GET /project?req=tree` and included in the transfer.
* All files (media + `.vpp`) are transferred to each remote via WebSocket (`/filetransfer`).
* Per-file progress bars show transfer status.
* After the transfer completes, the master sends `POST /project { req: "project.load" }` to each remote so the client engine automatically loads the pushed project.

#### Large File Robustness

The file transfer is designed to handle large media files (hundreds of MB to multiple GB) reliably:

| Feature                  | Detail                                                                                                                                |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------- |
| **Adaptive chunking**    | Chunk size scales with file size: 64 KB (< 1 MB), 256 KB (1–100 MB), 1 MB (100 MB–1 GB), 2 MB (> 1 GB)                                |
| **Backpressure**         | Monitors `ws.bufferedAmount`; pauses sending when the WebSocket buffer exceeds 4 MB to prevent browser memory issues                  |
| **Auto-retry**           | Failed transfers are automatically retried up to 3 times with exponential backoff (1s → 2s → 4s + jitter)                             |
| **Timeout**              | Per-file timeout scaled to file size (minimum 30s, assumes ≥ 1 MB/s + 50% buffer); prevents stalled transfers from blocking the queue |
| **Size verification**    | After transfer completes, verifies bytes-sent matches expected file size; mismatches are not retried (indicates a deeper issue)       |
| **Graceful degradation** | A failed file does not block the remaining files; the progress UI shows `✗ failed` per file                                           |

> **Note**: Client PCs receive the project from the master during Copy Files and load it automatically. They do not need their own `.vpp` file.

### Active Sync (Auto)

Enable **Auto** to automatically push sync folder metadata every 10 seconds:

* The master sends the folder path, file list, and timestamp to each remote.
* Remotes can use this information to stay in sync with the master's content.
* Status indicators show "✓ N files" or "✗ error" per remote.

### Sync Folder

Configure the sync folder path in the text input. The default is `$[exaplay]/sync`. The path is sanitised to prevent path traversal.

## Network Requirements

| Port  | Protocol      | Purpose                                   |
| ----- | ------------- | ----------------------------------------- |
| 8123  | HTTP/WS       | Engine API, file transfer, project data   |
| 3001  | HTTP/WS       | Effects server (shader control)           |
| 45454 | UDP broadcast | Auto-discovery of other Exaplay instances |

Ensure Windows Firewall allows all three ports on all machines. See [Firewall Configuration](/v3/troubleshooting/network.md) for details.

## Troubleshooting

### Remote not appearing in Systems list

* Verify both PCs are on the same network subnet.
* Check that the Exaplay Engine is running on the remote PC.
* Ensure **UDP port 45454** is not blocked by firewall — this is required for auto-discovery.
* Ensure **TCP port 8123** is not blocked by firewall — this is required for HTTP communication.
* Verify that broadcast packets are not blocked by your network switch or router (some managed switches disable UDP broadcast by default).
* Click the refresh button in the Systems panel.

### Playback sync commands not received

* Verify the remote shows as "UP" in the Systems panel.
* Check that Playback Sync is enabled (toggle button shows "On").
* Ensure the target client's checkbox is checked.
* Check the browser console for HTTP errors.

### File copy fails

* Verify the sync folder exists on the master PC.
* Check that the remote engine's WebSocket port (8123) is accessible.
* Monitor the per-file progress bars for error indicators (✗).


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.exaplay.one/v3/features/multi-client.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
