ThisCalculator

Everyday

WebRTC Video Call Between Two Browsers in React

Updated · Standard formula, shown below

WebRTC Video Call Between Two Browsers in React

To connect two browsers for a video call in React using WebRTC, you need to establish a peer-to-peer connection by exchanging session descriptions (SDP) and ICE candidates through a signaling server, then attach local and remote media streams to video elements. This article explains the core WebRTC flow, provides a minimal React implementation, and covers common pitfalls like STUN/TURN configuration and signaling.

Background: Why WebRTC for Browser-to-Browser Video

WebRTC (Web Real-Time Communication) is a free, open-source project that enables real-time audio, video, and data transfer directly between browsers without plugins. Unlike traditional client-server streaming, WebRTC uses peer-to-peer (P2P) connections, which reduces latency and server bandwidth costs. The technology is supported in all modern browsers (Chrome, Firefox, Safari, Edge) and is standardized by the W3C and IETF.

In a React app, WebRTC works the same as in vanilla JavaScript, but you integrate it with React’s component lifecycle and state management. The browser handles the heavy lifting—codecs, encryption, and network traversal—while your React code manages the signaling and UI.

How WebRTC Works: The Core Flow

Setting up a WebRTC connection between two browsers involves several steps:

  1. Get local media: Use navigator.mediaDevices.getUserMedia() to capture the camera and microphone.
  2. Create RTCPeerConnection: Instantiate a peer connection object with optional ICE servers.
  3. Add tracks: Add the local media tracks to the connection so they can be sent to the remote peer.
  4. Exchange SDP offers/answers: One peer creates an offer (with createOffer()), sets it as local description, and sends it to the other peer via signaling. The other peer sets it as remote description, creates an answer, and sends it back.
  5. Exchange ICE candidates: Each peer sends its ICE candidates (network addresses) to the other. The browser uses these to find the best path for the P2P connection.
  6. Receive remote stream: When the connection is established, the remote peer’s media arrives and is attached to a video element.

Signaling is not part of WebRTC—you must implement it using WebSockets, Socket.IO, or any messaging service. The signaling channel only carries metadata (SDP and ICE), not the actual media data.

Choosing a Signaling Method

For a simple two-browser demo, you can use a WebSocket server (e.g., with ws in Node.js) or a service like Firebase Realtime Database. For production, consider managed signaling services (e.g., Ably, Pusher) or full WebRTC platforms that handle signaling and TURN. The key is that the signaling server must reliably deliver messages between the two peers, even if they are behind NATs.

When choosing, consider: latency (WebSockets are low-latency), scalability (how many concurrent calls), and whether you need data channels for chat or file sharing. For a single call, a simple WebSocket server is sufficient.

Minimal React Implementation

Below is a conceptual React component that sets up a WebRTC call. This assumes you have a signaling server that sends messages to the other peer via a sendMessage function (e.g., Socket.IO).

import React, { useRef, useEffect, useState } from 'react';

function VideoCall({ socket }) {
  const localVideoRef = useRef();
  const remoteVideoRef = useRef();
  const pcRef = useRef();
  const [inCall, setInCall] = useState(false);

  useEffect(() => {
    // Set up socket event listeners
    socket.on('offer', async (offer) => {
      if (!pcRef.current) createPeerConnection();
      await pcRef.current.setRemoteDescription(offer);
      const answer = await pcRef.current.createAnswer();
      await pcRef.current.setLocalDescription(answer);
      socket.emit('answer', answer);
    });

    socket.on('answer', async (answer) => {
      await pcRef.current.setRemoteDescription(answer);
    });

    socket.on('ice-candidate', (candidate) => {
      pcRef.current.addIceCandidate(candidate);
    });
  }, [socket]);

  const createPeerConnection = () => {
    const pc = new RTCPeerConnection({ iceServers: [{ urls: 'stun:stun.l.google.com:19302' }] });
    pc.onicecandidate = (e) => socket.emit('ice-candidate', e.candidate);
    pc.ontrack = (e) => { remoteVideoRef.current.srcObject = e.streams[0]; };
    pcRef.current = pc;
  };

  const startCall = async () => {
    const stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
    localVideoRef.current.srcObject = stream;
    if (!pcRef.current) createPeerConnection();
    stream.getTracks().forEach(track => pcRef.current.addTrack(track, stream));
    const offer = await pcRef.current.createOffer();
    await pcRef.current.setLocalDescription(offer);
    socket.emit('offer', offer);
    setInCall(true);
  };

  return (
    <div>
      <video ref={localVideoRef} autoPlay playsInline muted />
      <video ref={remoteVideoRef} autoPlay playsInline />
      <button onClick={startCall} disabled={inCall}>Start Call</button>
    </div>
  );
}

