Appearance
UDP Command API
Exaplay 3 exposes a UDP command interface that carries the same command set as the TCP Command API. It is aimed at show control systems, touch panels, and embedded controllers that prefer the simplicity of fire-and-forget datagrams over a persistent TCP connection.
Naming note: The Exaplay UI calls this composition type a Playlist. The wire protocol still uses the original
cuelistidentifier (returned byget:type, used inget:cuelist, etc.). This is intentional so existing integrations continue to work — treatcuelistandCueListin the protocol as synonyms for Playlist in the UI.
Connection
| Parameter | Value |
|---|---|
| Protocol | UDP (datagram) |
| Default Port | 8200 |
| Configuration | Config → Network → UDP Listen |
| Line terminator | \r\n (CRLF) recommended; bare \r or \n are also accepted |
| Encoding | ASCII / UTF-8 |
Each datagram contains exactly one command. The engine sends the complete response — which may span multiple logical lines — back to the sender's IP and port as a single reply datagram. There is no persistent session; each command/response exchange is independent.
Note: Because UDP does not guarantee delivery or ordering, avoid sending commands faster than you can process the responses. For reliable bidirectional communication (status polling, multi-step workflows) the TCP Command API is preferable.
Changing the Listen Address/Port
The UDP Listen config field accepts one or more entries (one per line), each in ADDRESS:PORT format. Use * as the address to listen on all network interfaces:
*:8200To listen on a specific interface only:
192.168.1.5:8200Leave the field empty to disable UDP control.
Command Format
COMMAND,COMPOSITION_ID[,ARGUMENT]- Fields are separated by commas.
- The first field is always the command name.
- Commands that target a composition require the composition's variable name (e.g.
comp_1) as the second field. - Retrieve variable names with
get:complist. - The response is returned as a single UDP datagram. Multi-line responses (list commands) have each line delimited by
\r\nwithin that datagram and are terminated by a finalEND\r\nline.
Global Commands
These commands do not require a composition ID.
| Command | Response | Description |
|---|---|---|
hello | hallo | Connection test / keep-alive |
get:ver | Exaplay,<version> | Get engine version string |
get:complist | One line per composition, then END | List all compositions |
get:projname | <project name> | Get the loaded project name |
get:complist response format
<varname>,<display name>\r\n
<varname>,<display name>\r\n
...
END\r\nExample:
→ get:complist
← comp_main,Main Show\r\ncomp_lobby,Lobby Loop\r\nEND\r\nComposition Commands
All composition commands use the format COMMAND,COMPID[,ARGUMENT].
Transport
| Command | Arguments | Response | Description |
|---|---|---|---|
play,COMPID | — | OK | Start playback |
pause,COMPID | — | OK | Pause playback |
stop,COMPID | — | OK | Stop playback |
set:cuetime,COMPID,SECONDS | SECONDS — float | OK | Seek to position (seconds) |
Audio
| Command | Arguments | Response | Description |
|---|---|---|---|
get:vol,COMPID | — | <0–100> | Get volume (integer %) |
set:vol,COMPID,VALUE | VALUE — integer 0–100 | OK | Set volume |
Display
| Command | Arguments | Response | Description |
|---|---|---|---|
get:alpha,COMPID | — | <0–100> | Get master alpha (integer %) |
set:alpha,COMPID,VALUE | VALUE — integer 0–100 | OK | Set master alpha |
Playback Options
| Command | Arguments | Response | Description |
|---|---|---|---|
get:loop,COMPID | — | 0 or 1 | Get loop state |
set:loop,COMPID,VALUE | VALUE — 0 or 1 | OK | Enable/disable looping |
Status and Info
| Command | Response format | Description |
|---|---|---|
get:name,COMPID | <name> | Get display name |
get:type,COMPID | timeline, cuelist, or composition | Get composition type |
get:itemcount,COMPID | <count> | Number of media items |
get:status,COMPID | STATUS,TIME,FRAME,CUEINDEX,TOTAL | Full playback status (see below) |
get:duration,COMPID | Depends on type (see below) | Total duration |
get:playingitem,COMPID | Depends on type (see below) | Currently playing item |
get:cuename,COMPID | <name> | Name of the currently active cue |
get:status response fields:
| Field | Values | Description |
|---|---|---|
STATUS | 0 stopped, 1 playing, 2 paused | Playback state |
TIME | float (seconds) | Current playback position |
FRAME | integer | Current frame number (TIME × 60) |
CUEINDEX | integer | Active cue index (timeline) or current item position (cuelist) |
TOTAL | float (seconds) | Total duration of current item (cuelist only) |
get:duration response:
- Timeline:
DURATION— total timeline length in seconds (float) - CueList:
COUNT,TOTAL— number of items and sum of all clip lengths (float seconds)
get:playingitem response:
- CueList:
INDEX,NAME,FILE— 1-based item index, friendly name, source file path - Timeline:
INDEX,CUENAME,TIME— cue variable index, cue name, current time (float seconds)
Timeline Commands
These commands are only valid when the composition is a Timeline.
| Command | Arguments | Response | Description |
|---|---|---|---|
set:cue,COMPID,INDEX | INDEX — cue variable index | OK | Jump to cue by index and trigger it |
get:cuelist,COMPID | — | One line per cue, then END | List all cues |
get:cueinfo,COMPID,INDEX | INDEX — cue variable index | INDEX,NAME,OFFSET,ACTIVE | Details about a specific cue |
get:cuelist (Timeline) response format:
<index>,<name>,<time-offset seconds>\r\n
...
END\r\nget:cueinfo (Timeline) response fields: INDEX,NAME,OFFSET_SECONDS,IS_ACTIVE
CueList Commands
These commands are only valid when the composition is a CueList.
| Command | Arguments | Response | Description |
|---|---|---|---|
set:cue,COMPID,INDEX | INDEX — 1-based item number | OK | Play item at the given position |
next,COMPID | — | OK | Advance to the next item |
prev,COMPID | — | OK | Go back to the previous item |
get:cuelist,COMPID | — | One line per item, then END | List all items |
get:cueinfo,COMPID,INDEX | INDEX — 1-based item number | INDEX,NAME,FILE,DURATION | Details about a specific item |
get:cuelist (CueList) response format:
<1-based index>,<name>,<file path>\r\n
...
END\r\nNote: Timeline cue indices match the cue's variable index (arbitrary integer). CueList item indices are always 1-based sequential numbers.
Error Responses
All error responses begin with ERR,:
| Error | Meaning |
|---|---|
ERR,unknown_command | Command string not recognised |
ERR,composition_not_found | No composition with the given variable name exists |
ERR,missing_argument | Required argument was not supplied |
ERR,cue_not_found | The cue/item index does not exist |
ERR,none_playing | Requested info about a playing item, but nothing is playing |
ERR,no_next_item | next reached the end of the cue list |
ERR,already_at_first | prev called when the first item is already playing |
ERR,cuelist_empty | next/prev called on an empty cue list |
ERR,unsupported_type | Command is not applicable to this composition type |
ERR,no_project | No project is currently loaded |
ERR,exception | An internal error occurred |
Example Sessions
Connection test
→ hello
← hallo\r\nDiscover and play a composition
→ get:complist
← comp_main,Main Show\r\ncomp_lobby,Lobby Loop\r\nEND\r\n
→ play,comp_main
← OK\r\n
→ get:status,comp_main
← 1,12.3456,740,0,0.0000\r\nWork with a CueList
→ set:cue,comp_lobby,2
← OK\r\n
→ next,comp_lobby
← OK\r\nWork with a Timeline
→ set:cue,comp_main,2
← OK\r\nSending Commands from Common Environments
Python (testing / scripting)
python
import socket
HOST = "192.168.1.5"
PORT = 8200
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.settimeout(2.0)
def send(cmd):
sock.sendto((cmd + "\r\n").encode(), (HOST, PORT))
try:
data, _ = sock.recvfrom(65536)
return data.decode().strip()
except socket.timeout:
return "(no response)"
print(send("hello")) # hallo
print(send("get:complist")) # comp_main,Main Show\r\nEND
print(send("play,comp_main")) # OKNode.js
js
const dgram = require('dgram');
const client = dgram.createSocket('udp4');
const HOST = '192.168.1.5';
const PORT = 8200;
function send(cmd) {
return new Promise((resolve) => {
client.once('message', (msg) => resolve(msg.toString().trim()));
client.send(cmd + '\r\n', PORT, HOST);
});
}
(async () => {
console.log(await send('hello')); // hallo
console.log(await send('play,comp_main')); // OK
client.close();
})();netcat / socat (quick diagnostics)
bash
# Send a single command and print the response
echo -ne "hello\r\n" | socat - UDP:192.168.1.5:8200
# Interactive session (socat keeps the socket open)
socat - UDP:192.168.1.5:8200TCP vs UDP — When to Use Which
| Scenario | Recommended |
|---|---|
| Show control system with persistent connection | TCP |
| Embedded controller / microcontroller with UDP only | UDP |
| Single fire-and-forget trigger commands | UDP |
| Polling status at high frequency | TCP |
| Multi-step workflows requiring ordered responses | TCP |
Firewall
Ensure UDP port 8200 is open on the Exaplay machine:
batch
netsh advfirewall firewall add rule name="Exaplay UDP Control" ^
dir=in action=allow protocol=UDP localport=8200See Also
- HTTP REST API — JSON-based HTTP API (port 8123) with equivalent seeking and transport commands
- TCP Command API — persistent connection variant of this API
