Skip to content

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 cuelist identifier (returned by get:type, used in get:cuelist, etc.). This is intentional so existing integrations continue to work — treat cuelist and CueList in the protocol as synonyms for Playlist in the UI.

Connection ​

ParameterValue
ProtocolUDP (datagram)
Default Port8200
ConfigurationMenu → Settings → Communication → UDP Listen
Line terminatoroptional — trailing \r, \n and spaces are ignored
EncodingUTF-8

Each datagram contains exactly one command — nothing is split inside a datagram, so comp1.play\r\ncomp2.play is read as one command (and answered ERR,command_failed). Datagrams are processed one at a time; while a project is loading they are dropped without a reply. 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 takes an ADDRESS:PORT entry. Use * as the address to listen on every network interface (one socket on 0.0.0.0, loopback included — see the TCP notes):

*:8200

To listen on a specific interface only:

192.168.1.5:8200

Leave the field empty to disable UDP control.

Command Format ​

UDP carries the same two syntaxes as TCP. The dotted syntax is the primary one:

OBJECT_PATH.VERB[=ARGUMENT]
→ comp_main.play
← OK\r\n

→ comp_main.cue.go=Blackout
← OK\r\n

→ comp_main.get:status
← 1,42.1230,2527,2,0\r\n

→ system.showmode=on
← OK\r\n

See Dotted command syntax in the TCP API reference for the full verb list and its error replies — transport (play/pause/stop/restart/set:time=), cues (cue.go=<index|name>, cue3.go), next/prev (Playlist items, Timeline cue markers), media3.play (a Playlist item), setters (set:vol=, set:alpha=, set:loop=, generic set:<property>=<value>), getters (get:status, get:cuelist, get:cueinfo=, generic get:<property>) and the system.* verbs (Show Mode, restart, PJLink). As over TCP, system.pjlink= only reaches a projector configured under Control → TCP Devices, and on a multi-client master a set: on composition content is passed on to every client.

The classic comma form remains fully supported:

COMMAND,COMPOSITION_ID[,ARGUMENT]
  • Fields are separated by commas — no space after a comma; a field in double quotes may contain commas, and the quotes are removed (set:cue,comp1,"Act 1, Scene 2").
  • The first field is always the command name.
  • Commands that target a composition take the composition's ID (its variable name, e.g. comp1) or its name (case-insensitive; _ and space are interchangeable) as the second field — the same addressing rules as TCP.
  • Retrieve IDs and names with get:complist, or read them off the composition's Inspector (Show Control section).
  • The response is returned as a single UDP datagram. Multi-line responses (list commands) have each line delimited by \r\n within that datagram and are terminated by a final END\r\n line.

Global Commands ​

These commands do not require a composition ID.

CommandResponseDescription
hellohalloConnection test / keep-alive
get:verExaplay,<version>Get engine version string
get:complistOne line per composition, then ENDList all compositions
get:projname<project name>Get the loaded project name
showmode:onOKEnable global Show Mode
showmode:offOKDisable global Show Mode
showmode:toggleOKToggle global Show Mode
get:showmode1 or 0Get current Show Mode state

Note: exit/quit is not available over UDP — there is no session to close; it answers ERR,unknown_command.

Dotted equivalents: system.get:ver, system.get:projname, system.showmode=on|off|toggle, system.get:showmode. The rig-wide switches work the same way over UDP: system.blank=on|off|toggle[,<fade s>], system.blankfade=<s>, system.audiomute=…, system.identify=…, system.playall, system.stopall — see the TCP Command API.

A screen's calibration switches work over UDP too, addressed by the screen's ID: screen1.calibration=on|off|toggle, screen1.calibration.blend=…, screen1.calibration.black=…, screen1.get:calibration[.blend|.black] and screen1.calibration.reload — see Screen verbs.

get:complist response format ​

<varname>,<display name>\r\n
<varname>,<display name>\r\n
...
END\r\n

Example:

→ get:complist
← comp_main,Main Show\r\ncomp_lobby,Lobby Loop\r\nEND\r\n

Composition Commands ​

All composition commands use the classic format COMMAND,COMPID[,ARGUMENT]. Each has a dotted equivalent — see the TCP reference.

Transport ​

CommandArgumentsResponseDescription
play,COMPID—OKStart playback
pause,COMPID—OKPause playback
stop,COMPID—OKStop playback
set:cuetime,COMPID,SECONDSSECONDS — floatOKSeek to position (seconds). On a Playlist, the time within the current item (as get:status reports it). A stopped composition stays stopped

Audio ​

CommandArgumentsResponseDescription
get:vol,COMPID—<0–100>Get volume (integer %, truncated)
set:vol,COMPID,VALUEVALUE — number 0–100 (out-of-range values are clamped)OKSet volume

set:vol, set:alpha and set:loop sent to a multi-client master reach every client, as they do over TCP.

Opacity ​

CommandArgumentsResponseDescription
get:alpha,COMPID—<0–100>Get master alpha (integer %, truncated)
set:alpha,COMPID,VALUEVALUE — number 0–100 (clamped)OKSet master alpha

Playback Options ​

CommandArgumentsResponseDescription
get:loop,COMPID—0 or 1Get loop state
set:loop,COMPID,VALUEVALUE — on/off, true/false or 1/0OKEnable/disable looping

Status and Info ​

CommandResponse formatDescription
get:name,COMPID<name>Get display name
get:type,COMPIDtimeline, cuelist, or compositionGet composition type
get:itemcount,COMPID<count>Playlist: number of items. On a Timeline, clips placed on tracks are not counted
get:status,COMPIDSTATUS,TIME,FRAME,CUEINDEX,TOTALFull playback status (see below)
get:duration,COMPIDDepends on type (see below)Total duration
get:playingitem,COMPIDDepends on type (see below)Currently playing item
get:cuename,COMPID<name>Name of the currently active cue

