Ninja Docs
  • What is VDO.Ninja?
    • How does it work
    • Use cases
    • Why use VDO.Ninja over other solutions?
    • Sponsor ❤
  • Getting started
    • VDO.Ninja basics
    • What are stream IDs?
    • The power of the URL parameter
    • Multi-Person Chat
    • Rooms
    • Even higher quality video
    • Mobile phone camera into webcam
    • Cheat sheet of basic parameters
  • Steve's helper apps & tools
    • Electron Capture
      • Documentation
    • Social Stream Ninja
      • Documentation reference
    • Meshcast.io
    • Caption.Ninja
    • Raspberry.Ninja
      • Documentation
    • Mixer App
    • WHIP and WHEP tooling
    • Versus.cam
    • Speed and Quality Tests
    • Comms
    • Teleprompter Tool
    • LUT maker for color grading
    • Native mobile app versions
    • VDO Applications
    • Tech Demonstrations
    • Invite Link Generators
    • Community contributed tools
    • Mic test
    • Whiteboard
  • Guides
    • Cheat Sheets
    • Common questions re: Rooms
    • Video bitrate for push/view links
    • Video bitrate in rooms
    • How to get permanent links
    • Basic hotkeys
    • MIDI, API and WebHID support
    • Hardware-accelerated video encoding
    • Audio Filters & Bitrate
    • Options to record streams
    • External guides and how-tos
    • How to lock the resolution
    • How to use VDO.Ninja as a webcam for Google Hangouts, Zoom, and more
    • How to capture without browser sources
    • How to control bitrate/quality
    • How to selectively allow access
    • Stream Scheduling and Promotion
    • How to send the audio/video output of one OBS to another OBS using VDO.Ninja
    • How to mirror a video while Full-Screen - For iPads and Teleprompters
    • How to capture an application's audio
    • How to control VDO.Ninja with Touch Portal
    • How to publish from OBS into VDO.Ninja
    • How to screen share your iPhone/iPad
    • How to get iPhones to output 1080p Videos
    • How to stream into Zoom without OBS
    • How to connect a smartphone to computer via USB
    • How to edit an invite after sending it
    • How to get highest video quality (for an interview)
    • How to stream 4K video using VDO.Ninja
    • How to get lowest audio latency possible
    • How to share webcam from inside OBS
    • How to publish to Facebook Live
    • How to embed VDO.Ninja into a site with iFrames
      • Detecting User Joins / Disconnects
      • Create custom drawing app
      • Generic P2P Data Transmission Guide
    • How to use the green screen just locally
    • How to connect a GoPro to VDO.Ninja
    • How to install RaspNinja on Jetson
    • How to transfer guests to other rooms
    • How to set up a simple chat room
    • How to screen share in 1080p
    • How to control PowerPoint remotely with VDO.Ninja
    • How to improve quality of the native app
    • How to stream transparent video
    • Recommended OBS WHIP settings
    • How to use VDO.Ninja on a website
    • Keep Mic Active in Background on Android Browser
    • PlayStation or Xbox to VDO.Ninja
    • Enabling WebRTC Sources in OBS
    • Picking the right microphone
    • Set Up Proper Lighting
    • System requirements for streaming
    • From OBS to VDO.Ninja using WHIP
    • Deploy your own Meshcast-like service
    • Windows TTS Audio Capture Methods for OBS
    • Syncing USB audio with VDO.Ninja -> OBS Virtual Camera
  • Advanced Options (URL Parameters)
    • Most common Parameters
      • &push
      • &quality
      • &videodevice
      • &audiodevice
      • &effects
      • &label
      • &meshcast
      • &view
      • &videobitrate
      • &audiobitrate
      • &codec
      • &novideo
      • &noaudio
      • &showlabels
      • &room
      • &director
      • &proaudio
      • &scene
      • &roombitrate
      • &password
      • &broadcast
    • Setup Parameters
      • &push
      • &room
      • &password
      • &hash
      • &e2ee
      • &label
      • &labelsuggestion
      • &permaid
      • &group
      • &groupview
      • &groupaudio
      • &datamode
      • &audiooutput
      • &sink
      • &audiodevice
      • &videodevice
      • &vdo
      • &device
      • &miconly
      • &miconlyoption
      • &safemode
      • &autostart
      • &easyexit
      • &webcam
      • &webcam2
      • &screenshare
      • &screenshare2
      • &website
      • &fileshare
      • &intro
      • &host
      • &tips
      • &welcome
      • &welcomeb64
      • &welcomeimage
      • &hangupmessage
      • &humb64
      • &groupmode
      • &audience
    • Camera Parameters
      • &whitebalance
      • &exposure
      • &saturation
      • &sharpness
      • &contrast
      • &brightness
    • Video Parameters
      • &blind
      • &quality
      • &width
      • &height
      • &aspectratio
      • &contenthint
      • &mediasettings
      • &noscale
      • &fps
      • &maxframerate
      • &effects
      • &effectvalue
      • &imagelist
      • &avatar
      • &fullscreen
      • &showpreview
      • &minipreview
      • &minipreviewoffset
      • &largepreview
      • &nopreview
      • &hideguest
      • &videomute
      • &ptz
      • &webp
      • &webpquality
      • &scale
      • &viewwidth
      • &viewheight
      • &dpi
      • &sharper
      • &codec
      • &h264profile
      • &buffer
      • &buffer2
      • &fadein
      • &broadcast
      • &directoronly
      • &showonly
      • &novideo
      • &nodirectorvideo
      • &slideshow
      • &zoom
    • Video Bitrate Parameters
      • &outboundvideobitrate
      • &maxvideobitrate
      • &limittotalbitrate
      • &controlroombitrate
      • &roombitrate
      • &maxbandwidth
      • &videobitrate
      • &totalscenebitrate
      • &totalroombitrate
      • &totalbitrate
      • &zoomedbitrate
      • &optimize
    • Audio Parameters
      • &proaudio
      • &stereo
      • &mutespeaker
      • &deafen
      • &noaudioprocessing
      • &audiodevice
      • &echocancellation
      • &audiogain
      • &autogain
      • &compressor
      • &denoise
      • &distort
      • &equalizer
      • &limiter
      • &lowcut
      • &noisegate
      • &noisegatesettings
      • &audiocontenthint
      • &audiolatency
      • &micdelay
      • &mute
      • &automute
      • &outboundaudiobitrate
      • &inputchannels
      • &monomic
      • &audiooutput
      • &sink
      • &volume
      • &volumecontrol
      • &audiobitrate
      • &vbr
      • &mono
      • &noaudio
      • &nodirectoraudio
      • &panning
      • &sync
      • &samplerate
      • &channels
      • &channeloffset
      • &playchannel
      • &ptime
      • &maxptime
      • &minptime
      • &audiocodec
      • &dtx
      • &nofec
    • Mixer/Scene Parameters
      • &solo
      • &view
      • &include
      • &exclude
      • &layout
      • &activespeaker
      • &activespeakerdelay
      • &order
      • &slots
      • &fakeguests
      • &randomize
      • &cover
      • &43
      • &portrait
      • &square
      • &forceviewerlandscape
      • &animated
      • &manual
      • &locked
      • &poster
      • &hideplaybutton
      • &motiondetection
      • &scene
      • &scenetype
      • &autoadd
      • &hiddenscenebitrate
      • &preloadbitrate
      • &waitimage
      • &waitmessage
      • &waittimeout
      • &viewslot
    • Settings Parameters
      • &language
      • &remote
      • &controlobs
      • &allowedscenes
      • &stats
      • &sticky
      • &clearstorage
      • &disablehotkeys
      • &showlist
      • &nopush
      • &hidehome
      • &hidetranslate
      • &clock
      • &clock24
      • &timer
      • &powerpoint
      • &widget
      • &token
      • &transcribe
      • &signalmeter
      • &batterymeter
      • &consent
      • &prompt
      • &hands
      • &notify
      • &r2d2
      • &directorchat
      • &maxconnections
      • &maxviewers
      • &chunked
      • &retransmit
      • &rampuptime
      • &sensor
      • &sensorfilter
      • &postimage
      • &postinterval
      • &slot
      • &closedcaptions
      • &nocaptionlabels
      • &enhance
      • &bitratecutoff
      • &cutscene
      • &statsinterval
      • &keyframerate
      • &maxpublishers
      • &showconnections
      • &obsfix
      • &streamlabs
      • &getfaces
      • &nochunked
    • Buttons and Control Bar Parameters
      • &autohide
      • &controlbarspace
      • &nosettings
      • &nomicbutton
      • &nospeakerbutton
      • &novideobutton
      • &nofileshare
      • &screensharebutton
      • &nohangupbutton
      • &chatbutton
      • &bigbutton
      • &fullscreenbutton
      • &nowebsite
      • &hands
      • &videocontrols
      • &nocontrols
      • &forcecontrols
    • Design Parameters
      • &label
      • &showlabels
      • &fontsize
      • &style
      • &bgimage
      • &showall
      • &meterstyle
      • &cleanoutput
      • &cleanish
      • &css
      • &base64css
      • &js
      • &base64js
      • &mirror
      • &nomirror
      • &flip
      • &rotatewindow
      • &structure
      • &color
      • &blur
      • &border
      • &bordercolor
      • &rounded
      • &margin
      • &darkmode
      • &lightmode
      • &background
      • &chroma
      • &transparent
      • &nocursor
      • &favicon
      • &headertitle
      • &rotate
      • &grid
      • &hideheader
      • &hidemenu
      • &tally
      • &tallyoff
      • &cleanviewer
      • &obsoff
      • &pip
      • &pipall
      • &pipme
    • Director Parameters
      • &director
      • &codirector
      • &blindall
      • &cleandirector
      • &hidesolo
      • &hidecodirectors
      • &minidirector
      • &orderby
      • &queue
      • &rooms
      • &broadcasttransfer
      • &showdirector
      • &slotmode
      • &previewmode
      • &novice
      • &layouts
      • &maindirectorpassword
      • &totalroombitrate
      • &limittotalbitrate
      • &notify
      • &mutespeaker=0
      • &showconnections
      • &widget
      • &pausepreview
    • Screen-share Parameters
      • &screensharestereo
      • &screenshare
      • &screenshare2
      • &screenshareaec
      • &screenshareautogain
      • &screensharecursor
      • &screensharedenoise
      • &screensharefps
      • &screensharehide
      • &screenshareid
      • &screensharelabel
      • &screensharequality
      • &screensharecontenthint
      • &screenshareaspectratio
      • &screensharetype
      • &smallshare
      • &screensharevideoonly
      • &suppresslocalaudio
      • &prefercurrenttab
      • &selfbrowsersurface
      • &systemaudio
      • &displaysurface
      • &screensharebutton
      • &screensharebitrate
      • &sharperscreen
      • &sspaused
    • Recording Parameters
      • &record
      • &autorecord
      • &autorecordlocal
      • &autorecordremote
      • &recordcodec
      • &pcm
      • &recordmotion
      • &chunked
    • Guest queuing Parameters
      • &queue
      • &screen
      • &hold
      • &holdwithvideo
      • &queuetransfer
    • Meshcast Parameters
      • &meshcast
      • &meshcastaudiobitrate
      • &meshcastbitrate
      • &meshcastcodec
      • &mcscreensharebitrate
      • &mcscreensharecodec
      • &meshcastscale
      • &meshcastcode
      • &nomeshcast
    • WHIP Parameters
      • &whipout
      • &whipview
      • &whipoutcodec
      • &whipoutaudiobitrate
      • &whipoutvideobitrate
      • &whipoutscale
      • &whipoutscreensharecodec
      • &whipoutscreensharebitrate
      • &cftoken
      • &svc
    • Mobile Parameters
      • &facing
      • &forcelandscape
      • &forceportrait
      • &forceios
      • &notios
      • &flagship
      • &mobile
      • &notmobile
      • &app
    • API & MIDI Parameters
      • &api
        • API reference
        • API reference - AI Generated
        • Client (node) event example
      • &pie
      • &midi
      • &midiin
      • &midiout
      • &midiremote
      • &midichannel
      • &mididevice
      • &midioffset
      • &mididelay
      • &datamode
      • &postapi
    • TURN & STUN Parameters
      • &turn
      • &stun
      • &addstun
      • &icefilter
      • &proxy
      • &relay
      • &secure
      • &tcp
      • &tz
    • Parameters added in Version 24
    • Complete List of Parameters in v26
    • Upcoming Parameters
    • Other Parameters
  • Releases
    • v24
    • v23 🌱
    • v22 👑
    • v21 ❤️
    • v20 🎁
    • v19 🚀🤯
      • v19.1 - 19.4
    • v18
      • v18.3
    • v17
    • v16
      • v16.3
      • v16.4
    • v15
    • v14
    • v13
      • v13.4
    • v12
    • v10
    • v8
  • Updates
    • Updates - VDO.Ninja
    • Updates - Social Stream & Chat Overlay
      • Updates - Social Stream Standalone App
    • Updates - Electron Capture App
    • Updates - Raspberry.Ninja
    • Updates - Versus.cam
    • Updates - Mixer App
    • Updates - WHIP/WHEP
    • Updates - Native mobile apps
    • Updates - Caption.Ninja
    • Updates - Meshcast.io
    • Updates - Speed Test
    • Updates - Comms
    • Updates - Miscellaneous
  • Development Progress
  • Help!
    • Fail safes and Backups
    • Privacy and security details
      • VDO.Ninja Terms of Service
      • VDO.Ninja Privacy Policy
    • Project Contact Info
    • Where can I report a bug?
    • Where can I get support?
    • Feature Requests
    • Logos and media assets
    • What does VDO stand for?
  • Common errors and known issues
    • Can't screen capture certain games
    • ATEM not working with Firestick
    • Very old iPhone support
    • Can't select audio output on iOS
    • Screen-share is just a black video
    • Mic audio dropping out
    • Loss of audio when OBS minimized
    • Known issues
    • Echo or feedback issues
    • Works on WiFi but not on 4G
    • Can't capture an application's audio when screen-sharing
    • Can't load camera both in OBS and VDON
    • Can't select a camera lens on mobile
    • No video in OBS, just an "Add camera" button
    • Audio over VDO.Ninja isn't working
    • Loading circle shows in OBS or browser
    • Appearing then disappearing guest
    • Can't auto-start screen sharing
    • Audio Clicking / Popping / Distortion
    • Can't share my screen
    • Nothing shows up in OBS
    • Already in use or claimed errors
    • Blue spinning window
    • Cursor shows trailing or artifacting
    • Packet Loss
    • Overheating
    • Audio is delayed in OBS
    • vMix High CPU
    • OBS Virtual Camera has low FPS
    • Virtual camera not working on Mac
    • Mic stops on MacOS when OBS opens
    • Video stream looks corrupted
    • Video freezes mid-stream
    • Webcam freezes after a time
    • Is the VDO.Ninja server down?
    • Hosted your own TURN server?
    • Can't screen-share from certain devices
    • Cursor shows when screen-sharing
    • Getting “Overconstrained" Camera Error
    • Autoplay doesn't work in Chrome or vMix v77
    • Low frame rates
    • Black borders around the video in OBS
    • Mic's volume keeps changing
    • Enable Camera / Microphone permissions
    • FPS drop if app not in focus
    • Surround sound error when screen sharing with USB headset
    • Relay candidate being selected
    • Camera works in Safari; not Chrome
    • Robotic Audio Distortion
    • Can't load camera from non-SSL host
    • Camera on macOS doesn't show?
    • Can't screen share Adobe Lightroom
    • Decklink support?
    • iOS audio stops during phone calls
    • Can't turn off echo-cancellation on macOS
    • Video lag grows over time
    • Can't connect unless via VPN
    • Improving vMix performance
  • Platform specific issues
    • Android
    • macOS
    • iOS (iPhone/iPad)
    • Firefox
    • Opera GX
  • Useful Links
  • FAQ
  • Sponsor ❤
  • Edit this documentation
