WebSockets

Composer ships an embedded WebSocket server that pushes real-time events to subscribed clients and accepts a small set of command messages. This page covers the connection model, the message envelope, the catalogue of message types, and the subscription channels.

Note — not available on Core licences

The WebSocket server is disabled at startup under a Vindral Composer Core (free) licence, regardless of the EnableWebSockets setting. No listener is opened on the configured port and clients will fail to connect. See Activating a license for licence-tier details.

Connection

Property Value
URL ws://{host}:{port}/
Default host localhost (configurable; see the Web API tab in Settings)
Default port 8081
Protocol RFC 6455 WebSocket; UTF-8 JSON text frames
TLS Not provided directly — terminate at a reverse proxy if needed

The server is off by default. Enable it via the EnableWebSockets setting (see the WebSockets section of the settings.xml reference). Restart Composer for the change to take effect — the WebSocket server is started during application startup.

On a successful handshake the server immediately sends a Welcome message containing the connection's assigned Guid. Clients should keep that GUID and quote it back in subsequent commands so the server can route replies.

Authentication

The WebSocket server does not perform API-key authentication itself. Treat its port as trusted: bind to localhost for local-only access, or restrict the listening interface to a private network and front it with a TLS-terminating proxy (nginx, Caddy, etc.) that handles auth.

Use the HTTP API's WebSocket management endpoints to enumerate or disconnect connected clients on demand — those are protected by the same apikey mechanism as the rest of the HTTP API.

Message envelope

Every frame sent in either direction is a JSON object with this shape:

{
  "Type":     "PropertyChanged",
  "Content":  "<payload — usually a JSON string or human-readable string>",
  "Guid":     "<connection / receiver guid>",
  "ObjectId": "<related object guid, or all-zero if not applicable>",
  "DateTime": "2026-04-29T13:42:18.0000000Z"
}

Type is one of the values in the table below (case-sensitive). Content is the actual payload — a plain string for system messages, a JSON-encoded inner document for PropertyChanged / MultiplePropertiesChanged / ComponentLogUpdate, etc. Guid is the connection's identity (server-assigned in the Welcome message) and ObjectId references a Composer object when relevant.

Message types

Defined in WebSocketServer.Enums.SocketMessageType.

Type Direction Description
Welcome server → client First message after handshake; carries the server-assigned connection GUID.
System both Generic system messages — server heartbeats, ping/pong, ack strings.
Subscribe client → server Subscribe to one of the channels listed in the next section.
Unsubscribe client → server Cancel a subscription. Content is the channel name (or All).
PropertyChanged server → client One property on one object changed. Content is a JSON document with object id, property name, new value.
MultiplePropertiesChanged server → client Several properties changed in the same tick — coalesced for efficiency.
SetPropertyValueByObjectId client → server Request a property value change. Content is a JSON document with the target object id, property name, and value.
ExecuteCommandByObjectId client → server Invoke a Command property on a target object.
AudioMixerSummary server → client Periodic audio-mixer levels snapshot for clients subscribed to AudioMixer.
LogMessage server → client Log entry for clients subscribed to LogFile.
ThumbnailUpdate server → client New input thumbnail for clients subscribed to thumbnail pushes.
ComponentLogUpdate server → client Component log entry for clients subscribed to ComponentLogs.
SystemInformationUpdate server → client Periodic CPU / memory / GPU stats for clients subscribed to SystemInformation.
Error server → client Server-side error in response to a malformed or rejected client message.

Setting a property value

Send a SetPropertyValueByObjectId message to change a property on a component. Content is a JSON-encoded string whose decoded form names the target object, the property, and the new value:

{
  "ObjectId": "<target object guid>",
  "PropertyName": "Mute",
  "Value": "true"
}

The server stamps the incoming frame with the connection's own Guid, so clients don't need to set it. There is no reply on success; a rejected or malformed request comes back as an Error message. Read the applied value back from the live AudioMixerSummary (for audio properties) or via the HTTP API's /api/getproperty.

Most properties take a scalar Value — a number, string, enum name, or true/false.

Collection properties

A handful of properties hold a collection rather than a scalar, so their Value is a structured string rather than a plain number/string. The encoding is property-specific. The collection properties writable over the WebSocket today are:

