Open source · Browser & Node.js

Build with VDO.Ninja

A JavaScript SDK for live video, audio, and data between browsers and Node.js apps. Peers connect directly over WebRTC, so there is no media server to run and no account or API key to manage.

npm install @vdoninja/sdk

MPL-2.0 licensed · Works with VDO.Ninja, OBS, and WHIP/WHEP services

app.js
const vdo = new VDONinjaSDK();

// Receive messages from any peer in the room
vdo.addEventListener('dataReceived', (e) => {
    console.log(e.detail.data);
});

// Join a room, announce yourself, connect to peers
await vdo.autoConnect({ room: 'my_unique_room' });

// Send to every connected peer
vdo.sendData({ hello: 'world' });

Two peers, one page, a direct connection

Each panel below is a separate SDK instance. Connect both and they find each other through the handshake server, then exchange messages directly over a WebRTC data channel.

  1. Connect both peers. Each joins the same private room.
  2. Send a message from either panel.
  3. See it arrive on the other side, peer to peer.
Connect both peers to start. Room —

Peer 1

Not connected
Stream ID —
Messages will appear here

Peer 2

Not connected
Stream ID —
Messages will appear here
The code each peer runs
const vdo = new VDONinjaSDK();

vdo.addEventListener('dataReceived', (e) => {
    showMessage(e.detail.data.message);
});

// Connects, joins the room, announces a data-only stream,
// and opens a data channel to the other peer
await vdo.autoConnect({ room: 'my_unique_room', label: 'Peer 1' });

// After 'dataChannelOpen' fires:
vdo.sendData({ message: 'Hello!' });
Connection log

Up and running in a few lines

Pick where your code runs. Every example connects to the same hosted handshake server, so a browser, a Node.js process, and VDO.Ninja itself can all join one room.

1. Add the script

<script src="https://cdn.jsdelivr.net/gh/steveseguin/ninjasdk@latest/vdoninja-sdk.min.js"></script>

Or install from npm: npm install @vdoninja/sdk. Pin a release tag such as @vX.Y.Z instead of @latest for reproducible builds.

2. Exchange data between two tabs

Open this page in two tabs with the same room name. Each peer waits for a data channel before sending.

const vdo = new VDONinjaSDK();

vdo.addEventListener('dataReceived', (event) => {
    const { data, uuid } = event.detail;
    console.log(`Received from ${uuid}:`, data);
});

vdo.addEventListener('dataChannelOpen', () => {
    vdo.sendData({ message: 'Hello from this peer!' });
});

await vdo.autoConnect({ room: 'replace_with_your_unique_room' });

Connection guide: rooms, passwords, hosts, and troubleshooting →

Publish a camera, view it anywhere

Capture media with getUserMedia, publish it under a stream ID, and receive remote tracks with the track event. Camera access needs HTTPS or localhost.

The published stream can be opened in VDO.Ninja, a scene, or an OBS browser source using the viewer link in the last line.

const vdo = new VDONinjaSDK({ salt: 'vdo.ninja', host: 'wss://apibackup.vdo.ninja' });

vdo.addEventListener('track', (event) => {
    const video = document.getElementById('remoteVideo');
    if (!video.srcObject) video.srcObject = new MediaStream();
    video.srcObject.addTrack(event.detail.track);
});

const stream = await navigator.mediaDevices.getUserMedia({ audio: true, video: true });

await vdo.connect();
await vdo.joinRoom({ room: 'videoroom' });
await vdo.publish(stream, { streamID: 'my_camera' });

// Viewer: https://vdo.ninja/alpha/?view=my_camera&room=videoroom&scene&wss2=apibackup.vdo.ninja

Media and streaming guide →

Install the SDK and a WebRTC runtime

npm install @vdoninja/sdk @roamhq/wrtc
# data-only bots can use node-datachannel instead

The same API runs in Node.js. Use @roamhq/wrtc for audio and video, or node-datachannel for lightweight data-only peers.

const VDONinjaSDK = require('@vdoninja/sdk/node');

const vdo = new VDONinjaSDK();

vdo.addEventListener('dataReceived', (event) => {
    console.log('Received:', event.detail.data);
});

await vdo.autoConnect({ room: 'replace_with_your_unique_room' });

Node.js setup guide →

No SDK: embed VDO.Ninja

For simple data exchange you can embed VDO.Ninja in an iframe and talk to it with postMessage. Use the SDK when you want your own interface and full control.

<iframe id="vdo" src="https://vdo.ninja/?room=my_room&dataonly"></iframe>
<script>
const iframe = document.getElementById('vdo');

// Listen for data
window.addEventListener('message', (e) => {
    if (e.source !== iframe.contentWindow) return;
    if (e.data.dataReceived) console.log('Received:', e.data.dataReceived);
});

// Send data
iframe.contentWindow.postMessage({ sendData: { msg: 'Hello!' }, type: 'pcs' }, '*');
</script>
Full iframe example with peer tracking and targeted sends
<iframe id="vdo"
        src="https://vdo.ninja/?room=my-data-room&cleanish&dataonly"
        allow="camera;microphone;fullscreen;display-capture;autoplay;"
        style="width: 100%; height: 400px;">
