Getting-started guide for the JavaScript client and gateway.
Binding: bindings/js/sdk/ (@asls/wsc-sdk)
Protocol version: 1.1.0
Runtime: Node.js ≥ 20 (gateway) · Any modern browser or Node.js ≥ 20 (client)
implementations/js/
├── README.md ← you are here
├── client/ ← browser / Node.js WebRTC client
│ ├── src/
│ │ └── main.js ← WscClient class
│ └── example/ ← full interactive protocol explorer (Vite dev server)
└── server/ ← gateway (Node.js, @roamhq/wrtc)
└── src/
├── main.js ← entry point
├── server.js ← WSCServer singleton
├── signaling.js ← WebSocket signaling server
├── peer.js ← WscPeer + WscPeerManager
├── gateways/
│ ├── abstract.gateway.js
│ ├── stream.gateway.js ← STREAM_CHANNELS, STREAM_TIMECODE
│ ├── control.gateway.js ← CONTROL_CUE, CONTROL_PARAM
│ └── state.gateway.js ← STATE_QUERY
└── protocols/
├── dmx/
│ └── dmx.artnet.js ← Art-Net UDP forwarder
└── osc/
└── osc.udp.js ← OSC UDP forwarder
The gateway accepts WebRTC DataChannel connections from clients and forwards WSC packets to downstream protocols.
# In the repository root
npm install# In the repository root
# Development (auto-restart on file change, requires tsx)
npm run dev:server # Start the WSC ServerThe gateway listens on port 4515 by default. This port is used for both the WebSocket signaling server and is referenced as ASLS_WSC_PORT.
On startup, the gateway:
- Starts the WebSocket signaling server on port 4515.
- Waits for clients to connect via WebRTC offer/answer exchange.
- Opens a DataChannel per message type for each connected peer.
- For each received packet, validates it and routes to the appropriate gateway module:
| Gateway module | Handled types | Forwarding |
|---|---|---|
WscStreamGateway |
STREAM_CHANNELS, STREAM_TIMECODE |
Art-Net (UDP) |
WscControlGateway |
CONTROL_CUE, CONTROL_PARAM |
OSC (UDP) |
WscStateGateway |
STATE_QUERY |
Responds with system info via STATE_ANSWER |
The example is a full interactive protocol explorer that exercises every message type. It runs in the browser via Vite.
# In the repository root
npm install# In the repository root
npm run dev:clientOpen http://localhost:5173 in a browser. Enter the gateway host and port (default: localhost:4515) and click Connect.
The example page provides UI panels for:
- DMX channel streaming (Art-Net)
- Linear timecode streaming
- Cue control (
CONTROL_CUE) - Parameter writes (
CONTROL_PARAM) - Raw tunnel (
TUNNEL_RAW) - State query / keepalive
Install the dependencies in your project:
npm install -S @asls/wsc-sdk
npm install -S @asls/wsc-clientImport the client and the SDK binding:
import { WscClient, WSC_REMOTE_STATE, KEEPALIVE_INTERVAL } from '@asls/wsc-client';
import { WscPacket, WscTransport, WscFlags, WscAddress } from '@asls/wsc-sdk';const client = new WscClient(
'192.168.1.50', // gateway host or IP
4515, // signaling WebSocket port
onOpen,
onMessage,
onClose,
onError,
);
client.connect();
function onOpen() {
console.log('Connected — WSC ready');
client.startKeepAliveSession();
}
function onMessage(packet) {
const decoded = WscPacket.decode(packet);
console.log(`← ${packet.typeName()}`, decoded);
}
function onClose() {
console.log('Disconnected');
}
function onError(err) {
console.error('Error:', err);
}const values = new Uint8Array(512).fill(0);
values[0] = 255; // channel 1 full
values[1] = 128; // channel 2 half
const packet = WscPacket.create(
WscPacket.Type.STREAM_CHANNELS,
{ universe: 0, startChannel: 1, values },
{
flags: new WscFlags(true, true), // TR + GW
transport: WscTransport.udp(
WscTransport.Protocol.ARTNET,
'127.0.0.1',
6454,
),
},
);
client.send(packet);const packet = WscPacket.create(
WscPacket.Type.CONTROL_CUE,
{
address: WscAddress.parse('lighting.cue.42'),
action: WscPacket.CueAction.START,
},
{
flags: new WscFlags(true, true), // TR + GW
transport: WscTransport.udp(
WscTransport.Protocol.OSC,
'127.0.0.1',
8000,
),
},
);
client.send(packet);const packet = WscPacket.create(
WscPacket.Type.CONTROL_PARAM,
{
address: WscAddress.parse('lighting.layer.2.intensity'),
valueType: WscPacket.ValueType.F32,
value: 0.75,
},
{
flags: new WscFlags(true, true),
transport: WscTransport.udp(
WscTransport.Protocol.OSC,
'127.0.0.1',
8000,
),
},
);
client.send(packet);const packet = WscPacket.create(
WscPacket.Type.STREAM_TIMECODE,
{ hours: 1, minutes: 0, seconds: 30, frames: 0, rate: 30 },
);
client.send(packet);const raw = new Uint8Array([0xF0, 0x42, 0x40, 0x7F, 0xF7]);
const packet = WscPacket.create(
WscPacket.Type.TUNNEL_RAW,
{ raw },
{
flags: new WscFlags(true, true),
transport: WscTransport.raw(),
},
);
client.send(packet);| Parameter | Type | Description |
|---|---|---|
host |
string |
Gateway hostname or IP address |
port |
number |
Signaling WebSocket port |
onOpen |
() => void |
Called when the first DataChannel is open |
onMessage |
(packet: WscPacket) => void |
Called for each received packet |
onClose |
() => void |
Called on disconnection |
onError |
(err: any) => void |
Called on transport or signaling error |
| Method | Description |
|---|---|
connect() |
Initiate WebSocket signaling and establish the DataChannel |
send(packet) |
Serialize and write a WscPacket to the appropriate DataChannel; silently drops if the channel is not open |
close() |
Disconnect cleanly and transition to IDLE |
startKeepAliveSession() |
Begin sending STATE_QUERY(KEEPALIVE) every 1 000 ms |
stopKeepaliveSession() |
Stop the keepalive timer |
| Property | Type | Description |
|---|---|---|
state |
WSC_REMOTE_STATE |
Current connection state |
debug |
DebugEntry[] |
Rolling debug log, last 100 entries |
| Value | Meaning |
|---|---|
IDLE (0) |
Not connected |
CONNECTING (1) |
Signaling in progress |
CONNECTED (2) |
DataChannel open |
ERROR (-1) |
Unrecoverable transport error |
The client creates one DataChannel per message type, named:
WSC!DC:<TYPE_NAME>
For example: WSC!DC:STREAM_CHANNELS, WSC!DC:CONTROL_CUE.
The gateway identifies WSC channels by the WSC!DC prefix. Response channels used by the server:
| Channel | Purpose |
|---|---|
WSC!DC:STATE_QUERY |
Carries STATE_ANSWER responses |
WSC!DC:STATE_ERROR |
Carries STATE_ERROR responses |
Always validate packets in development to catch flag and compatibility errors early:
const { valid, errors, warnings } = WscPacket.validate(packet);
if (!valid) {
console.error('Invalid packet:', errors);
return;
}
if (warnings.length) {
console.warn('Packet warnings:', warnings);
}
client.send(packet);let client = null;
function connect(host, port) {
client = new WscClient(host, port,
() => {
client.startKeepAliveSession();
},
(packet) => {
// handle incoming STATE_ANSWER, STATE_ERROR, etc.
},
() => {
client = null;
setTimeout(() => connect(host, port), 2000);
},
(err) => console.error(err),
);
client.connect();
}| Document | Contents |
|---|---|
| Architecture | System overview and gateway module model |
| Message Types | Full payload schemas for all 8 message types |
| Address System | Token registry and address construction rules |
| Transport Descriptor | Downstream protocols and compatibility matrices |
| Session | Connection lifecycle and keepalive specification |
| Error Handling | Error codes and receiver obligations |