Powered by GitBook
On this page
  • Understanding the P2P Data Channels
  • Why VDO.Ninja's P2P Data Channels Are Powerful
  • Basic Setup
  • Setting Up Event Listeners
  • Sending Data
  • Tracking Connected Peers
  • Getting All Connected StreamIDs
  • Detailed State Information
  • Data Structure Best Practices
  • Complete Example: Simple Chat System
  • Best Practices
  • Troubleshooting

Was this helpful?

  1. Guides
  2. How to embed VDO.Ninja into a site with iFrames

Generic P2P Data Transmission Guide

Generic P2P Data Transmission Guide

This guide focuses specifically on how to send and receive generic data between clients using VDO.Ninja's peer-to-peer (P2P) data channels over the IFRAME API.

Understanding the P2P Data Channels

VDO.Ninja provides a powerful API that allows websites to send arbitrary data between connected clients through its peer-to-peer infrastructure. This enables you to:

  • Create custom communication channels between clients

  • Implement application-specific data exchange

  • Build interactive multi-user experiences

  • Exchange any type of serializable data

Why VDO.Ninja's P2P Data Channels Are Powerful

VDO.Ninja's data channels offer several compelling advantages that make them ideal for modern web applications:

  • Production-Proven Reliability: Used in production applications like Social Stream Ninja, which processes hundreds of messages per minute per peer connection

  • Automatic LAN Optimization: Detects when connections are on the same local network and routes data directly, reducing latency

  • Firewall Traversal: Enables communication between devices behind different firewalls without port forwarding

  • Cost-Effective: No server costs or bandwidth charges for data transmission, as everything happens peer-to-peer

  • Low Latency: Direct connections between peers minimize delay, ideal for real-time applications

  • Scalability: Each peer connects directly to others, allowing for programmed distribution of loads

  • AI Integration Ready: Perfect for distributing AI processing tasks or sharing AI-generated content between users, or accessing private AI services that are behind firewalls.

  • Remote Control Applications: Enables secure remote control of devices through firewalls without complex networking setups

  • Works Across Platforms: Functions on mobile, desktop, and various browsers without additional plugins