</iframe>

<script>
const iframe = document.getElementById('vdo');
const connectedPeers = {};

window.addEventListener('message', function (e) {
    // Only accept messages from our iframe
    if (e.source !== iframe.contentWindow) return;

    if (e.data.action === 'guest-connected' && e.data.streamID) {
        connectedPeers[e.data.streamID] = e.data.value?.label || 'Guest';
        console.log('Peer connected:', e.data.streamID);
    }

    if (e.data.dataReceived) {
        console.log('Data from', e.data.UUID + ':', e.data.dataReceived);
        if (e.data.dataReceived.myApp) handleCustomData(e.data.dataReceived.myApp);
    }
});

// Send data to all peers (namespace it to avoid conflicts)
function sendToAll(data) {
    iframe.contentWindow.postMessage({ sendData: { myApp: data }, type: 'pcs' }, '*');
}

// Send data to one peer
function sendToPeer(data, targetUUID) {
    iframe.contentWindow.postMessage({ sendData: { myApp: data }, type: 'pcs', UUID: targetUUID }, '*');
}

iframe.onload = function () {
    setTimeout(() => sendToAll({ message: 'Hello from P2P!', timestamp: Date.now() }), 2000);
};

function handleCustomData(data) {
    console.log('Custom data received:', data);
}
</script>

CDN

Load the minified build from jsDelivr. @latest follows the newest release.

≈135 KB minified≈32 KB gzipped

Download

Host the files yourself. The full build is readable and commented.

npm

Includes TypeScript types, the Node.js entry point, and standalone WHIP/WHEP clients.

Direct connections, without the infrastructure

A small hosted handshake server introduces peers. After that, media and data travel peer to peer, using a TURN relay only when a direct path is not possible.

Peer to peer by default

No relay server to scale or pay for. Bandwidth comes from the peers themselves.

Encrypted in transit

WebRTC encrypts every connection with DTLS and SRTP. The handshake server only exchanges connection setup.

No accounts or API keys

Pick a room name and connect. Add a password to keep the room private.

Browser and Node.js

One API in both. Run headless bots, recorders, and bridges next to your web app.

Works with VDO.Ninja

Open your streams in VDO.Ninja's director room, scenes, and OBS browser sources.

WHIP and WHEP included

Standalone clients for publishing to and viewing from Cloudflare Stream, Twitch, Meshcast, and more.

What people build with it

Remote control and dashboards

Control OBS, stage equipment, or kiosks from a phone. Peers reach each other through NAT without port forwarding or a VPN.

Live video tools

Publish cameras, screens, or canvas output and show them in your own interface, in OBS, or in VDO.Ninja.

AI with live video and audio

Stream a camera or microphone to a local model or bot and send results back over the same connection.

Chat and real-time apps

Low-latency messaging, shared state, and pub/sub channels. Social Stream Ninja uses it to deliver live chat for free.

Local network streaming

When peers share a LAN, WebRTC keeps traffic on the local network, which suits studios, classrooms, and venues.

Node.js automation

Record every participant, generate audio, forward tally to OSC, or run bots without a browser.

How it compares

VDO.Ninja SDK Socket.io Firebase Realtime Raw WebRTC
Data pathPeer to peer, TURN fallbackThrough your serverThrough the cloud servicePeer to peer
Servers you runNone (hosted signaling)WebSocket serverNone (managed)Signaling and TURN
Audio and videoBuilt inNoNoYes, manual setup
NAT traversalBuilt in (STUN/TURN)Not neededNot neededYou configure it
Accounts and keysNoneYour own authProject and API keysYour own
CostFreeHostingUsage basedHosting and TURN

Guides for common tasks

Not sure where to start? Compare the SDK, iframe, page API, MCP, and WHIP/WHEP options, including which IDs and credentials each needs.

Core concepts

Room
A named space where peers discover each other. A password changes how the name is hashed, so only peers with it meet.
Stream ID
How a peer announces itself so others can view it.
Publish and view
Publishing sends media or data; viewing receives it.
Handshake server
Exchanges connection setup only. It does not carry your media or messages.
Open the API reference

Connection flow

await vdo.connect();                                  // 1. signaling
await vdo.joinRoom({ room: 'myroom', password: 'optional' }); // 2. room

await vdo.publish(stream, { streamID: 'cam-1' });     // 3a. send media
await vdo.announce({ streamID: 'bot-1' });           // 3b. or data only

await vdo.view('cam-1', { audio: true, video: true }); // 4. receive

vdo.addEventListener('track', (e) => { /* e.detail.track */ });
vdo.addEventListener('dataReceived', (e) => { /* e.detail.data */ });

For AI coding assistants

Give your assistant the whole SDK guide in one paste: API, patterns, and pitfalls.

Read it llms.txt

Part of the VDO.Ninja family

Apps built with the SDK speak the same protocol as VDO.Ninja, so you can reuse its interface and tools instead of building your own.

VDO.Ninja

Invite guests, manage rooms, and mix cameras with VDO.Ninja's director and scene layouts. Bring any SDK stream into OBS as a browser source.

  • Director room with multi-camera switching
  • Scene links and layouts for OBS
  • Recording options