Property Value format
PreFaderSendConfigurations JSON array of send-slot objects (the same shape the HTTP API's /api/setproperty accepts and /api/getproperty returns) — worked example below.
AudioChannelOutputMappings Comma-separated {audioStripId}:{gain}:{channelMapping} triples, e.g. b2e5afc4-…:0:OneTwo,d4ea516f-…:-6:ThreeFour, where channelMapping is an AudioChannelMapping enum name.

Read the current value back from the live AudioMixerSummary (it serialises each audio property in the same string form), or — for PreFaderSendConfigurations — via the HTTP API's /api/getproperty.

PreFaderSendConfigurations is an input's pre-fader audio sends; each array element is one send slot:

Field Meaning
Active Whether this slot routes audio.
TargetAudioStripId GUID of the destination audio channel strip (all-zero when inactive).
InputAudioSendType 0 = pre-fader (tapped before the source fader), 1 = post-fader.
TargetCh01..TargetCh08 Per-source-channel routing: 0 = none, 1..8 = destination channel.

The four send slots are fixed per input; to clear the sends, send four slots with "Active": false rather than an empty array.

Because the collection value is itself a JSON string, it ends up JSON-encoded twice — once as Value, and again inside the Content document. Building the message with JSON.stringify keeps the nesting correct:

// Route the input's audio pre-fader to two audio strips (slots 3-4 left inactive).
const sends = [
  { Active: true,  TargetAudioStripId: "b2e5afc4-d29f-4f72-b87b-c42045e73054",
    InputAudioSendType: 0, TargetCh01: 1, TargetCh02: 2, TargetCh03: 0, TargetCh04: 0,
    TargetCh05: 0, TargetCh06: 0, TargetCh07: 0, TargetCh08: 0 },
  { Active: true,  TargetAudioStripId: "d4ea516f-ab03-4d4c-b24d-bacbf92d7bd8",
    InputAudioSendType: 0, TargetCh01: 1, TargetCh02: 2, TargetCh03: 0, TargetCh04: 0,
    TargetCh05: 0, TargetCh06: 0, TargetCh07: 0, TargetCh08: 0 },
  { Active: false, TargetAudioStripId: "00000000-0000-0000-0000-000000000000",
    InputAudioSendType: 0, TargetCh01: 0, TargetCh02: 0, TargetCh03: 0, TargetCh04: 0,
    TargetCh05: 0, TargetCh06: 0, TargetCh07: 0, TargetCh08: 0 },
  { Active: false, TargetAudioStripId: "00000000-0000-0000-0000-000000000000",
    InputAudioSendType: 0, TargetCh01: 0, TargetCh02: 0, TargetCh03: 0, TargetCh04: 0,
    TargetCh05: 0, TargetCh06: 0, TargetCh07: 0, TargetCh08: 0 }
];

ws.send(JSON.stringify({
  Type: "SetPropertyValueByObjectId",
  Content: JSON.stringify({
    ObjectId: inputGuid,                       // the input that owns the sends
    PropertyName: "PreFaderSendConfigurations",
    Value: JSON.stringify(sends)               // the collection value is itself a JSON string
  })
}));

The decoded Content for the first slot above looks like this (the remaining slots follow the same shape):

{
  "ObjectId": "06b44dd8-01eb-4040-9239-8e3e0012a1e0",
  "PropertyName": "PreFaderSendConfigurations",
  "Value": "[{\"Active\":true,\"TargetAudioStripId\":\"b2e5afc4-d29f-4f72-b87b-c42045e73054\",\"InputAudioSendType\":0,\"TargetCh01\":1,\"TargetCh02\":2,\"TargetCh03\":0,\"TargetCh04\":0,\"TargetCh05\":0,\"TargetCh06\":0,\"TargetCh07\":0,\"TargetCh08\":0}, ...]"
}

Subscription channels

Send a Subscribe message with Content set to one of these values to start receiving the matching push type. Channel names are case-insensitive on subscribe; Unsubscribe accepts the same names plus the wildcard All.

Channel Pushed message types Notes
AudioMixer AudioMixerSummary Throttled to the rate set by WebSocketThrottledPropertiesFrequency.
LogFile LogMessage Streams every log entry written by Composer's logger. Volume can be high under debug logging — consider scoping.
Components PropertyChanged for non-audio [ApiProperty] properties Every API-exposed property of an input/operator/target that isn't an audio-mixer value — run state, source URL, errors, and so on. Audio-level properties stream on AudioMixer instead.
Switcher PropertyChanged, pre-filtered to switchers A Switcher's important properties — PGM / Preview source, blend / fade-to-black progress, transition durations — from any origin. Same data as the switcher SSE topic; the animated progress values are throttled to ~10 Hz.
ComponentLogs ComponentLogUpdate Per-component log entries (the same lines you'd see in the Desktop log panel for that component).
SystemInformation SystemInformationUpdate Periodic snapshot — CPU, RAM, GPU, frame budget, etc.
Thumbnails ThumbnailUpdate Pushed implicitly when an input registers for thumbnail updates; not a separate Subscribe channel today.

The set of properties the Components and AudioMixer channels deliver is defined by attributes, not a fixed list: a property is broadcast when it carries [ApiProperty]. It streams on AudioMixer when it is also marked [AudioProperty] (audio-mixer levels) and on Components otherwise. Each component's reference page in the Inputs, Operators, and Targets manuals badges its [ApiProperty] properties with the channel they stream on, so those per-component property tables are the authoritative list of what each subscription delivers.

An Unsubscribe message with Content: "All" cancels every subscription on the connection.

Lifecycle events

The Runtime API broadcasts coarse lifecycle events (project start/stop/reload, runtime exit, etc.) to all WebSocket clients via the RuntimeEvent message type. The full event list lives on the Runtime API WebSocket Events page.

Configuration

All WebSocket-related settings live under the WebSockets section of settings.xml:

  • EnableWebSockets — master on/off switch.
  • WebSocketsHostName and WebSocketsPort — listening interface and port.
  • WebSocketsMaxConnectedSubscribers — connection cap (default 1).
  • WebSocketsMaxIncomingMessageQueueLength / WebSocketsMaxOutgoingMessageQueueLength — back-pressure tuning.
  • WebSocketThrottledPropertiesFrequency — how often coalesced pushes (audio mixer, property changes) emit.
  • WebSocketMsgAudioDecimals / WebSocketMsgAudioRmsDecimals — precision of audio level values to keep payloads small.

Quick start (browser JavaScript)

const ws = new WebSocket('ws://localhost:8081/');

ws.addEventListener('open', () => {
  // Subscribe to system info pushes.
  ws.send(JSON.stringify({ Type: 'Subscribe', Content: 'SystemInformation' }));
});

ws.addEventListener('message', (event) => {
  const msg = JSON.parse(event.data);
  switch (msg.Type) {
    case 'Welcome':
      console.log('Connected as', msg.Guid);
      break;
    case 'SystemInformationUpdate':
      console.log('System info:', JSON.parse(msg.Content));
      break;
    case 'Error':
      console.warn('Server error:', msg.Content);
      break;
  }
});