End of Life for Twilio Programmable Video - Upgrade to VideoSDKLearn More

How to Build WebRTC Video Streaming App with JavaScript?

Learn how to create a WebRTC video streaming app from scratch with our step-by-step guide. Explore setup, implementation, advanced features to build a robust real-time communication application.

What is WebRTC Video Streaming?

WebRTC, or Web Real-Time Communication, is a powerful technology that enables real-time audio, video, and data sharing between web browsers and devices. This open-source project, originally developed by Google, has revolutionized the way we think about video streaming applications, making it possible to create seamless, peer-to-peer communication experiences directly within web browsers without the need for plugins or third-party software.
The rise of WebRTC has coincided with the growing demand for real-time communication tools. From video conferencing apps and live streaming platforms to interactive online courses and telehealth services, WebRTC's capabilities are being harnessed across various industries. Its ability to deliver high-quality video and audio, combined with low latency, makes it an ideal choice for developers looking to build robust and scalable video streaming applications.

How WebRTC Video Streaming Works?

At its core, WebRTC is designed to enable peer-to-peer communication. This is achieved through three main components:
  • MediaStream: This API handles the acquisition of audio and video streams from the user's device. It allows developers to capture media from cameras, microphones, or even screen sharing.
  • RTCPeerConnection: This API is responsible for establishing the actual peer-to-peer connection. It handles the negotiation of network traversal (NAT and firewall), codec selection, and media transfer between peers.
  • RTCDataChannel: This API allows for the transfer of arbitrary data between peers, enabling features like file sharing, text chat, and gaming data synchronization.
WebRTC operates by creating a direct connection between users, thus reducing latency and improving the quality of the communication.
This is achieved through a series of steps:
  • Signaling: Before a peer-to-peer connection can be established, peers need to exchange information about how to connect. This involves sharing network details, capabilities, and other metadata. Signaling can be implemented using various methods like WebSockets, XMLHttpRequest, or any other messaging mechanism.
  • Connection Establishment: Using the signaling information, WebRTC sets up an RTCPeerConnection. This involves the exchange of offer and answer messages, along with the negotiation of ICE (Interactive Connectivity Establishment) candidates to traverse NAT and firewalls.
  • Media and Data Transfer: Once the connection is established, media streams and data channels are used to transmit audio, video, and other data between peers.

Setting Up Your Development Environment

To start building a WebRTC video streaming app, you'll need to set up your development environment. Here’s a step-by-step guide to get you started:

Step 1: Install Node.js and npm:

Node.js and its package manager, npm, are essential for running local servers and managing dependencies. You can download and install them from the

official Node.js website

.

Step 2: Choose a Code Editor:

A good code editor will make development easier. Popular choices include Visual Studio Code, Sublime Text, and Atom.

Step 3: Create a New Project:

Open your terminal and create a new project directory. Navigate to this directory and initialize a new npm project:

JavaScript

1mkdir webrtc-video-app
2cd webrtc-video-app
3npm init -y

Step 4: Install Required Libraries:

For this project, you’ll need some libraries to handle signaling and server setup. Install the following packages:

bash

1    npm install express socket.io

Step 5: Set Up a Basic Server:

Create a simple Express server in a file named server.js:

JavaScript

1    const express = require('express');
2    const http = require('http');
3    const socketIo = require('socket.io');
4
5    const app = express();
6    const server = http.createServer(app);
7    const io = socketIo(server);
8
9    app.use(express.static('public'));
10
11    io.on('connection', (socket) => {
12        console.log('a user connected');
13        socket.on('disconnect', () => {
14            console.log('user disconnected');
15        });
16    });
17
18    server.listen(3000, () => {
19        console.log('Server is running on port 3000');
20    });

Step 6: Create Public Directory:

This directory will contain your HTML, CSS, and client-side JavaScript files. Create a public directory and add an index.html file for your basic webpage structure.
With your development environment set up, you’re ready to start building your WebRTC video streaming application.