export default VideoCall;

This component assumes the other peer is already listening for the offer. In a real app, you’d have a call setup flow (e.g., a room ID) and handle the case where the remote peer is not yet connected.

Common Problems and Solutions

  • No video/audio: Ensure you request permissions via getUserMedia and handle the promise rejection. Also check that the remote stream is attached to the video element after ontrack fires.
  • Connection fails behind NAT: Without a TURN server, peers behind symmetric NATs cannot connect. Add a TURN server (e.g., from a service like Twilio or Open Relay) to your iceServers list.
  • Signaling messages lost: If the signaling server doesn’t guarantee delivery or reordering, ICE candidates may arrive before the remote description is set. Buffer candidates until the remote description is set.
  • State management: In React, avoid storing media streams in state; use refs to avoid re-renders that disrupt the connection.
  • Cleanup: When the component unmounts, close the peer connection and stop the local stream tracks to free the camera.

STUN and TURN: What You Need to Know

STUN (Session Traversal Utilities for NAT) servers help peers discover their public IP address and port. They are free and often sufficient for most home networks. TURN (Traversal Using Relays around NAT) servers relay media when a direct P2P connection is impossible (e.g., strict firewalls). TURN is expensive because it consumes server bandwidth, so use it only as a fallback. For production, you’ll need a TURN server; free public TURN servers exist but are not reliable for heavy use.

Alternatives and When to Use Them

If you need group calls (more than 2 participants), WebRTC’s mesh topology becomes inefficient—each peer sends to every other. For groups, consider using a Selective Forwarding Unit (SFU) like Mediasoup or a managed service such as Daily, Twilio, or Agora. These handle SFU and TURN for you, but you lose the pure P2P simplicity. For a simple two-browser call, pure WebRTC is the most lightweight and cost-effective solution.

Frequently Asked Questions

Do I need a server for WebRTC?

Yes, you need a signaling server to exchange SDP and ICE candidates. However, the media streams flow directly between browsers, so the server doesn’t handle the heavy data. For testing, you can use a simple WebSocket server or even a public signaling service.

Can I use WebRTC without a TURN server?

Yes, for most connections, a STUN server is enough. TURN is only needed when a direct connection fails due to NAT or firewall restrictions. For a demo, STUN is usually sufficient; for production, include TURN as a fallback.

How do I handle multiple users?

For more than two participants, you’d need to create a peer connection for each other user (mesh) or use an SFU. The mesh approach is simple but doesn’t scale beyond 4-6 users. For larger calls, use an SFU or a managed WebRTC platform.

Is WebRTC secure?

WebRTC encrypts all media and data channels using DTLS and SRTP, so the content is secure. However, the signaling channel must also be secure (e.g., using WSS) to prevent interception of the SDP.

Can I record the video call?

Yes, you can use the MediaRecorder API to record the local stream or the remote stream. Note that recording the remote stream may require the remote peer’s consent, and you must handle privacy considerations.

How do I test WebRTC locally?

You can test on the same machine by opening two browser tabs and using a local signaling server. However, the camera may be shared between tabs, so use a fake device or test with different users on different machines.

Related

Method: the standard formula for this calculation, rounded as each result states. See how the calculators work or report an error.