The creators of VDO.Ninja use these data channels in numerous applications beyond video, demonstrating their versatility and reliability in real-world scenarios.

Basic Setup

First, set up your VDO.Ninja iframe:

// 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 Event Listeners

To receive data from other clients, set up an event listener:

// Set up event listener (cross-browser compatible)
var eventMethod = window.addEventListener ? "addEventListener" : "attachEvent";
var eventer = window[eventMethod];
var messageEvent = eventMethod === "attachEvent" ? "onmessage" : "message";

// Connected peers storage
var connectedPeers = {};

// Add the event listener
eventer(messageEvent, function(e) {
    // Make sure the message is from our VDO.Ninja iframe
    if (e.source != iframe.contentWindow) return;
    
    // Process connection events to track connected peers
    if ("action" in e.data) {
        handleConnectionEvents(e.data);
    }
    
    // Handle received data
    if ("dataReceived" in e.data) {
        handleDataReceived(e.data.dataReceived, e.data.UUID);
    }
}, false);

function handleConnectionEvents(data) {
    if (data.action === "guest-connected" && data.streamID) {
        // Store connected peer information
        connectedPeers[data.streamID] = data.value?.label || "Guest";
        console.log("Guest connected:", data.streamID, "Label:", connectedPeers[data.streamID]);
    } 
    else if (data.action === "push-connection" && data.value === false && data.streamID) {
        // Remove disconnected peers
        console.log("Guest disconnected:", data.streamID);
        delete connectedPeers[data.streamID];
    }
}