Creating a Basic WebRTC Application

Now that your development environment is ready, let's create a basic WebRTC application. We'll start by setting up the initial project structure and then build the HTML framework.

Step 1: Project Setup:

Ensure you have your project directory structured as follows:
1    webrtc-video-app/
2    ├── public/
3    │   └── index.html
4    └── server.js

Step 2: HTML Structure:

In public/index.html, create a basic HTML page:

HTML

1    <!DOCTYPE html>
2    <html lang="en">
3    <head>
4        <meta charset="UTF-8">
5        <meta name="viewport" content="width=device-width, initial-scale=1.0">
6        <title>WebRTC Video Streaming App</title>
7    </head>
8    <body>
9        <h1>WebRTC Video Streaming App</h1>
10        <video id="localVideo" autoplay playsinline></video>
11        <video id="remoteVideo" autoplay playsinline></video>
12        <script src="/socket.io/socket.io.js"></script>
13        <script src="app.js"></script>
14    </body>
15    </html>

Step 3: Including WebRTC Scripts:

Add the WebRTC scripts and initialize the connection logic. Create a new file public/app.js and include the following:

JavaScript

1    const localVideo = document.getElementById('localVideo');
2    const remoteVideo = document.getElementById('remoteVideo');
3
4    const socket = io();
5
6    let localStream;
7    let peerConnection;
8
9    const configuration = {
10        iceServers: [
11            { urls: 'stun:stun.l.google.com:19302' }
12        ]
13    };
14
15    async function startVideo() {
16        try {
17            localStream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
18            localVideo.srcObject = localStream;
19        } catch (error) {
20            console.error('Error accessing media devices.', error);
21        }
22    }
23
24    startVideo();
This setup provides a foundation for capturing and displaying video streams.

Capturing and Displaying Video

To capture and display video using WebRTC, we'll utilize the getUserMedia() API to access the user's camera and microphone. Here’s how to do it:

[a] Capturing Video:

Update public/app.js to handle media capture:

JavaScript

1    async function startVideo() {
2        try {
3            localStream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
4            localVideo.srcObject = localStream;
5        } catch (error) {
6            console.error('Error accessing media devices.', error);
7        }
8    }
9
10    startVideo();

[b] Displaying Video:

The getUserMedia() API captures the media stream, which is then assigned to the srcObject of the video element to display it on the page.
With this setup, you now have a basic video streaming app that captures and displays the local video stream.

Establishing a Peer Connection

To enable communication between peers, you need to establish a peer connection using the RTCPeerConnection API. This involves creating and exchanging offers and answers, as well as handling ICE candidates.

[a] Setting Up RTCPeerConnection:

JavaScript

1    const peerConnection = new RTCPeerConnection(configuration);
2
3    localStream.getTracks().forEach(track => {
4        peerConnection.addTrack(track, localStream);
5    });
6
7    peerConnection.ontrack = (event) => {
8        remoteVideo.srcObject = event.streams[0];
9    };
10
11    peerConnection.onicecandidate = (event) => {
12        if (event.candidate) {
13            socket.emit('ice-candidate', event.candidate);
14        }
15    };

[b] Creating and Exchanging Offers and Answers:

JavaScript

1    async function createOffer() {
2        const offer = await peerConnection.createOffer();
3        await peerConnection.setLocalDescription(offer);
4        socket.emit('offer', offer);
5    }
6
7    socket.on('offer', async (offer) => {
8        await peerConnection.setRemoteDescription(new RTCSessionDescription(offer));
9        const answer = await peerConnection.createAnswer();
10        await peerConnection.setLocalDescription(answer);
11        socket.emit('answer', answer);
12    });
13
14    socket.on('answer', async (answer) => {
15        await peerConnection.setRemoteDescription(new RTCSessionDescription(answer));
16    });

[c] Handling ICE Candidates:

JavaScript

