Meld Studio's WebChannel API enables JavaScript clients to interact with various components of Meld Studio. It allows for the management of scenes, audio tracks, effects, and other session states, providing an RPC-like link between the web clients and the application's backend.
Meld Studio's WebSocket API is managed by a QWebChannel protocol server. This is used to provide a Qt-like API in Javascript, including Qt's concept of signals and slots (see below for a brief description).
The supported Javascript QWebChannel client protocol implementation can be found at https://packages.streamwithmeld.com/qt6.8/qwebchannel.min.js.
See Qt's Documentation for more details.
Qt's concept of signals and slots can be likened to JavaScript's event handling mechanisms but with a few key differences. We've provided a quick description below, but more information can be found in Qt's Signals and Slots Documentation.
In Qt, a signal is akin to an event being dispatched in JavaScript. It acts as a notifier that something significant has occurred within the object. For example, if a data model changes, a signal might be emitted to indicate this change, similar to firing a custom event in JavaScript.
Slots in Qt are similar to event listeners in JavaScript. They are functions that respond to a specific signal. When a signal is emitted, all connected slots are called automatically. Adding a slot is a listener is called connecting to the signal. These connections last for the lifetime of the objects and can be disconnected when notification is no longer desired.
This code block will get you up and communicating with Meld Studio in a web browser:
function connect() {
// Currently, Meld Studio will always listen on this address:port.
// This may be configurable in the future.
const address = "127.0.0.1";
const port = 13376;
var socket = new WebSocket(`ws://${address}:${port}`);
socket.onopen = function () {
// Store this somewhere so that it doesn't get GC'd.
const channel = new QWebChannel(socket, function (channel) {
// Optionally store `meld` for direct access to the Meld Studio API.
const meld = channel.objects.meld;
// Dump the version. If undefined, the API is version 1.
console.log(meld.version);
// -------------------------
// Example Property Read:
// This dumps an object containing all Scenes, Layers, Effects, and Audio
// keyed by ID.
console.log(meld.session.items);
// Status properties.
console.log(meld.isStreaming);
console.log(meld.isRecording);
// -------------------------
// Example Listener:
// Subscribing to receive notifications when the Session property changes.
meld.sessionChanged.connect(() => {
console.log("sessionChanged()");
});
// -------------------------
// Example Call:
// Will tell Meld Studio to toggle the recording state.
meld.toggleRecord();
});
};
socket.onclose = function () {
console.log("Closed");
};
socket.onerror = function (error) {
console.log("Error: ", error);
};
}| property | type | description |
|---|---|---|
| isStreaming | bool |
Indicates active streaming status. |
| isRecording | bool |
Indicates active recording status. |
| session | object |
Represents the current session state with a structured JSON object detailing scenes, tracks, layers, and effects. See Session Schema |
The session property is an object with a single key of items. items is an object that contains all scenes, tracks, layers, and effects in the current Meld Studio session and their relevant properties. Each item is stored in the items object by its identifier: /^[ 0-9A-F ]{ 32 }$/. These are guaranteed to be unique within a given session.
Example:
{
"items": {
"05731BDA0C5DE934359C8F7F24CE6C1B": {
"current": true,
"index": 1,
"name": "Just Chatting",
"staged": false,
"type": "scene"
},
"375F1BB5A322BCC7F60CAAD16EE859D7": {
"monitoring": false,
"muted": false,
"name": "Spoilers",
"parent": "89BFE4955144FBD6490ECBB453CA6639",
"type": "track"
},
"6A92BA3C4FCC18180F76D3EFE9167EF0": {
"height": 699,
"index": 1,
"name": "Fortnite",
"parent": "9DA5A633182EB0C0804E671B26A1B581",
"rotation": 0,
"type": "layer",
"visible": true,
"width": 1303,
"x": 265,
"y": 132
},
"7958A12F7C782F392B08A793218AB7E8": {
"height": 1080,
"index": 2,
"isPlaying": true,
"mediaSource": "file:///path/to/video.mp4",
"name": "Background",
"parent": "05731BDA0C5DE934359C8F7F24CE6C1B",
"rotation": 0,
"type": "layer",
"visible": true,
"width": 1920,
"x": 0,
"y": 0
},
"85DFC4BCD7451D2DB3C55A6E2D9948E2": {
"enabled": true,
"name": "Glow Styles",
"parent": "9F2A72924699F775E0E789C6940141F6",
"type": "effect"
},
"86DF5581538B7A73504368E4F02E242A": {
"monitoring": false,
"muted": false,
"name": "Background",
"parent": "7958A12F7C782F392B08A793218AB7E8",
"type": "track"
},
"89BFE4955144FBD6490ECBB453CA6639": {
"height": 720,
"index": 1,
"name": "Spoilers",
"parent": "05731BDA0C5DE934359C8F7F24CE6C1B",
"rotation": 0,
"type": "layer",
"url": "https://www.meldstudio.co/browser",
"visible": true,
"width": 1280,
"x": 118,
"y": 120
},
"8DB9CC06AFCA152EA30278380B569751": {
"height": 281,
"index": 0,
"name": "Camera Frame",
"parent": "05731BDA0C5DE934359C8F7F24CE6C1B",
"rotation": 0,
"source": "file:///path/to/image.png",
"type": "layer",
"visible": true,
"width": 500,
"x": 1354,
"y": 156
},
"9DA5A633182EB0C0804E671B26A1B581": {
"current": false,
"index": 0,
"name": "Gaming",
"staged": false,
"type": "scene"
},
"9F2A72924699F775E0E789C6940141F6": {
"height": 297,
"index": 0,
"name": "Camera",
"parent": "9DA5A633182EB0C0804E671B26A1B581",
"rotation": 0,
"type": "layer",
"visible": true,
"width": 264,
"x": 1436,
"y": 683
},
"D4D3457F8AF5D1F5381A903A8C43BE27": {
"monitoring": false,
"muted": true,
"name": "Microphone",
"type": "track"
},
"EAAF1F3600F53FE362F9CD8194CB1639": {
"monitoring": false,
"muted": false,
"name": "Fortnite",
"parent": "6A92BA3C4FCC18180F76D3EFE9167EF0",
"type": "track"
}
}
}| property | type | description |
|---|---|---|
| type | string |
scene |
| index | int |
Position of the Scene in the Scene list. |
| name | string |
User specified name of the scene. |
| current | bool |
Whether the scene is currently being displayed. |
| staged | bool |
Whether the scene is staged to be presented. |
{
"current": true,
"index": 0,
"name": "Intro",
"staged": false,
"type": "scene"
}| property | type | description |
|---|---|---|
| type | string |
track |
| parent | string |
<optional> Scene Identifier; if not present, the track is a global track. |
| name | string |
If parent is set, this mirrors the name of the parent Layer. If not set, this has the name of the track. |
| monitoring | bool |
Whether the track is currently cued. |
| muted | bool |
Whether the track is currently muted. |
####### Global Track:
{
"type": "track",
"name": "Track 1",
"monitoring": false,
"muted": false
}####### Layer Track:
{
"type": "track",
"parent": "7C64F74757BCADDEF5C2E2F65B467783",
"name": "Fortnite",
"monitoring": false,
"muted": false
}| property | type | description |
|---|---|---|
| type | string |
layer |
| parent | string |
Reference Identifier for the parent scene. |
| index | int |
Position of the Layer in the Layer list. |
| name | string |
User specified name of the |
| visible | bool |
Whether the layer is currently being rendered. |
| height | int |
Height of the layer in the scene. |
| width | int |
Width of the layer in the scene. |
| x | int |
Horizontal position of the layer in the scene. |
| y | int |
Vertical position of the layer in the scene. |
| source | string |
Path for image source. |
| url | string |
URL for browser source. |
| mediaSource | string |
Path for media source. |
{
"height": 281,
"index": 0,
"name": "Camera Frame",
"parent": "05731BDA0C5DE934359C8F7F24CE6C1B",
"rotation": 0,
"source": "file:///path/to/image.png",
"type": "layer",
"visible": true,
"width": 500,
"x": 1354,
"y": 156
}| property | type | description |
|---|---|---|
| type | string |
effect |
| parent | string |
Layer Identifier |
| name | string |
The effect name. |
| enabled | bool |
Whether the effect is currently applied. |
{
"enabled": true,
"name": "CCToner",
"parent": "DECD0C69E669AB0D495F4A466CEA9BDD",
"type": "effect"
}Signals provide methods for registering listeners.
// Connect a signal callback.
const ref = meld.signalName.connect((parameter: type) => {
// Do something with the information.
});
// Disconnect the signal callback.
meld.signalName.disconnect(ref);Called by Meld Studio to notify callbacks when the gain or mute status of a track changes. These are significantly faster than waiting for the session to update and are ideal for real time volume controls.
Parameters:
| property | type | description |
|---|---|---|
| trackid | string |
Identifier for the audio track. |
| gain | Number |
Current gain level. |
| muted | bool |
Mute status. |
meld.gainUpdated.connect((trackId: string, gain: Number, muted: boolean) => {
// ... handle callback ...
});Triggered any time the contents of session change.
meld.sessionChanged.connect(() => {
const session = meld.session.items;
// ... handle callback ...
});Triggered any time the value of isStreaming changes.
meld.isStreamingChanged.connect(() => {
const isStreaming = meld.isStreaming;
// ... handle callback ...
});Triggered any time the value of isRecording changes.
meld.isRecordingChanged.connect(() => {
const isRecording = meld.isRecording;
// ... handle callback ...
});These functions can be invoked from JavaScript to interact with Meld Studio.
Switches to the specified scene.
Parameters:
| property | type | description |
|---|---|---|
| sceneId | string |
Scene Identifier of the scene to show. |
const sceneId = /* desired scene id */;
meld.showScene(sceneId);Toggles mute status of an audio track.
Side Effects: This will result in a session update (see sessionChanged) and possibly a call to gainUpdated if the trackId has been registered with registerTrackObserver(...).
Parameters:
| property | type | description |
|---|---|---|
| trackId | string |
Track Identifier of the track to mute. |
const trackId = /* desired track id */;
meld.toggleMute(trackId);Toggles monitoring status of an audio track.
Side Effects: This will result in a session update. See signal sessionChanged.
Parameters:
| property | type | description |
|---|---|---|
| trackId | string |
Track Identifier of the track to monitor. |
const trackId = /* desired track id */;
meld.toggleMonitor(trackId);Toggles visibility of a layer within a scene.
Side Effects: This will result in a session update. See signal sessionChanged.
Parameters:
| property | type | description |
|---|---|---|
| sceneId | string |
Scene Identifier that contains the layer |
| layerId | string |
Layer Identifier of the desired layer to manipulate. |
const sceneId = /* desired scene id */;
const layerId = /* desired layer id */;
meld.toggleLayer(sceneId, layerId);Toggles the enabled status of an effect within a layer.
Side Effects: This will result in a session update. See signal sessionChanged.
Parameters:
| property | type | description |
|---|---|---|
| sceneId | string |
Scene Identifier that contains the layer |
| layerId | string |
Layer Identifier that has the effect |
| effectId | string |
Effect Identifier of the desired effect to manipulate |
const sceneId = /* desired scene id */;
const layerId = /* desired layer id */;
const effectId = /* desired effect id */;
meld.toggleEffect(sceneId, layerId, effectId);Toggles the recording status.
Side Effects: Once streaming starts, the channel's isRecording property will update. See signal isRecordingChanged.
meld.toggleRecord();Toggles the streaming status.
Side Effects: Once streaming starts, the channel's isStreaming property will update. See signal isStreamingChanged.
meld.toggleStream();Registers an observer for changes to a specific track.
Parameters:
| property | type | description |
|---|---|---|
| context | string |
The Identifier associated with the registering context. The same value must be used when calling unregisterTrackObserver. |
| trackId | string |
Track Identifier that should be registered for updates. |
const trackId = /* desired track id */;
meld.registerTrackObserver("documentation-context", trackId);Unregisters an observer for a specific track.
Parameters:
| property | type | description |
|---|---|---|
| context | string |
The Identifier associated with the registering context. |
| trackId | string |
Track Identifier that should be unregistered for updates. |
const trackId = /* desired track id */;
meld.unregisterTrackObserver("documentation-context", trackId);Adjusts the gain for an audio track.
Parameters:
| property | type | description |
|---|---|---|
| trackId | string |
Track Identifier to modify. |
| gain | Number |
The gain to set the specified track to. Range: 0.0 to 1.0. |
const trackId = /* desired track id */;
meld.setGain(trackId, 0.5);callFunction(layerId: string, command: string) and callFunctionWithArgs(layerId: string, command: string, args: any[])
Sends a custom command.
Parameters:
| property | type | description |
|---|---|---|
| layerId | string |
Layer ID for layer to call function on. |
| command | string |
Command to trigger in Meld. |
| args | any[] |
Arguments. |
meld.callFunction("7958A12F7C782F392B08A793218AB7E8", "play");
meld.callFunction("7958A12F7C782F392B08A793218AB7E8", "pause");
meld.callFunctionWithArgs("7958A12F7C782F392B08A793218AB7E8", "seekTo", [0]);Supported Functions:
Media players support callFunction:
| event | action |
|---|---|
play |
Play the media. |
pause |
Pause the medis. |
Media players support callFunctionWithArgs:
| event | action |
|---|---|
seekTo |
Seek to the specified time (in seconds). |
Sends a custom command.
Parameters:
| property | type | description |
|---|---|---|
| command | string |
Command to trigger in Meld. |
meld.sendCommand("meld.screenshot");Supported Events:
| event | action |
|---|---|
meld.screenshot |
Takes a screenshot. |
meld.screenshot.vertical |
Takes a vertical screenshot. |
meld.startStreamingAction |
Start streaming. |
meld.stopStreamingAction |
Stop streaming. |
meld.toggleStreamingAction |
Toggles streaming. |
meld.startRecordingAction |
Start recording. |
meld.stopRecordingAction |
Stop recording. |
meld.toggleRecordingAction |
Toggle recording. |
meld.toggleVirtualCameraAction |
Toggle virtual camera. |
meld.recordClip |
Records a clip. |
meld.replay.show |
Show replay layer for last clip. |
meld.replay.dismiss |
Dismiss all active replay layers. |
Dispatches a widget-specific event to Meld Studio (e.g., stopwatch, countdown, confetti, subathon timer, wheel spin).
Parameters:
| property | type | description |
|---|---|---|
| type | string | Event identifier for the widget action. |
| data | any | Optional payload. Only required for events that accept data (see table below). |
Supported Events:
| type | widget | description | data |
|---|---|---|---|
STOPWATCH_RESET |
stopwatch | Reset the stopwatch. | – |
STOPWATCH_PAUSE |
stopwatch | Pause the stopwatch. | – |
STOPWATCH_RESUME |
stopwatch | Resume the stopwatch. | – |
COUNTDOWN_RESET |
countdown | Reset the countdown. | – |
COUNTDOWN_PAUSE |
countdown | Pause the countdown. | – |
COUNTDOWN_RESUME |
countdown | Resume the countdown. | – |
CONFETTIFALL_TRIGGER |
confetti fall | Trigger a confetti fall animation. | – |
CONFETTIPOP_TRIGGER |
confetti pop | Trigger a confetti pop animation. | – |
SUBATHONTIMER_RESET |
subathon timer | Reset the subathon timer. | – |
SUBATHONTIMER_PAUSE |
subathon timer | Pause the subathon timer. | – |
SUBATHONTIMER_RESUME |
subathon timer | Resume the subathon timer. | – |
SUBATHONTIMER_ADDTIME |
subathon timer | Add time (seconds) to the subathon timer. | { \"amount\": number } |
WHEELSPIN_SPIN |
wheel spin | Spin the wheel. | – |
COUNTER_INCREMENT |
counter | Increments the counter. | – |
COUNTER_DECREMENT |
counter | Decrements the counter. | – |
Examples:
// Trigger a confetti pop
meld.sendStreamEvent("CONFETTIPOP_TRIGGER");
// Add 120 seconds to the subathon timer
meld.sendStreamEvent("SUBATHONTIMER_ADDTIME", { amount: 120 });deprecated: Use sendCommand instead.
Sends a custom event.
Parameters:
| property | type | description |
|---|---|---|
| event | string |
Event to trigger in Meld. |
meld.sendEvent("co.meldstudio.events.screenshot");Supported Events:
| event | action |
|---|---|
co.meldstudio.events.screenshot |
Takes a screenshot. |
Prepares a scene for switching without making it live.
Parameters:
| property | type | description |
|---|---|---|
| sceneId | string |
Scene Identifier of the scene to mark as staged. |
const sceneId = /* desired scene id */;
meld.setStagedScene(sceneId);Switches to the prepared staged scene.
const sceneId = /* desired scene id */;
meld.setStagedScene(sceneId);
// note: user may change the Staged scene via other means.
meld.showStagedScene();Introduced in Version 2. Sets the value of the given property.
note: setting "parent", "type", and "index" will have no effect.
/*
* "8DB9CC06AFCA152EA30278380B569751": {
* "height": 281,
* "index": 0,
* "name": "Camera Frame",
* "parent": "05731BDA0C5DE934359C8F7F24CE6C1B",
* "rotation": 0,
* "source": "file:///path/to/image.png",
* "type": "layer",
* "visible": false,
* "width": 500,
* "x": 1354,
* "y": 156
* },
*/
const layerId = "8DB9CC06AFCA152EA30278380B569751";
meld.setProperty(layerId, "name", "Camera Frame (Active)");
meld.setProperty(layerId, "visible", true);