get:status response fields:

FieldValuesDescription
STATUS0 stopped, 1 playing, 2 pausedPlayback state
TIMEseconds, 4 decimalsTimeline: timeline position. Playlist: position within the current item
FRAMEintegerCurrent frame number (TIME × 60)
CUEINDEXintegerTimeline: active cue index. Playlist: 1-based current item, 0 if none
TOTALseconds, shortest form (0, 184.2)Playlist: duration of the current item. Always 0 on a Timeline

get:duration response:

  • Timeline: DURATION — end of the last clip or cue marker, 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. On another type they answer ERR,unknown_command. The classic next,/prev, are Playlist-only — on a Timeline use the dotted comp_main.next / comp_main.prev (next / previous cue marker).

CommandArgumentsResponseDescription
set:cue,COMPID,INDEXINDEX — cue variable indexOKJump to cue by index and trigger it
set:cue,COMPID,NAMENAME — cue name (case-insensitive)OKJump to cue by name and trigger it
get:cuelist,COMPID—One line per cue, then ENDList all cues
get:cueinfo,COMPID,INDEXINDEX — cue variable index (index only; COMPID.get:cueinfo=NAME takes a name)INDEX,NAME,OFFSET,ACTIVEDetails about a specific cue

A purely numeric argument is always treated as the index; anything else is matched (ASCII case-insensitively) against the cue names of this composition. Everything after the second comma counts as the name, so names containing commas work.

get:cuelist (Timeline) response format:

<index>,<name>,<time-offset seconds>\r\n
...
END\r\n

get:cueinfo (Timeline) response fields: INDEX,NAME,OFFSET_SECONDS,IS_ACTIVE


CueList Commands ​

These commands are only valid when the composition is a CueList (Playlist). On another type they answer ERR,unknown_command.

CommandArgumentsResponseDescription
set:cue,COMPID,INDEXINDEX — 1-based item numberOKPlay item at the given position
set:cue,COMPID,NAMENAME — item name (case-insensitive)OKPlay item by name
next,COMPID—OKAdvance to the next item. Nothing playing → start the first item
prev,COMPID—OKGo back to the previous item. Nothing playing → start the last item
get:cuelist,COMPID—One line per item, then ENDList all items
get:cueinfo,COMPID,INDEXINDEX — 1-based item number (index only)INDEX,NAME,FILE,DURATIONDetails about a specific item

get:cuelist (CueList) response format:

<1-based index>,<name>,<file path>\r\n
...
END\r\n

Note: Timeline cue indices match the cue's variable index (arbitrary integer). CueList item indices are always 1-based sequential numbers.


Inputs (tracking channels) ​

The track,… lines of TCP › Inputs work over UDP too, with the same replies:

CommandMeaning
track,CHANNEL,X[,Y]Position (Y optional)
track,CHANNEL,x,VALUEX only
track,CHANNEL,y,VALUEY only
track,CHANNEL,click[,VALUE]Click (value defaults to 1)

Error Responses ​

All error responses begin with ERR,:

ErrorMeaning
ERR,unknown_commandCommand string not recognised — also a composition command without its composition, a type-specific command on the wrong composition type, or a dotted line containing >
ERR,composition_not_foundNo composition matches the given ID or name (also a global command with extra fields; also classic commands with no project loaded)
ERR,missing_argumentRequired argument was not supplied
ERR,cue_not_foundThe cue/item index does not exist
ERR,none_playingRequested info about a playing item, but nothing is playing
ERR,no_next_itemnext reached the end of the cue list
ERR,already_at_firstprev called when the first item is already playing
ERR,cuelist_emptynext/prev called on an empty cue list
ERR,unsupported_typeget:playingitem, get:cuename or get:duration on a plain composition
ERR,no_projectClassic get:projname with no project loaded (a dotted command answers ERR,command_failed then)
ERR,no_devicessystem.pjlink-all with no PJLink projector configured
ERR,command_failedA dotted command could not be resolved or failed — also system.pjlink= to an address that is not a configured projector
ERR,track_expected_channel, ERR,track_expected_value, ERR,track_bad_argsA malformed track,… line (see Inputs)
ERR,exceptionAn internal error occurred

Example Sessions ​

Connection test ​

→ hello
← hallo\r\n

Discover and play a composition ​

→ get:complist
← comp_main,Main Show\r\ncomp_lobby,Lobby Loop\r\nEND\r\n

→ comp_main.play              (classic: play,comp_main)
← OK\r\n

→ comp_main.get:status        (classic: get:status,comp_main)
← 1,12.3456,740,0,0\r\n

Work with a CueList ​

→ comp_lobby.cue.go=2         (classic: set:cue,comp_lobby,2)
← OK\r\n

→ comp_lobby.next             (classic: next,comp_lobby)
← OK\r\n

Work with a Timeline ​

→ comp_main.cue.go=2          (classic: set:cue,comp_main,2)
← OK\r\n

→ comp_main.cue.go=Scene B    (classic: set:cue,comp_main,Scene B)
← OK\r\n

Show Mode ​

→ system.showmode=on          (classic: showmode:on)
← OK\r\n

→ system.get:showmode         (classic: get:showmode)
← 1\r\n

Sending 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("comp_main.play"))    # OK

Node.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('comp_main.play'));  // 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:8200

TCP vs UDP — When to Use Which ​

ScenarioRecommended
Show control system with persistent connectionTCP
Embedded controller / microcontroller with UDP onlyUDP
Single fire-and-forget trigger commandsUDP
Polling status at high frequencyTCP
Multi-step workflows requiring ordered responsesTCP

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=8200

See 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

Exaplay 3 User Documentation