How to Control Backstage via the JSON Remote Control Protocol
How to Control Backstage via the JSON Remote Control Protocol
Drive Backstage playback, audio, calibration, and node-graph endpoints from an external control system (Crestron, AMX, Q-SYS, or a custom application) using a structured JSON protocol over TCP or UDP.
Looking for a different protocol? Backstage supports several control interfaces. Choose the one that fits your integration:
OSC -- standard Open Sound Control messages. Best for systems that already speak OSC. See How to Control Backstage via OSC.
REST API -- HTTP request/response. Best for web-based dashboards or stateless queries. See Integrate with Backstage via the REST API.
UDP trigger strings -- lightweight fire-and-forget playback triggers via the StepperCommander node. See Trigger Playback via UDP.
JSON Remote Control Protocol (this page) -- structured JSON messages over TCP/UDP with typed domains, named addressing, and asynchronous live feedback. Best when you need full bidirectional control of sequencers, steppers, broadcast schedulers, calibrators, audio outputs, and node-graph endpoints from a single connection.
Before you start
- Backstage is running with at least one sequence, stepper, or other entity you want to control
- You know the IP address of the Backstage server on your control network
- Your control system can send TCP or UDP messages (plain JSON text, newline-terminated)
- If using the reference control-panel app: .NET 10 SDK installed on a Windows machine
Steps
1. Enable the protocol in Settings
Open Settings and expand the Remote Control section. Check Enable Show-Control Protocol. This starts the TCP server immediately.
By default, the TCP server listens on port 7400 on all network interfaces. UDP is disabled by default. To enable UDP, check UDP and confirm the port (default 7401).
[USER-ACTION-REQUIRED: Attach a screenshot of the Settings > Remote Control section here]
2. Restrict access with the IP allowlist
In the same Remote Control section, enter allowed client IPs or CIDR ranges in the IP allowlist field (comma-separated, e.g. 10.0.0.5, 192.168.1.0/24). Leave empty to allow connections from any address.
If you want to require authentication, enter a string in the Auth token field. Clients must then send an auth message before any control commands are accepted.
3. Connect over TCP
From your control system, open a TCP connection to the Backstage server on port 7400 (or your configured port). The protocol uses newline-delimited JSON (NDJSON) -- send exactly one JSON object per line, terminated with a newline.
If an auth token is configured, authenticate first:
{"domain":"sys","action":"auth","args":{"token":"your-token"}}
Verify the connection with a ping:
{"domain":"sys","action":"ping"}
You should receive:
{"ok":true,"result":{"pong":true}}
Quick manual test: Use
ncat <host> 7400, then paste a request line and press Enter.
4. Connect over UDP (optional)
If UDP is enabled, send datagrams to port 7401. Each datagram carries one or more newline-separated JSON messages. UDP is connectionless -- responses and live feedback go back to the sender's address, or to a fixed host:port if configured in the UDP feedback dropdown.
UDP is best for fire-and-forget commands on a trusted control LAN. For reliable bidirectional communication with live feedback, use TCP.
5. Address entities by name
Every entity in Backstage is addressed by the exact name you see in the application (case-sensitive). There are no numeric IDs or indices to track.
Use the list action on any domain to discover current names:
{"domain":"seq","action":"list"}
Response:
{"ok":true,"result":{"items":[{"name":"Show A","state":"Stopped","time":0,"duration":120},{"name":"Show B"}]}}
Then target a specific entity by name:
{"domain":"seq","action":"play","target":"Show A"}
The protocol covers these domains:
|
Domain |
Controls |
|---|---|
|
|
Session management: ping, version, auth, subscribe/unsubscribe |
|
|
Steppers: play, pause, stop, next, goto step |
|
|
Sequencers: play, pause, stop, seek, jump, tempo, fade |
|
|
Broadcast Schedulers: enable/disable, force entry, clear |
|
|
Calibrator Lite: start alignment, start black level, cancel, restore snapshots |
|
|
Audio Output mixers: volume (master or per-channel), mute |
|
|
Audio Matrix Mixers: volume (master or per-channel), mute |
|
|
Node-graph endpoints: send values in, read values out |
6. Subscribe to live feedback
For real-time status updates, subscribe to one or more domains:
{"domain":"sys","action":"subscribe","args":{"domains":["seq","step","output"]}}
Omit the domains argument to subscribe to all domains. After subscribing, Backstage pushes:
-
An immediate snapshot of all current status
-
Live events whenever a subscribed entity's state changes
-
A periodic heartbeat (~3 seconds) that re-sends current status, so any dropped message self-heals
Events arrive as unsolicited messages on the same TCP connection:
{"type":"event","domain":"seq","status":{"name":"Show A","state":"Playing","time":12.5,"duration":120,"looped":false,"tempo":1.0,"fade":1.0}}
7. Use Remote Control nodes in the Node Editor
For deeper integration with the node graph, use the dedicated bridge nodes in the Network category:
-
Network:Remote Control In -- receives values sent via the
nodedomain. Set a Command name in the node; external systems send to this name. The node outputs the value as a String, as a Float (parsed number), and fires a Bang on every received message. -
Network:Remote Control Out -- publishes values from the node graph to external subscribers. Set a Feedback name in the node; subscribers receive updates when the input value changes.
Additionally, any labelled datatype node (Float, Int, String, Color, Bang) automatically becomes a control endpoint when you give it a non-empty Label. Clearing the label hides it from the protocol.
Use node.list to discover all available endpoints:
{"domain":"node","action":"list"}
Response includes a dir field indicating the endpoint's direction: in (settable only), out (readable only), or inout (both).
Send a value to an endpoint:
{"domain":"node","action":"send","target":"houselights","args":{"value":"0.5"}}
8. Use the reference control-panel application
A standalone Backstage Control Panel ships with Backstage (in tools/BackstageControlPanel/). It is a C# WPF application that speaks the full protocol and serves as both a testing tool and a reference implementation for integrators.
To run it:
cd tools/BackstageControlPanel dotnet build -c Release dotnet run -c Release
The control panel has:
-
A connection bar at the top: enter Host, Port, and Token, then click Connect. The panel auto-reconnects if the connection drops.
-
Seven tabs for each entity type: Steppers, Sequencers, Broadcast Schedulers, Calibrators, Output (audio), Matrix Mixer (audio), and Nodes. Each tab lists entities with live status and provides control buttons.
-
A Protocol JSON log panel at the bottom (collapsed by default). Expand it to see every sent and received JSON message in real time. Use Pause to freeze the log, Clear to reset it, or Copy All to copy the full log to the clipboard.
[USER-ACTION-REQUIRED: Attach a screenshot of the Backstage Control Panel application here]
Options explained
Settings (Remote Control section)
|
Setting |
Description |
Values / Range |
|---|---|---|
|
Enable Show-Control Protocol |
Master on/off for the JSON control protocol |
Checkbox (off by default) |
|
Bind interface |
Network interface the protocol listens on |
Dropdown: Any (0.0.0.0) or a specific NIC |
|
TCP |
Enable or disable the TCP transport |
Checkbox (on by default when protocol is enabled) |
|
TCP Port |
Port number for the TCP server |
1--65535 (default 7400) |
|
UDP |
Enable or disable the UDP transport |
Checkbox (off by default) |
|
UDP Port |
Port number for UDP receive |
1--65535 (default 7401) |
|
UDP feedback |
Where to send UDP responses and events |
Dropdown: Reply to sender / Fixed host:port |
|
Feedback host |
IP address for fixed-target UDP feedback (visible when "Fixed host:port" selected) |
IP address |
|
Feedback port |
Port for fixed-target UDP feedback |
1--65535 (default 7402) |
|
CRLF line endings |
Use CRLF instead of LF as line terminator in responses |
Checkbox (on by default) |
|
Auth token |
Require clients to authenticate before issuing commands |
Text (empty = no auth) |
|
IP allowlist |
Restrict which IPs/CIDRs may connect |
Comma-separated (empty = allow any) |
|
Feedback rate (Hz) |
How often the server checks for and pushes status changes |
1--60 Hz (default 10) |
Key message types
|
Message |
Description |
Example |
|---|---|---|
|
|
Connection health check |
|
|
|
Protocol version query |
Returns |
|
|
Start receiving live status events |
|
|
|
Jump to a specific step (1-based) |
|
|
|
Start playback of a sequencer |
|
|
|
Seek to an absolute time position (seconds) |
|
|
|
Set playback speed (1.0 = normal) |
|
|
|
Set audio output volume (linear, 1.0 = unity) |
|
|
|
Mute/unmute a matrix mixer |
|
|
|
Push a value into a node-graph endpoint |
|
|
|
Start camera calibration alignment |
|
Common Mistakes
-
Forgetting the newline -- Every JSON message must be terminated with a newline character (LF, or CR+LF). Without it, the server buffers the partial line and never processes the message. Make sure your control system appends the line terminator to each message it sends.
-
Case mismatch on target names -- Entity names are matched exactly (case-sensitive).
"Show A"and"show a"are different targets. Use thelistaction to confirm the exact name spelling. -
Sending control commands before authenticating -- If an auth token is configured in Backstage, every command returns error
401until you send a validsys.authmessage. If you get unexpected 401 errors, check whether a token is set in Settings > Remote Control > Auth token. -
Assuming control responses confirm the action completed -- A successful response (
"accepted": true) only means Backstage received and queued the command. The action is applied asynchronously. Subscribe to live events or poll withstatusto confirm the actual state change. -
Using UDP on an untrusted network -- UDP is connectionless and source-spoofable. Only use it on a dedicated, trusted control LAN. For production integrations, prefer TCP.
Tips
-
Use the
idfield in your requests (any JSON value -- a number, string, or UUID). Backstage echoes it back in the response, which lets async clients correlate requests with responses when multiple messages are in flight. -
The Backstage Control Panel app's Protocol JSON log is the fastest way to debug integration issues. Pause the log, reproduce the problem, then inspect the exact JSON exchanged.
-
Ports 7400/7401 were chosen to avoid conflicts with common AV protocols (NDI, Dante, ST 2110, Q-SYS, Crestron, AMX). If your site runs DDS/RTPS middleware, which also uses the 7400 range, change the port in Settings to avoid a clash.
-
The periodic heartbeat (~3 s) re-sends current status to all subscribers. This means a dropped event frame self-heals within a few seconds -- you do not need to implement your own polling loop.
-
You can give any Datatype:Float, Datatype:Int, Datatype:String, Datatype:Color, or Datatype:Bang node a non-empty Label to expose it as a control endpoint without adding dedicated Remote Control nodes. This is convenient for quick integrations where you want to control an existing graph variable externally.
Related
-
How to Control Backstage via OSC -- Open Sound Control integration for systems that already speak OSC.
-
Integrate with Backstage via the REST API -- HTTP-based control for web dashboards and stateless queries.
-
Trigger Playback via UDP -- Lightweight UDP string commands via the StepperCommander node.
-
Build a Multi-Step Show -- Setting up steppers and sequences for show playback.
-
Your First Graph -- Getting started with the Node Editor.