function handleDataReceived(data, senderUUID) {
    console.log("Data received from:", senderUUID, "Data:", data);
    
    // Example: Check for your custom data namespace
    if (data.overlayNinja) {
        processCustomData(data.overlayNinja, senderUUID);
    }
}

function processCustomData(data, senderUUID) {
    // Process based on your application's needs
    console.log("Processing custom data:", data);
    
    // Example: Handle different data types
    if (data.message) {
        displayMessage(data.message);
    } else if (data.command) {
        executeCommand(data.command);
    }
}

Sending Data

Send Data Structure

When sending data via the VDO.Ninja IFRAME API, you use this general format:

iframe.contentWindow.postMessage({
    sendData: yourDataPayload,
    type: "pcs",  // Connection type (see below)
    UUID: targetUUID  // Optional: specific target
}, "*");

The components are:

  • sendData: Your data payload (object)

  • type: Connection type (string)

    • "pcs": Use peer connections (most reliable)

    • "rpcs": Use request-based connections

  • UUID or streamID: Optional target identifier

Sending to All Connected Peers

function sendDataToAllPeers(data) {
    // Create the data structure with your custom namespace
    var payload = {
        overlayNinja: data  // Your custom data under a namespace
    };
    
    // Send to all peers
    iframe.contentWindow.postMessage({
        sendData: payload,
        type: "pcs"  // Use peer connection for reliability
    }, "*");
}