1    socket.on('ice-candidate', async (candidate) => {
2        try {
3            await peerConnection.addIceCandidate(new RTCIceCandidate(candidate));
4        } catch (e) {
5            console.error('Error adding received ICE candidate', e);
6        }
7    });
With these code snippets, you’ve established a peer connection, enabling video communication between two peers. This basic setup can be expanded to include multiple peers and additional features, providing a robust foundation for your WebRTC video streaming app.

Get Free 10,000 Minutes Every Months

No credit card required to start

Advanced Features and Use Cases

Implementing Signaling Server

The signaling server is a crucial component of any WebRTC application. It facilitates the exchange of signaling data (like session descriptions and ICE candidates) between peers. While WebRTC handles peer-to-peer connections directly, the initial setup requires an intermediary to negotiate and establish these connections.
To implement a signaling server, we'll use Node.js and Socket.io. Here’s how to set it up:

[a] Server Setup

In server.js, update your server to handle signaling messages:

JavaScript

1    const express = require('express');
2    const http = require('http');
3    const socketIo = require('socket.io');
4
5    const app = express();
6    const server = http.createServer(app);
7    const io = socketIo(server);
8
9    app.use(express.static('public'));
10
11    io.on('connection', (socket) => {
12        console.log('a user connected');
13
14        socket.on('offer', (offer) => {
15            socket.broadcast.emit('offer', offer);
16        });
17
18        socket.on('answer', (answer) => {
19            socket.broadcast.emit('answer', answer);
20        });
21
22        socket.on('ice-candidate', (candidate) => {
23            socket.broadcast.emit('ice-candidate', candidate);
24        });
25
26        socket.on('disconnect', () => {
27            console.log('user disconnected');
28        });
29    });
30
31    server.listen(3000, () => {
32        console.log('Server is running on port 3000');
33    });

[b] Client-side Signaling

Update public/app.js to handle signaling messages from the server:

JavaScript

1    socket.on('offer', async (offer) => {
2        if (!peerConnection) {
3            await createPeerConnection();
4        }
5        await peerConnection.setRemoteDescription(new RTCSessionDescription(offer));
6        const answer = await peerConnection.createAnswer();
7        await peerConnection.setLocalDescription(answer);
8        socket.emit('answer', answer);
9    });
10
11    socket.on('answer', async (answer) => {
12        await peerConnection.setRemoteDescription(new RTCSessionDescription(answer));
13    });
14
15    socket.on('ice-candidate', async (candidate) => {
16        try {
17            await peerConnection.addIceCandidate(new RTCIceCandidate(candidate));
18        } catch (e) {
19            console.error('Error adding received ICE candidate', e);
20        }
21    });
22
23    async function createOffer() {
24        const offer = await peerConnection.createOffer();
25        await peerConnection.setLocalDescription(offer);
26        socket.emit('offer', offer);
27    }
By setting up the signaling server, your WebRTC application can now establish peer-to-peer connections between clients.

Enhancing Video Streaming Quality

Optimizing video quality is essential for a good user experience. Here are some tips and techniques:

[a] Adaptive Bitrate Streaming:

Implement adaptive bitrate streaming to adjust the video quality based on network conditions.

JavaScript

1    // Example of managing bandwidth
2    const constraints = {
3        video: {
4            width: { ideal: 1280 },
5            height: { ideal: 720 },
6            frameRate: { ideal: 30, max: 60 }
7        }
8    };
9
10    async function startVideo() {
11        try {
12            localStream = await navigator.mediaDevices.getUserMedia(constraints);
13            localVideo.srcObject = localStream;
14            localStream.getTracks().forEach(track => {
15                peerConnection.addTrack(track, localStream);
16            });
17        } catch (error) {
18            console.error('Error accessing media devices.', error);
19        }
20    }

[b] Bandwidth Management:

Monitor and manage bandwidth to ensure optimal video quality.

JavaScript

