Skip to content
Documentation
Download
English

Connect to Sound Show 2

Control a running Sound Show 2 app from your own script, hardware controller, or integration. The WebSocket API can launch playables, control players and outputs, run cues, and send live state back to your controller.

This guide covers protocol v2. It connects directly to Sound Show on your computer or local network. You do not need a website API key or an account access token. Project editing and file uploads are outside this API.

  1. Open Sound Show 2 on a desktop computer and load your project. The host needs Pro access, including an active Pro trial. See Free and Pro.
  2. Open Options → Integrations → Remote Server. You can also use the Remote panel and its settings button.
  3. Choose a Port (the default is 8080) and a Password of at least six characters. Save your settings. Use a unique password for this connection.
  4. Start the server using its connection button. If you leave the password empty, confirm the app’s passwordless-access warning.
  5. Copy the address shown by Sound Show. Keep Serve web page enabled if you also want to use the built-in browser remote; the WebSocket endpoint works with that option disabled too.

The browser remote and WebSocket API share the same host and port.

Purpose Example on the Sound Show computer
WebSocket connection ws://127.0.0.1:8080/ws
Built-in browser remote http://127.0.0.1:8080/
Server health http://127.0.0.1:8080/health

From another device, replace 127.0.0.1 with the Sound Show computer’s address, for example ws://192.168.1.20:8080/ws. Use your configured port if it differs from 8080. localhost always means the device running your client.

The health endpoint returns an object such as {"status":"ok","protocol_version":2}. It confirms that the server is reachable; it does not authenticate your client.

By default, Sound Show checks the browser’s Origin header. It must be an HTTP origin using the server’s port and an accepted host: localhost, the detected LAN address, or the configured advertised host. Opening the built-in remote at the address displayed by Sound Show supplies an accepted origin.

A custom page hosted on another device, website, or development-server port needs the Allow external origins option described below. Native clients, including the JavaScript terminal example on this page, can omit Origin and do not need this option.

Enable this when you host your own browser remote separately from Sound Show:

  1. In Sound Show, open Options → Integrations → Remote Server, or open the Remote panel’s settings.
  2. Set a remote Password, then enable Allow external origins and apply the settings. The option is off by default. If it is missing, update Sound Show to a version that includes it.
  3. Open your custom remote page on a device that can reach the Sound Show computer. Connect its WebSocket to Sound Show’s address, not the address hosting your page, and send the normal hello message with the password.

For example, a controller served at http://192.168.1.30:3000 can connect to Sound Show at ws://192.168.1.20:8080/ws. Your browser script can start with:

const remotePassword = window.prompt("Sound Show remote password");
if (remotePassword !== null) {
const socket = new WebSocket("ws://192.168.1.20:8080/ws");
socket.addEventListener("open", () => {
socket.send(JSON.stringify({
type: "hello", version: 2, password: remotePassword,
features: ["runtime_snapshots"],
}));
});
socket.addEventListener("message", (event) => {
const message = JSON.parse(event.data);
console.log(message); // Handle hello_rejected, errors, and snapshots here.
});
}

Replace the example address with your host and port. Wait for the first state before enabling playback controls, and use the snapshot-handling logic in the complete JavaScript example. Keep the password in your controller’s session rather than embedding it in a publicly hosted page.

The option accepts HTTP and HTTPS page origins with different hosts or ports. The password handshake and Pro requirement still apply. Saving a change to the option takes effect immediately and disconnects clients, so reconnect and authenticate again. This also ends existing external sessions when you switch the option off. The built-in browser remote works with the option off.

Browser restrictions still apply. For a simple LAN setup, serve your custom page over HTTP. An HTTPS page may block a connection to plaintext ws://; enabling this option does not provide a secure wss:// endpoint. An HTTPS controller needs a browser-compatible secure connection, such as a TLS proxy you configure separately. Browsers may also require local-network permission or impose other network restrictions. The option does not add CORS headers to HTTP endpoints. file:// pages and opaque null origins remain rejected: serve a local HTML file through an HTTP server instead.

Send one JSON text message immediately after the socket opens (within ten seconds). Include the remote password configured in Sound Show:

{
"type": "hello",
"version": 2,
"password": "your-remote-password",
"features": ["runtime_snapshots"]
}

For a confirmed passwordless server, omit password or use an empty string. features is optional; requesting runtime_snapshots gives your client separate live updates for control state, instructions, playlists, and playable activity.

On success, the server sends:

{
"type": "hello_ok",
"protocol_version": 2,
"requested_version": 2,
"state_mode": "full_snapshot",
"features": ["ping", "runtime_snapshots"]
}

A full state message follows automatically. Wait for it before selecting a target or sending show commands. Later messages replace either the full state or one named part of it. There is no subscription command and no per-command success acknowledgement: the resulting state is authoritative.

On failure, expect hello_rejected with reason and message, followed by disconnection. Reasons include invalid_password, unsupported_version, and rate_limited. See errors and limits.