// Example usage
sendDataToAllPeers({
    message: "Hello everyone!",
    timestamp: Date.now()
});

Sending to a Specific Peer by UUID

function sendDataToPeer(data, targetUUID) {
    // Create the data structure
    var payload = {
        overlayNinja: data  // Your custom data
    };
    
    // Send to specific UUID
    iframe.contentWindow.postMessage({
        sendData: payload,
        type: "pcs",
        UUID: targetUUID
    }, "*");
}

// Example usage
sendDataToPeer({
    message: "Hello specific peer!",
    timestamp: Date.now()
}, "peer-uuid-123");

Sending to Peers with Specific Labels

function sendDataByLabel(data, targetLabel) {
    // Create the data structure
    var payload = {
        overlayNinja: data  // Your custom data
    };
    
    // Iterate through connected peers to find those with matching label
    var keys = Object.keys(connectedPeers);
    for (var i = 0; i < keys.length; i++) {
        try {
            var UUID = keys[i];
            var label = connectedPeers[UUID];
            if (label === targetLabel) {
                // Send to this specific peer
                iframe.contentWindow.postMessage({
                    sendData: payload,
                    type: "pcs",
                    UUID: UUID
                }, "*");
            }
        } catch (e) {
            console.error("Error sending to peer:", e);
        }
    }
}

