# Understanding the VDO.Ninja IFRAME API: Detecting User Joins and Disconnects The VDO.Ninja IFRAME API allows websites to embed and interact with VDO.Ninja streams. One of the most useful features is the ability to detect when users join or disconnect from your stream through event messaging. This guide will explain how to implement this functionality in your own projects. ## How the IFRAME API Works VDO.Ninja's IFRAME API uses the browser's `postMessage` API to communicate between your parent website and the embedded VDO.Ninja iframe. This allows you to: 1. Send commands to control the VDO.Ninja instance 2. Receive events and data from the VDO.Ninja instance ## Setting Up the Basic Structure First, you need to create an iframe that loads VDO.Ninja: ```javascript // Create the iframe element var iframe = document.createElement("iframe"); // Set necessary permissions iframe.allow = "camera;microphone;fullscreen;display-capture;autoplay;"; // Set the source URL (your VDO.Ninja room) iframe.src = "https://vdo.ninja/?room=your-room-name&cleanoutput"; // Add the iframe to your page document.getElementById("container").appendChild(iframe); ``` ## Setting Up the Event Listener To detect joins and disconnects, you need to set up an event listener for messages from the iframe: ```javascript // Set up event listener (cross-browser compatible) var eventMethod = window.addEventListener ? "addEventListener" : "attachEvent"; var eventer = window[eventMethod]; var messageEvent = eventMethod === "attachEvent" ? "onmessage" : "message"; // Add the event listener eventer(messageEvent, function (e) { // Make sure the message is from our VDO.Ninja iframe if (e.source != iframe.contentWindow) return; // Log the data for debugging console.log(e.data); // Process specific events if ("action" in e.data) { // Handle different actions handleAction(e.data); } }, false); ``` ## Detecting User Joins and Disconnects The key events to watch for are: ### Guest Connections ```javascript function handleAction(data) { if (data.action === "guest-connected") { // A new guest has connected console.log("Guest connected:", data.streamID); // You can access additional info if available if (data.value && data.value.label) { console.log("Guest label:", data.value.label); } } else if (data.action === "view-connection") { // Someone viewing the stream has connected console.log("Viewer connected:", data.streamID); // The value property will be true for connections if (data.value) { console.log("New viewer connected"); } else { console.log("Viewer disconnected"); } } else if (data.action === "director-connected") { // The director has connected console.log("Director connected"); } else if (data.action === "scene-connected") { // A scene has connected console.log("Scene connected:", data.value); // Scene ID } else if (data.action === "slot-updated") { // A stream has been assigned to a slot console.log("Stream", data.streamID, "assigned to slot", data.value); } } ``` ### Disconnections ```javascript function handleAction(data) { // Handling disconnections if (data.action === "view-connection" && data.value === false) { // A viewer has disconnected console.log("Viewer disconnected:", data.streamID); } else if (data.action === "director-share" && data.value === false) { // A director has stopped sharing console.log("Director stopped sharing:", data.streamID); } else if (data.action === "push-connection" && data.value === false) { // A guest has disconnected console.log("Guest disconnected:", data.streamID); } } ``` ## Complete Working Example Here's a complete example that demonstrates detecting joins and disconnects: ```javascript // Create the container for the iframe var container = document.createElement("div"); container.id = "vdo-container"; document.body.appendChild(container); // Create the iframe element var iframe = document.createElement("iframe"); iframe.allow = "camera;microphone;fullscreen;display-capture;autoplay;"; iframe.src = "https://vdo.ninja/?room=your-room-name&cleanoutput"; iframe.style.width = "100%"; iframe.style.height = "100%"; container.appendChild(iframe); // Create a status display element var statusDiv = document.createElement("div"); statusDiv.id = "connection-status"; document.body.appendChild(statusDiv); // Set up event listener var eventMethod = window.addEventListener ? "addEventListener" : "attachEvent"; var eventer = window[eventMethod]; var messageEvent = eventMethod === "attachEvent" ? "onmessage" : "message"; // Keep track of connected users var connectedUsers = {}; // Add the event listener eventer(messageEvent, function (e) { // Make sure the message is from our VDO.Ninja iframe if (e.source != iframe.contentWindow) return; // Log all messages for debugging console.log(e.data); // Process specific actions if ("action" in e.data) { handleAction(e.data); } }, false); function handleAction(data) { // Handle connections if (data.action === "guest-connected" && data.streamID) { connectedUsers[data.streamID] = data.value?.label || "Guest"; updateStatusDisplay("Guest connected: " + (data.value?.label || data.streamID)); } else if (data.action === "view-connection") { if (data.value && data.streamID) { connectedUsers[data.streamID] = "Viewer"; updateStatusDisplay("Viewer connected: " + data.streamID); } else if (data.streamID) { delete connectedUsers[data.streamID]; updateStatusDisplay("Viewer disconnected: " + data.streamID); } } else if (data.action === "director-connected") { updateStatusDisplay("Director connected"); } else if (data.action === "push-connection" && data.value === false && data.streamID) { delete connectedUsers[data.streamID]; updateStatusDisplay("User disconnected: " + data.streamID); } } function updateStatusDisplay(message) { var timestamp = new Date().toLocaleTimeString(); statusDiv.innerHTML += `

