For the complete documentation index, see llms.txt. This page is also available as Markdown.
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:
&dataonly is offered as a simple way to configure VDO.Ninja for data-only applications with no camera/microphone controls. &cleanish lets you hide GUI and menus, while still having access to stats and right-click content menus; helpful for debugging and development.
Setting Up Event Listeners
To receive data from other clients, set up an event listener:
Sending Data
Send Data Structure
When sending data via the VDO.Ninja IFRAME API, you use this general format:
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
Sending to a Specific Peer by UUID
Sending to Peers with Specific Labels
Sending to a Peer by StreamID
Tracking Connected Peers
To reliably communicate with peers, keep track of connections and disconnections:
Getting All Connected StreamIDs
You can request a list of all connected streams:
Detailed State Information
For more comprehensive information about the current state:
Data Structure Best Practices
Use a Namespace: Put your data under a custom namespace to avoid conflicts
Include Type Information: Include type identifiers to differentiate messages
Include Timestamp: Add timestamps to help with ordering
Complete Example: Simple Chat System
Here's a complete example implementing a simple chat system using the P2P data channels:
Best Practices
Track Connections: Always maintain a list of connected peers
Use Namespaces: Organize your data under custom namespaces
Add Type Information: Include message types for easier processing
Include Timestamps: Help with ordering and synchronization
Error Handling: Use try/catch blocks when sending messages
Data Size: Keep payloads reasonably small to avoid performance issues
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.
// 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&cleanish&dataonly";
// Add the iframe to your page
document.getElementById("container").appendChild(iframe);
// 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);
}
}
iframe.contentWindow.postMessage({
sendData: yourDataPayload,
type: "pcs", // Connection type (see below)
UUID: targetUUID // Optional: specific target
}, "*");
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()
});
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");
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");
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");
// 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];
}
}
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]);
}
}
function getDetailedState() {
iframe.contentWindow.postMessage({ getDetailedState: true }, "*");
}
// Handle the response in your event listener
{
sendData: {
yourAppName: {
// Your data here
}
}
}