// Example usage
sendDataByLabel({
    message: "Hello all viewers!",
    timestamp: Date.now()
}, "viewer");

Sending to a Peer by StreamID

function sendDataByStreamID(data, streamID) {
    // Create the data structure
    var payload = {
        overlayNinja: data  // Your custom data
    };
    
    // Send to specific streamID
    iframe.contentWindow.postMessage({
        sendData: payload,
        type: "pcs",
        streamID: streamID
    }, "*");
}

// Example usage
sendDataByStreamID({
    message: "Hello by stream ID!",
    timestamp: Date.now()
}, "stream-123");

Tracking Connected Peers

To reliably communicate with peers, keep track of connections and disconnections:

// Store connected peers
var connectedPeers = {};

function handleConnectionEvents(data) {
    // Guest connections
    if (data.action === "guest-connected" && data.streamID) {
        connectedPeers[data.streamID] = data.value?.label || "Guest";
        console.log("Guest connected:", data.streamID, "Label:", connectedPeers[data.streamID]);
    }
    // View connections
    else if (data.action === "view-connection") {
        if (data.value && data.streamID) {
            connectedPeers[data.streamID] = "Viewer";
            console.log("Viewer connected:", data.streamID);
        } else if (data.streamID) {
            console.log("Viewer disconnected:", data.streamID);
            delete connectedPeers[data.streamID];
        }
    }
    // Director connections
    else if (data.action === "director-connected") {
        console.log("Director connected");
    }
    // Handle disconnections
    else if (data.action === "push-connection" && data.value === false && data.streamID) {
        console.log("User disconnected:", data.streamID);
        delete connectedPeers[data.streamID];
    }
}

