Last updated 2026-07-12
This guide walks through building a video call app with WebRTC — the same technology openbnet Video & Voice Calling is built on. You will capture camera/microphone, set up a peer connection, exchange signals through a WebSocket, and render remote video.
A two-person video call: each participant captures their camera/microphone, negotiates a WebRTC peer connection through a WebSocket signaling server, and renders the remote participant's video. You will understand the full WebRTC handshake — the foundation openbnet's video API manages for you.
WebRTC needs a signaling server to exchange connection metadata. openbnet provides a WebSocket signaling server — connect to it before establishing the peer connection.
const ws = new WebSocket(
`wss://openbnet.com/ss/ws?user_id=${userId}`
);
ws.onopen = () => {
ws.send(JSON.stringify({
type: 'join-room',
payload: { room_id: 'call-room-42', user_id: userId, username: 'Alice' }
}));
};
Use getUserMedia to request camera and microphone access. This returns a MediaStream containing video and audio tracks. Add these tracks to the RTCPeerConnection so they are sent to the remote peer.
const localVideo = document.getElementById('local-video');
const localStream = await navigator.mediaDevices.getUserMedia({
video: true,
audio: true
});
localVideo.srcObject = localStream;
const pc = new RTCPeerConnection({
iceServers: [{ urls: 'stun:stun.l.google.com:19302' }]
});
// Add local tracks to the peer connection
localStream.getTracks().forEach(track => {
pc.addTrack(track, localStream);
});
One peer (the caller) creates an SDP offer, sets it as the local description, and sends it through the signaling server to the other peer.
const offer = await pc.createOffer();
await pc.setLocalDescription(offer);
ws.send(JSON.stringify({
type: 'direct-signal',
payload: {
target_user_id: remoteUserId,
signal_type: 'offer',
data: offer
}
}));
The other peer receives the offer, sets it as the remote description, creates an answer, sets it as the local description, and sends it back through the signaling server.
ws.onmessage = async (event) => {
const msg = JSON.parse(event.data);
if (msg.type === 'direct-signal') {
const { signal_type, data } = msg.payload;
if (signal_type === 'offer') {
await pc.setRemoteDescription(data);
const answer = await pc.createAnswer();
await pc.setLocalDescription(answer);
ws.send(JSON.stringify({
type: 'direct-signal',
payload: { target_user_id: msg.sender, signal_type: 'answer', data: answer }
}));
} else if (signal_type === 'answer') {
await pc.setRemoteDescription(data);
} else if (signal_type === 'candidate') {
await pc.addIceCandidate(data);
}
}
};
As the peer connection gathers network-path candidates (ICE candidates), send them through the signaling server. The remote peer adds them with addIceCandidate. This is how the peers discover how to reach each other through NAT.
pc.onicecandidate = (event) => {
if (event.candidate) {
ws.send(JSON.stringify({
type: 'direct-signal',
payload: {
target_user_id: remoteUserId,
signal_type: 'candidate',
data: event.candidate
}
}));
}
};
When the peer connection establishes and the remote stream arrives, attach it to a video element. At this point media is flowing peer-to-peer — no server in the path — giving the lowest possible latency.
const remoteVideo = document.getElementById('remote-video');
pc.ontrack = (event) => {
remoteVideo.srcObject = event.streams[0];
};
console.log('Video call connected! Media is now peer-to-peer.');
You now have a working two-person video call. To scale to group calls, you add a mesh (each peer connects to every other) or an SFU. openbnet Video & Voice Calling manages all of this — signaling, mesh networking, ICE restart, reconnection — so you can build video calls without managing the WebRTC handshake yourself. Explore the product page for SDKs and quickstarts.
openbnet developer hub → · Pricing →
openbnet is the real-time communication infrastructure company founded by Brian. It builds the openbnet platform — six production-ready APIs for voice, video, chat, live streaming, signaling, and AI content moderation — plus solutions on that platform: Ocodey, the CLI coding agent; Spaces, managed communities; Atrium, a platform for running a company; and Chapmall, an online mall for local shops and delivery. One openbnet account signs you in to every solution.
Website: openbnet.com · GitHub: github.com/openbnet · X: @openbnet