Save this as soundshow-client.mjs. Run it with Node.js 22.12 or later; it uses the built-in WebSocket client and needs no packages. It lists your project’s playables and follows live state. It only starts playback when you pass a playable ID as a command-line argument.

const url = process.env.SOUNDSHOW_REMOTE_URL ?? "ws://127.0.0.1:8080/ws";
const password = process.env.SOUNDSHOW_REMOTE_PASSWORD ?? "";
const playableId = process.argv[2];
const ws = new WebSocket(url);
let state = null;
let firstSnapshot = true;
const domains = {
control_snapshot: "control",
players_snapshot: "players",
instructions_snapshot: "instructions",
playlists_snapshot: "playlists",
playable_runtime_snapshot: "playable_runtime",
logical_devices_snapshot: "logical_devices",
timers_snapshot: "timers",
audio_controls_snapshot: "audio_controls",
lights_snapshot: "lights",
};
function send(command) {
if (!state || ws.readyState !== WebSocket.OPEN) {
throw new Error("Wait for the first state snapshot before sending commands.");
}
ws.send(JSON.stringify(command));
}
ws.addEventListener("open", () => {
ws.send(JSON.stringify({
type: "hello", version: 2, password, features: ["runtime_snapshots"],
}));
});
ws.addEventListener("message", (event) => {
const message = JSON.parse(event.data);
if (message.type === "hello_rejected" || message.type === "error") {
console.error(message.reason ?? message.code ?? "error", message.message);
return;
}
if (message.type === "state") {
state = message.state; // Replace the entire state, even after a project change.
} else {
const domain = domains[message.type];
if (!state || !domain) return; // Ignore unknown additive message types.
if ((domain === "audio_controls" || domain === "lights") &&
message.revision < (state[domain]?.revision ?? -1)) return;
state = { ...state, [domain]: message[domain] };
}
if (firstSnapshot) {
firstSnapshot = false;
const playables = (state.project?.categories ?? []).flatMap((category) =>
category.playables.map((p) => ({
id: p.id, name: p.name, kind: p.kind, category: category.name,
}))
);
console.table(playables);
if (playableId) {
if (playables.some((p) => p.id === playableId)) {
send({ type: "play_playable", playable_id: playableId, action: "start" });
} else {
console.error("That playable ID is not in the current project.");
}
}
console.log("Connected. Press Ctrl+C to disconnect.");
}
// Use `state` here to update your controller after every snapshot.
});
ws.addEventListener("error", () => console.error("Connection failed. Check the server address and settings."));
ws.addEventListener("close", (event) => {
state = null;
console.log("Disconnected:", event.code, event.reason);
});
process.on("SIGINT", () => ws.close());

In PowerShell, set the address and your remote password, then list playables:

Terminal window
$env:SOUNDSHOW_REMOTE_URL = 'ws://127.0.0.1:8080/ws'
$env:SOUNDSHOW_REMOTE_PASSWORD = 'your-remote-password'
node soundshow-client.mjs

In a macOS or Linux terminal:

Terminal window
export SOUNDSHOW_REMOTE_URL='ws://127.0.0.1:8080/ws'
export SOUNDSHOW_REMOTE_PASSWORD='your-remote-password'
node soundshow-client.mjs

Keep your real password out of shared scripts. For a passwordless server, use an empty value. Stop the script with Ctrl+C, then copy a playable ID from the table and run it again to trigger that playable once:

Terminal window
node soundshow-client.mjs "paste-a-playable-id-here"

The example does not reconnect automatically or replay commands. Before building a long-running controller, add the connection recovery behavior and choose commands from the reference.

Symptom What to check
Connection refused or times out Keep Sound Show open and start the server. Check the port, host address, firewall, and whether both devices can reach each other on the network.
Server will not start Check Pro access, the connection log, and whether another application already uses the port.
HTTP 403 during connection Check Pro access and browser origin. For a page hosted on another device, port, or website, enable Allow external origins in Sound Show.
Custom remote still cannot connect after allowing external origins Check the Sound Show host address, firewall, and browser console. The option does not override mixed-content or local-network restrictions; use the HTTP LAN setup described above.
invalid_password Use the Remote Server password, not your Sound Show account password.
unsupported_version Send version: 2 and use /ws. Sound Show 1 messages are incompatible.
HTTP 429 or rate_limited Stop rapid retries or commands and follow the retry guidance.
Connected but no state Send hello first. Check for a rejection or malformed JSON.
A command has no effect Resolve IDs from the latest state; check locks, target availability, licensing, and error messages.
The controller display becomes stale Handle domain snapshots as well as full state messages. Silence alone does not mean the connection is lost.

Use /ws instead of /SoundShow, and a lowercase type field instead of $type and .NET class names. Send the v2 hello handshake. Use player IDs and logical-device IDs instead of numeric player or output indexes. Icons are HTTP icon_url resources, and state arrives as full or domain snapshots.