Getting All Connected StreamIDs

You can request a list of all connected streams:

function getConnectedPeers() {
    iframe.contentWindow.postMessage({ getStreamIDs: true }, "*");
}

// In your event listener, handle the response:
if ("streamIDs" in e.data) {
    console.log("Connected streams:");
    for (var key in e.data.streamIDs) {
        console.log("StreamID:", key, "Label:", e.data.streamIDs[key]);
    }
}

Detailed State Information

For more comprehensive information about the current state:

function getDetailedState() {
    iframe.contentWindow.postMessage({ getDetailedState: true }, "*");
}

// Handle the response in your event listener

Data Structure Best Practices

  1. Use a Namespace: Put your data under a custom namespace to avoid conflicts

    {
      sendData: {
        yourAppName: {
          // Your data here
        }
      }
    }
  2. Include Type Information: Include type identifiers to differentiate messages

    {
      sendData: {
        yourAppName: {
          type: "command",
          data: { /* command data */ }
        }
      }
    }
  3. Include Timestamp: Add timestamps to help with ordering

    {
      sendData: {
        yourAppName: {
          type: "update",
          data: { /* update data */ },
          timestamp: Date.now()
        }
      }
    }

Complete Example: Simple Chat System

Here's a complete example implementing a simple chat system using the P2P data channels:

// Create the interface
const container = document.createElement('div');
container.style.width = '100%';
container.style.maxWidth = '800px';
container.style.margin = '0 auto';
document.body.appendChild(container);

// Create VDO.Ninja iframe
const iframe = document.createElement('iframe');
iframe.allow = "camera;microphone;fullscreen;display-capture;autoplay;";
iframe.src = "https://vdo.ninja/?room=chat-demo&cleanoutput";
iframe.style.width = "100%";
iframe.style.height = "360px";
container.appendChild(iframe);

// Create chat interface
const chatContainer = document.createElement('div');
chatContainer.style.marginTop = '20px';
container.appendChild(chatContainer);

const chatMessages = document.createElement('div');
chatMessages.style.height = '300px';
chatMessages.style.border = '1px solid #ccc';
chatMessages.style.padding = '10px';
chatMessages.style.overflowY = 'scroll';
chatContainer.appendChild(chatMessages);

const inputContainer = document.createElement('div');
inputContainer.style.marginTop = '10px';
inputContainer.style.display = 'flex';
chatContainer.appendChild(inputContainer);

const messageInput = document.createElement('input');
messageInput.type = 'text';
messageInput.placeholder = 'Type your message...';
messageInput.style.flexGrow = '1';
messageInput.style.padding = '8px';
inputContainer.appendChild(messageInput);

const sendButton = document.createElement('button');
sendButton.textContent = 'Send';
sendButton.style.marginLeft = '10px';
sendButton.style.padding = '8px 16px';
inputContainer.appendChild(sendButton);

// Store connected peers
const connectedPeers = {};

// Add event listeners
sendButton.addEventListener('click', sendChatMessage);
messageInput.addEventListener('keypress', function(e) {
    if (e.key === 'Enter') {
        sendChatMessage();
    }
});