${timestamp}: ${message}

`; // Update connected users count var count = Object.keys(connectedUsers).length; document.getElementById("user-count").textContent = count; } // Add a user count display var countDiv = document.createElement("div"); countDiv.innerHTML = "Connected users: 0"; document.body.insertBefore(countDiv, statusDiv); ``` ## Waiting Room Example You can implement a waiting room like the one in the `waitingroom.html` file from your code samples: ```javascript // Setup event listener var eventMethod = window.addEventListener ? "addEventListener" : "attachEvent"; var eventer = window[eventMethod]; var messageEvent = eventMethod === "attachEvent" ? "onmessage" : "message"; var waiting = null; eventer(messageEvent, function (e) { if (e.source != iframe.contentWindow) return; if ("action" in e.data) { if (e.data.action == "joining-room") { // Show initial joining message outputWindow.innerHTML = "JOINING ROOM"; // After 1 second, show waiting message if director hasn't joined waiting = setTimeout(function() { outputWindow.innerHTML = "Waiting for the director to join"; outputWindow.classList.remove("hidden"); }, 1000); } else if (e.data.action == "director-connected") { // Director has joined, clear waiting message clearTimeout(waiting); outputWindow.innerHTML = ""; outputWindow.classList.add("hidden"); } } }); ``` ## Getting Additional Information About Connections For more detailed information about connections, you can use the `getStreamIDs` or `getDetailedState` commands: ```javascript // Request info about all connected streams iframe.contentWindow.postMessage({ "getStreamIDs": true }, "*"); // Request detailed state information iframe.contentWindow.postMessage({ "getDetailedState": true }, "*"); ``` ## Best Practices 1. **Always check the source**: Make sure messages are coming from your VDO.Ninja iframe. 2. **Handle disconnections gracefully**: Sometimes connections drop unexpectedly. 3. **Consider implementing reconnection logic**: If important users disconnect, you might want to notify them or attempt to reconnect. 4. **Debug with console.log**: Log all events during development to understand the full message flow. 5. **Test with multiple users**: The behavior can be different depending on who connects first. By implementing these techniques, you can build sophisticated applications that respond to users joining and leaving your VDO.Ninja sessions, creating more interactive and responsive experiences. # VDO.Ninja IFRAME API - Complete Inbound Control Reference This document provides a comprehensive list of all inbound remote control calls available through the VDO.Ninja IFRAME API. These commands allow you to control a VDO.Ninja instance embedded in an iframe from your parent webpage. ## Table of Contents - [Basic Usage](#basic-usage) - [Audio Controls](#audio-controls) - [Video Controls](#video-controls) - [Stream Management](#stream-management) - [Recording Controls](#recording-controls) - [Group Management](#group-management) - [Bitrate & Quality Controls](#bitrate--quality-controls) - [Device Management](#device-management) - [Layout & Display Controls](#layout--display-controls) - [Data & Messaging](#data--messaging) - [Statistics & Monitoring](#statistics--monitoring) - [Utility Functions](#utility-functions) - [Advanced Controls](#advanced-controls) ## Basic Usage To send commands to the VDO.Ninja iframe: ```javascript iframe.contentWindow.postMessage({ command: value, // optional parameters }, "*"); ``` ## Audio Controls ### `mic` - Microphone Control Controls the local microphone mute state. ```javascript // Unmute microphone iframe.contentWindow.postMessage({ mic: true }, "*"); // Mute microphone iframe.contentWindow.postMessage({ mic: false }, "*"); // Toggle microphone iframe.contentWindow.postMessage({ mic: "toggle" }, "*"); ``` ### `mute` / `speaker` - Speaker Control Controls the speaker mute state (incoming audio). ```javascript // Mute speakers iframe.contentWindow.postMessage({ mute: true }, "*"); // OR iframe.contentWindow.postMessage({ speaker: false }, "*"); // Unmute speakers iframe.contentWindow.postMessage({ mute: false }, "*"); // OR iframe.contentWindow.postMessage({ speaker: true }, "*"); // Toggle speaker iframe.contentWindow.postMessage({ mute: "toggle" }, "*"); ``` ### `volume` - Volume Control Sets the volume level for incoming audio (0.0 to 1.0). ```javascript // Set volume to 50% iframe.contentWindow.postMessage({ volume: 0.5 }, "*"); // Set volume for specific stream iframe.contentWindow.postMessage({ volume: 0.8, target: "streamID123" // or use "*" for all streams }, "*"); ``` ### `panning` - Audio Panning Adjusts stereo panning for incoming audio. ```javascript // Pan left (-90 to 90, where -90 is full left, 90 is full right) iframe.contentWindow.postMessage({ panning: -45, UUID: "connection-uuid" // optional, applies to all if omitted }, "*"); ``` ### `targetAudioBitrate` - Audio Bitrate Target Sets the target audio bitrate (in kbps). ```javascript iframe.contentWindow.postMessage({ targetAudioBitrate: 128, target: "streamID123" // optional }, "*"); ``` ### `audiobitrate` - Audio Bitrate Control Changes the audio bitrate with optional lock. ```javascript iframe.contentWindow.postMessage({ audiobitrate: 64, lock: true, // optional, defaults to true target: "streamID123" // optional }, "*"); ``` ### `PPT` - Push-to-Talk Controls push-to-talk functionality. ```javascript // Activate PPT (unmute) iframe.contentWindow.postMessage({ PPT: true }, "*"); // Deactivate PPT (mute) iframe.contentWindow.postMessage({ PPT: false }, "*"); // Toggle PPT iframe.contentWindow.postMessage({ PPT: "toggle" }, "*"); ``` ## Video Controls ### `camera` - Camera Control Controls the local camera on/off state. ```javascript // Turn on camera iframe.contentWindow.postMessage({ camera: true }, "*"); // Turn off camera iframe.contentWindow.postMessage({ camera: false }, "*"); // Toggle camera iframe.contentWindow.postMessage({ camera: "toggle" }, "*"); ``` ### `pauseinvisible` - Pause Invisible Videos Controls whether videos hidden in the mixer are paused. ```javascript // Enable pause invisible iframe.contentWindow.postMessage({ pauseinvisible: true }, "*"); // Disable pause invisible iframe.contentWindow.postMessage({ pauseinvisible: false }, "*"); // Toggle iframe.contentWindow.postMessage({ pauseinvisible: "toggle" }, "*"); ``` ### `keyframe` - Request Keyframe Forces a keyframe to be sent to all scene connections. ```javascript iframe.contentWindow.postMessage({ keyframe: true }, "*"); ``` ## Stream Management ### `requestStream` - Request Specific Stream Loads a specific stream by ID. ```javascript iframe.contentWindow.postMessage({ requestStream: "streamID123" }, "*"); ``` ### `close` / `hangup` - Disconnect Streams Disconnects and hangs up connections. ```javascript // Normal hangup iframe.contentWindow.postMessage({ close: true }, "*"); // Emergency stop (immediate) iframe.contentWindow.postMessage({ close: "estop" }, "*"); // Hangup and reload iframe.contentWindow.postMessage({ close: "reload" }, "*"); ``` ## Recording Controls ### `record` - Local Recording Control Controls local video recording. ```javascript // Start recording iframe.contentWindow.postMessage({ record: true }, "*"); // Stop recording iframe.contentWindow.postMessage({ record: false }, "*"); // Record specific video element iframe.contentWindow.postMessage({ record: "videoElementId" }, "*"); ``` ## Group Management ### `groups` - Set Groups Sets the groups for the local stream. ```javascript // Set groups as array iframe.contentWindow.postMessage({ groups: ["group1", "group2"] }, "*"); // Set groups as comma-separated string iframe.contentWindow.postMessage({ groups: "group1,group2" }, "*"); // Clear groups iframe.contentWindow.postMessage({ groups: [] }, "*"); ``` ### `groupView` - Set View Groups Sets which groups are visible. ```javascript // View specific groups iframe.contentWindow.postMessage({ groupView: ["group1", "group3"] }, "*"); // View all groups iframe.contentWindow.postMessage({ groupView: [] }, "*"); ``` ## Bitrate & Quality Controls ### `bitrate` - Video Bitrate Control Sets video bitrate for streams (in kbps). ```javascript // Set bitrate for all streams iframe.contentWindow.postMessage({ bitrate: 2500, lock: true // optional, defaults to true }, "*"); // Set bitrate for specific stream iframe.contentWindow.postMessage({ bitrate: 1000, target: "streamID123" // or UUID: "uuid-here" }, "*"); ``` ### `targetBitrate` - Target Video Bitrate Sets the fundamental bitrate target. ```javascript iframe.contentWindow.postMessage({ targetBitrate: 3000, target: "streamID123" // optional }, "*"); ``` ### `manualBitrate` - Manual Bandwidth Control Sets manual bandwidth limits. ```javascript iframe.contentWindow.postMessage({ manualBitrate: 5000, target: "streamID123" // optional }, "*"); ``` ### `scale` - Resolution Scaling Controls resolution scaling. ```javascript // Set specific scale percentage iframe.contentWindow.postMessage({ scale: 50 }, "*"); // Disable manual scaling (enable dynamic) iframe.contentWindow.postMessage({ scale: false }, "*"); // Apply to specific stream iframe.contentWindow.postMessage({ scale: 75, UUID: "connection-uuid" }, "*"); ``` ### `targetWidth` / `targetHeight` - Resolution Request Request specific resolution from remote connection. ```javascript iframe.contentWindow.postMessage({ targetWidth: 1280, targetHeight: 720, UUID: "connection-uuid" // required }, "*"); ``` ## Device Management ### `changeVideoDevice` - Change Camera Changes the active video input device. ```javascript iframe.contentWindow.postMessage({ changeVideoDevice: "deviceId-here" }, "*"); ``` ### `changeAudioDevice` - Change Microphone Changes the active audio input device. ```javascript iframe.contentWindow.postMessage({ changeAudioDevice: "deviceId-here" }, "*"); ``` ### `changeAudioOutputDevice` - Change Speaker Changes the audio output device. ```javascript iframe.contentWindow.postMessage({ changeAudioOutputDevice: "deviceId-here" }, "*"); ``` ### `getDeviceList` - List Available Devices Requests a list of available media devices. ```javascript iframe.contentWindow.postMessage({ getDeviceList: true, cib: "callback-id" // optional callback ID }, "*"); // Response will be sent back via postMessage: // { deviceList: [...], cib: "callback-id" } ``` ## Layout & Display Controls ### `layout` - Set Layout Sets the display layout. ```javascript // Set single layout iframe.contentWindow.postMessage({ layout: "grid" }, "*"); // Set multiple layouts (array) iframe.contentWindow.postMessage({ layout: ["grid", "presenter"] }, "*"); // With scene control (director only) iframe.contentWindow.postMessage({ layout: "grid", scene: 1, UUID: "target-uuid" // optional }, "*"); ``` ### `previewMode` - Switch Preview Mode Switches between preview modes. ```javascript iframe.contentWindow.postMessage({ previewMode: 1 // mode number }, "*"); ``` ### `slotmode` - Slot Mode Control Controls slot mode behavior. ```javascript iframe.contentWindow.postMessage({ slotmode: 1 // slot mode number, or false to disable }, "*"); ``` ### `advancedMode` - Toggle Advanced UI Shows/hides advanced UI elements. ```javascript // Show advanced elements iframe.contentWindow.postMessage({ advancedMode: true }, "*"); // Hide advanced elements iframe.contentWindow.postMessage({ advancedMode: false }, "*"); ``` ### `toggleSettings` - Toggle Settings Panel Controls the settings panel visibility. ```javascript // Toggle settings iframe.contentWindow.postMessage({ toggleSettings: "toggle" }, "*"); // Show settings iframe.contentWindow.postMessage({ toggleSettings: true }, "*"); ``` ### `target` - DOM Manipulation Manipulates video elements in the DOM. ```javascript // Add video to grid iframe.contentWindow.postMessage({ target: "streamID123", add: true }, "*"); // Remove video from grid iframe.contentWindow.postMessage({ target: "streamID123", remove: true }, "*"); // Replace all videos with target iframe.contentWindow.postMessage({ target: "streamID123", replace: true }, "*"); // Apply settings to video element iframe.contentWindow.postMessage({ target: "streamID123", settings: { style: "transform: scale(1.5);", muted: true } }, "*"); ``` ## Data & Messaging ### `sendData` - Send Generic Data Sends data through peer connections. ```javascript iframe.contentWindow.postMessage({ sendData: { custom: "data" }, UUID: "target-uuid", // optional streamID: "streamID123", // optional type: "custom-type" // optional }, "*"); ``` ### `sendChat` - Send Chat Message Sends a chat message to all peers. ```javascript iframe.contentWindow.postMessage({ sendChat: "Hello everyone!" }, "*"); ``` ### `sendMessage` - WebRTC Message to Viewers Sends a message to viewer connections. ```javascript iframe.contentWindow.postMessage({ sendMessage: { custom: "viewer-data" } }, "*"); ``` ### `sendRequest` - WebRTC Request to Publishers Sends a request to publisher connections. ```javascript iframe.contentWindow.postMessage({ sendRequest: { action: "some-action" } }, "*"); ``` ### `sendPeers` - Message All Peers Sends a message to all connected peers. ```javascript iframe.contentWindow.postMessage({ sendPeers: { broadcast: "data" } }, "*"); ``` ### `sendRawMIDI` - Send MIDI Data Sends raw MIDI messages. ```javascript iframe.contentWindow.postMessage({ sendRawMIDI: { data: [144, 60, 127], // MIDI data array channel: 1, timestamp: Date.now() }, UUID: "target-uuid" // optional }, "*"); ``` ## Statistics & Monitoring ### `getStats` - Get Quick Stats Requests current statistics. ```javascript // Get all stats iframe.contentWindow.postMessage({ getStats: true, cib: "callback-id" }, "*"); // Get stats for specific stream iframe.contentWindow.postMessage({ getStats: true, streamID: "streamID123", cib: "callback-id" }, "*"); ``` ### `getFreshStats` - Get Detailed Stats Requests detailed statistics (takes ~1 second). ```javascript iframe.contentWindow.postMessage({ getFreshStats: true, cib: "callback-id" }, "*"); ``` ### `getRemoteStats` - Request Remote Stats Requests statistics from remote peers. ```javascript iframe.contentWindow.postMessage({ getRemoteStats: true }, "*"); ``` ### `requestStatsContinuous` - Continuous Stats Enables/disables continuous statistics updates. ```javascript // Enable continuous stats iframe.contentWindow.postMessage({ requestStatsContinuous: true }, "*"); // Disable continuous stats iframe.contentWindow.postMessage({ requestStatsContinuous: false }, "*"); ``` ### `getLoudness` - Audio Loudness Monitoring Enables/disables loudness monitoring. ```javascript // Enable loudness monitoring iframe.contentWindow.postMessage({ getLoudness: true, cib: "callback-id" }, "*"); // Disable loudness monitoring iframe.contentWindow.postMessage({ getLoudness: false }, "*"); ``` ### `getStreamIDs` - List Stream IDs Gets a list of all connected stream IDs. ```javascript iframe.contentWindow.postMessage({ getStreamIDs: true, cib: "callback-id" }, "*"); ``` ### `getStreamInfo` - Detailed Stream Information Gets detailed information about all streams. ```javascript iframe.contentWindow.postMessage({ getStreamInfo: true, cib: "callback-id" }, "*"); ``` ### `getDetailedState` - Complete State Information Gets comprehensive state information. ```javascript iframe.contentWindow.postMessage({ getDetailedState: true, cib: "callback-id" }, "*"); ``` ### `getGuestList` - Get Guest List Gets a list of all connected guests. ```javascript iframe.contentWindow.postMessage({ getGuestList: true, cib: "callback-id" }, "*"); ``` ## Utility Functions ### `reload` - Reload Page Forces a page reload. ```javascript iframe.contentWindow.postMessage({ reload: true }, "*"); ``` ### `style` - Inject Custom CSS Injects custom CSS into the iframe. ```javascript iframe.contentWindow.postMessage({ style: ` .videoContainer { border: 2px solid red; } #mutebutton { background: blue; } ` }, "*"); ``` ### `function` - Execute Built-in Functions Executes predefined functions. ```javascript // Preview webcam iframe.contentWindow.postMessage({ function: "previewWebcam" }, "*"); // Publish screen iframe.contentWindow.postMessage({ function: "publishScreen" }, "*"); // Change HTML content iframe.contentWindow.postMessage({ function: "changeHTML", target: "elementId", value: "