1    // Adjust bandwidth settings
2    const sender = peerConnection.getSenders().find(s => s.track.kind === 'video');
3    const parameters = sender.getParameters();
4    parameters.encodings[0].maxBitrate = 2500000; // 2.5 Mbps
5    sender.setParameters(parameters);

[c] Error Handling and Reconnection:

Implement error handling and reconnection strategies to maintain a stable connection.

JavaScript

1    peerConnection.oniceconnectionstatechange = () => {
2        if (peerConnection.iceConnectionState === 'disconnected') {
3            console.log('Peer disconnected. Attempting to reconnect...');
4            // Reconnect logic here
5        }
6    };

Security Considerations

Securing your WebRTC application is critical to protect user data and privacy. Here are some best practices:

[a] Secure Signaling:

Use HTTPS and WSS (WebSocket Secure) for signaling to prevent eavesdropping and man-in-the-middle attacks.

JavaScript

1    const server = https.createServer(credentials, app);
2    const io = socketIo(server, { secure: true });

[b] Encryption:

WebRTC uses SRTP (Secure Real-time Transport Protocol) to encrypt media streams. Ensure that your application uses SRTP by default.

[c] Handling Permissions:

Properly handle user permissions for accessing media devices.

JavaScript

1    async function startVideo() {
2        try {
3            const stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
4            // Handle stream
5        } catch (error) {
6            console.error('Permission denied or error accessing media devices.', error);
7        }
8    }
By following these security practices, you can ensure that your WebRTC application remains secure and reliable.

In-depth Use Cases and Applications

Building a Multi-user Video Conference

To create a multi-user video conference, you need to manage multiple peer connections. Here’s how to extend your current setup:

[a] Managing Multiple Connections:

Keep track of all peers and their connections.

JavaScript

1    const peers = {};
2
3    socket.on('offer', (offer, id) => {
4        const peerConnection = new RTCPeerConnection(configuration);
5        peers[id] = peerConnection;
6
7        // Handle offer and create answer
8    });
9
10    socket.on('answer', (answer, id) => {
11        peers[id].setRemoteDescription(new RTCSessionDescription(answer));
12    });
13
14    socket.on('ice-candidate', (candidate, id) => {
15        peers[id].addIceCandidate(new RTCIceCandidate(candidate));
16    });
17
18    function createOffer(id) {
19        const peerConnection = new RTCPeerConnection(configuration);
20        peers[id] = peerConnection;
21        // Create and send offer
22    }

[b] Handling Multiple Video Streams:

Display multiple video streams in the UI.

HTML

1<div id="videos">
2     <video id="localVideo" autoplay playsinline></video>
3     <div id="remoteVideos"></div>
4</div>

JavaScript

1    peerConnection.ontrack = (event) => {
2        const remoteVideo = document.createElement('video');
3        remoteVideo.srcObject = event.streams[0];
4        remoteVideo.autoplay = true;
5        document.getElementById('remoteVideos').appendChild(remoteVideo);
6    };
By managing multiple connections and streams, you can build a fully functional multi-user video conference application.

Integrating WebRTC with Other Technologies

WebRTC can be integrated with other real-time communication technologies to enhance functionality.

[a] Combining WebRTC with WebSockets:

Use WebSockets for signaling and additional real-time data communication.

JavaScript

1    const socket = io();
2
3    socket.on('message', (data) => {
4        // Handle real-time data
5    });
6
7    function sendMessage(data) {
8        socket.emit('message', data);
9    }

[b] Using WebRTC with WebRTC-DataChannel:

Create a data channel for additional communication.

JavaScript

1    const dataChannel = peerConnection.createDataChannel('chat');
2
3    dataChannel.onmessage = (event) => {
4        console.log('Received message:', event.data);
5    };
6
7    function sendMessage(message) {
8        dataChannel.send(message);
9    }
By integrating WebRTC with other technologies, you can create a more versatile and powerful communication platform.

Want to level-up your learning? Subscribe now

Subscribe to our newsletter for more tech based insights

FAQ