function sendChatMessage() {
    const message = messageInput.value.trim();
    if (message) {
        // Create message object
        const chatData = {
            type: 'chat',
            text: message,
            sender: 'Me',
            timestamp: Date.now()
        };
        
        // Add to local chat
        addMessageToChat(chatData.sender, chatData.text);
        
        // Send to all peers
        sendDataToAllPeers(chatData);
        
        // Clear input
        messageInput.value = '';
    }
}

function addMessageToChat(sender, text) {
    const messageElement = document.createElement('div');
    messageElement.style.marginBottom = '8px';
    
    const senderSpan = document.createElement('strong');
    senderSpan.textContent = sender + ': ';
    messageElement.appendChild(senderSpan);
    
    const textNode = document.createTextNode(text);
    messageElement.appendChild(textNode);
    
    chatMessages.appendChild(messageElement);
    
    // Scroll to bottom
    chatMessages.scrollTop = chatMessages.scrollHeight;
}

function sendDataToAllPeers(data) {
    // Create the data structure
    const payload = {
        chatApp: data  // Using a custom namespace
    };
    
    // Send to all peers
    iframe.contentWindow.postMessage({
        sendData: payload,
        type: "pcs"
    }, "*");
}

// Set up event listener for messages from iframe
const eventMethod = window.addEventListener ? "addEventListener" : "attachEvent";
const eventer = window[eventMethod];
const messageEvent = eventMethod === "attachEvent" ? "onmessage" : "message";

eventer(messageEvent, function(e) {
    // Make sure the message is from our VDO.Ninja iframe
    if (e.source != iframe.contentWindow) return;
    
    // Process connection events
    if ("action" in e.data) {
        handleConnectionEvents(e.data);
    }
    
    // Handle received data
    if ("dataReceived" in e.data) {
        handleDataReceived(e.data.dataReceived, e.data.UUID);
    }
}, false);

function handleConnectionEvents(data) {
    if (data.action === "guest-connected" && data.streamID) {
        // Store connected peer information
        connectedPeers[data.streamID] = data.value?.label || "Guest";
        console.log("Guest connected:", data.streamID, "Label:", connectedPeers[data.streamID]);
        
        // Announce new connection in chat
        addMessageToChat("System", `${connectedPeers[data.streamID]} joined the chat`);
    } 
    else if (data.action === "push-connection" && data.value === false && data.streamID) {
        // Announce disconnection
        if (connectedPeers[data.streamID]) {
            addMessageToChat("System", `${connectedPeers[data.streamID]} left the chat`);
        }
        
        // Remove from tracking
        console.log("Guest disconnected:", data.streamID);
        delete connectedPeers[data.streamID];
    }
}

function handleDataReceived(data, senderUUID) {
    // Check for chat messages
    if (data.chatApp && data.chatApp.type === 'chat') {
        const chatData = data.chatApp;
        
        // Get sender name from our peer tracking if available
        const senderName = connectedPeers[senderUUID] || chatData.sender || "Unknown";
        
        // Add to chat
        addMessageToChat(senderName, chatData.text);
    }
}

Best Practices

  1. Track Connections: Always maintain a list of connected peers

  2. Use Namespaces: Organize your data under custom namespaces

  3. Add Type Information: Include message types for easier processing

  4. Include Timestamps: Help with ordering and synchronization

  5. Error Handling: Use try/catch blocks when sending messages

  6. Data Size: Keep payloads reasonably small to avoid performance issues

  7. UUID vs StreamID: Prefer UUID for targeting as it's more stable

Troubleshooting

  • No Data Received: Verify the UUID or streamID is correct

  • Connection Issues: Check if peers are properly connected before sending

  • Timing Problems: Ensure the iframe is fully loaded before sending messages

  • Data Format: Make sure your data is properly serializable

  • Security Settings: Check that your iframe permissions are set correctly

By following this guide, you can implement robust P2P data exchange between VDO.Ninja clients for any custom application.

PreviousCreate custom drawing appNextHow to use the green screen just locally

Last updated 4 hours ago

Was this helpful?