Open VDO.Ninja

Social Stream Ninja

Two-way live chat from Twitch, YouTube, TikTok, Kick, X, Facebook, Discord, Instagram, and more, delivered to your app over P2P data channels.

Listen to Social Stream Ninja chat
const sdk = new VDONinjaSDK({
    host: 'wss://wss.socialstream.ninja',
    room: 'your-session-id',  // Social Stream calls this the session ID
    password: false
});

sdk.on('dataReceived', (event) => {
    const msg = event.detail?.data?.overlayNinja;
    if (msg) console.log(`[${msg.type}] ${msg.chatname}: ${msg.chatmessage}`);
});

await sdk.connect();
await sdk.joinRoom();
await sdk.view('your-session-id', { audio: false, video: false, label: 'dock' });

WHIP and WHEP

Besides rooms, the SDK ships standalone clients for the standard WebRTC HTTP protocols: publish to Twitch, Meshcast, or Cloudflare Stream, and view any WHEP endpoint.

Publish and view with Meshcast
// Publish
const whip = new WHIPClient('https://cae1.meshcast.io/whip/mystream');
await whip.publish(mediaStream);

// View
const whep = new WHEPClient('https://cae1.meshcast.io/whep/mystream');
whep.addEventListener('track', (e) => video.srcObject = e.detail.streams[0]);
await whep.view();

MCP add-on for AI agents Optional

@vdoninja/mcp gives AI tools private, encrypted P2P rooms for agent-to-agent collaboration.

  • A Claude Code session asking a Codex CLI session for help
  • OpenClaw bots collaborating in real time
  • Encrypted group chats for brainstorming and review loops

Other platforms: Raspberry.Ninja for Python, and the Flutter sample app for mobile.

Frequently asked questions

Do I need to run my own server?

No. The SDK uses a free hosted handshake server to set up WebRTC connections. After that, media and data travel peer to peer, or through a TURN relay when a direct path is not possible.

Is it really free?

Yes. There are no paid tiers. Because peers carry the traffic, our server costs stay small and we cover them. Please follow the usage guidelines so it stays that way.

How is this different from Socket.io or WebSockets?

WebSockets send every message through a server you run. The SDK connects browsers directly, which lowers latency and removes the relay server and its bandwidth bill.

What about firewalls and NAT?

WebRTC uses STUN to find a direct path and falls back to the free TURN relays we provide when it cannot. That works on most networks, including many restrictive ones.

Can bots and AI agents use it?

Yes. There is no CAPTCHA, email verification, or account. Connect with a room name and start exchanging data. Automated clients must still follow the usage guidelines.

What is the latency like?

Usually just the network distance between peers, often under 50 ms within the same region. Server-relayed systems add an extra hop. On the same LAN it can be a few milliseconds.

How many peers can connect?

Rooms are limited to about 80 connections. A full mesh works best with 5 to 10 peers; broadcast layouts can serve 50 or more viewers. Individual streams may also limit viewers. Higher limits are available on request.

Is it secure?

WebRTC encrypts every connection: DTLS for data channels and SRTP for media. After setup, the handshake server does not carry your media or messages. Use a room password to keep others out of your room.

Can I use it commercially?

Yes. MPL-2.0 permits the SDK to be used and distributed in a larger proprietary application without relicensing the application's separate files. You must preserve the SDK's notices and make its MPL-covered source available as required. If you distribute a modified MPL-covered SDK file, that file and its source remain subject to MPL-2.0.

Which platforms are supported?

Browsers: Chrome/Edge 80+, Firefox 75+, Safari 14+, and mobile browsers with WebRTC.

Node.js: full support with @roamhq/wrtc or node-datachannel. See the Node.js guide.

Python: via Raspberry.Ninja. Mobile: the vdon_flutter sample app.

Can I self-host the handshake server?

Self-hosting support is planned for an upcoming version of the SDK.

Does traffic stay on my local network?

Yes, once connected. When two peers share a LAN, WebRTC picks the local path and media and data stay on that network. The initial handshake still needs to reach the signaling server.

Using the free handshake server

The handshake server only helps peers find each other and set up WebRTC. Once peers connect, it is no longer involved. These rules keep it free and reliable for everyone.

Always use the SDK

  • Direct WebSocket connections that bypass the SDK are not permitted and will be blocked.
  • The handshake protocol can change without notice; the SDK keeps up with it.

Signaling only

  • The server exchanges offers, answers, and ICE candidates, and keeps no state after the handshake.
  • Send all application data over peer-to-peer data channels. Using the handshake server as a data relay is prohibited.

Limits and fair use

  • Rate limits apply. Rapid reconnects or excessive handshakes are throttled.
  • Rooms support about 80 connections, and streams may cap viewers.
  • Planning a launch with high connection rates or a large event? Contact us first for higher limits.

Best practices

  • Load the official SDK from the CDN or GitHub, and keep it up to date.
  • Use exponential backoff when reconnecting.
  • Test your connection patterns before launch.

This handshake server is a free community service. Abuse affects everyone, so please be a good citizen of the ecosystem.