New content

" }, "*"); // Route WebSocket message iframe.contentWindow.postMessage({ function: "routeMessage", value: { /* message data */ } }, "*"); // Execute arbitrary code (use with caution) iframe.contentWindow.postMessage({ function: "eval", value: "console.log('Hello from eval');" }, "*"); ``` ### `saveVideoFrameToDisk` - Save Screenshot Saves a video frame to disk. ```javascript // Save local video iframe.contentWindow.postMessage({ saveVideoFrameToDisk: true, filename: "screenshot.png" // optional }, "*"); // Save specific stream iframe.contentWindow.postMessage({ saveVideoFrameToDisk: true, streamID: "streamID123", filename: "stream-capture.jpg" }, "*"); // Save all streams iframe.contentWindow.postMessage({ saveVideoFrameToDisk: true, UUID: "*" }, "*"); ``` ### `getVideoFrame` - Get Video Frame Data Gets video frame data as base64. ```javascript iframe.contentWindow.postMessage({ getVideoFrame: true, streamID: "streamID123", // or UUID cib: "callback-id" }, "*"); ``` ### `copyVideoFrameToClipboard` - Copy Screenshot Copies a video frame to clipboard. ```javascript iframe.contentWindow.postMessage({ copyVideoFrameToClipboard: true, streamID: "streamID123" // or UUID }, "*"); ``` ### `getSnapshotBySlot` / `getSnapshotByStreamID` - Get Slot/Stream Snapshot Gets a snapshot from a specific slot or stream using MediaStreamTrackProcessor. ```javascript // By slot number iframe.contentWindow.postMessage({ getSnapshotBySlot: 0, // slot index cib: "callback-id" }, "*"); // By stream ID iframe.contentWindow.postMessage({ getSnapshotByStreamID: "streamID123", cib: "callback-id" }, "*"); // Response includes base64 image data: // { // type: 'frame', // frame: 'data:image/png;base64,...', // UUID: 'connection-uuid', // streamID: 'streamID123', // slot: 0, // format: 'png', // cib: 'callback-id' // } ``` ## Advanced Controls ### `sceneState` - OBS Scene State Sets the scene state for OBS integration. ```javascript // Scene is live iframe.contentWindow.postMessage({ sceneState: true }, "*"); // Scene is not live iframe.contentWindow.postMessage({ sceneState: false }, "*"); ``` ### `layouts` - OBS Layout Sync Syncs layouts with OBS. ```javascript iframe.contentWindow.postMessage({ layouts: ["layout1", "layout2"], obsSceneTriggers: true // optional }, "*"); ``` ### `obsCommand` - OBS Commands Sends commands to OBS. ```javascript iframe.contentWindow.postMessage({ obsCommand: "some-command", remote: "remote-id", // optional UUID: "target-uuid", // optional streamID: "streamID123" // optional }, "*"); ``` ### `setBufferDelay` - Audio/Video Buffer Delay Sets the buffer delay in milliseconds. ```javascript // Set default buffer delay iframe.contentWindow.postMessage({ setBufferDelay: 200 }, "*"); // Set for specific stream iframe.contentWindow.postMessage({ setBufferDelay: 300, streamID: "streamID123" // or UUID or label }, "*"); // Set for all streams iframe.contentWindow.postMessage({ setBufferDelay: 250, UUID: "*" }, "*"); ``` ### `automixer` - Automixer Control Controls the automatic mixer behavior. ```javascript // Enable automixer iframe.contentWindow.postMessage({ automixer: true }, "*"); // Disable automixer (manual control) iframe.contentWindow.postMessage({ automixer: false }, "*"); ``` ### `enableYouTube` - YouTube Chat Integration Enables YouTube chat integration. ```javascript // Enable with API key iframe.contentWindow.postMessage({ enableYouTube: "your-youtube-api-key" }, "*"); // Enable with existing key iframe.contentWindow.postMessage({ enableYouTube: true }, "*"); ``` ### `nextSlide` / `prevSlide` - Slide Navigation Controls slide navigation. ```javascript // Next slide iframe.contentWindow.postMessage({ nextSlide: true }, "*"); // Previous slide iframe.contentWindow.postMessage({ prevSlide: true }, "*"); ``` ### `getFaces` / `faceTrack` - Face Detection Controls face detection/tracking. ```javascript // Enable face tracking iframe.contentWindow.postMessage({ getFaces: true, faceTrack: true }, "*"); // Disable face tracking iframe.contentWindow.postMessage({ getFaces: true, faceTrack: false }, "*"); ``` ### `getEffectsData` - Effects Data Gets data from visual effects (face tracking, etc.). ```javascript // Get specific effect data iframe.contentWindow.postMessage({ getEffectsData: "effect-name" }, "*"); // Disable effects data iframe.contentWindow.postMessage({ getEffectsData: false }, "*"); ``` ### `action` - Companion API Actions Executes Companion API actions. ```javascript iframe.contentWindow.postMessage({ action: "action-name", value: "action-value", target: "optional-target" }, "*"); ``` ## Response Handling Many commands support a callback ID (`cib`) for tracking responses: ```javascript // Send request with callback ID iframe.contentWindow.postMessage({ getStats: true, cib: "unique-callback-123" }, "*"); // Listen for response window.addEventListener("message", function(e) { if (e.data.cib === "unique-callback-123") { console.log("Stats received:", e.data.stats); } }); ``` ## Notes - All commands are sent via `postMessage` to the iframe's `contentWindow` - The second parameter `"*"` can be replaced with a specific origin for security - Some commands require director privileges to function - Commands that affect remote streams often accept `UUID`, `streamID`, or `target` parameters - The `lock` parameter on bitrate controls prevents automatic adjustments - Many "get" commands return data via postMessage back to the parent window