How to Build Adapter.js WebRTC App with JavaScript?

Learn how to create cross-browser compatible WebRTC applications using adapter.js. This guide covers setup, implementation, and best practices for seamless real-time communication.

Introduction to adapter.js WebRTC

WebRTC (Web Real-Time Communication) is a powerful technology enabling peer-to-peer communication directly from web browsers. However, one of the challenges in using WebRTC is ensuring consistent behavior across different browsers due to varying implementations of the WebRTC API. This is where adapter.js comes into play. Adapter.js is a shim library designed to standardize WebRTC functionality across all browsers, ensuring a seamless development experience.

What is Adapter.js WebRTC Technology?

Adapter.js abstracts away the differences between browsers, providing a consistent API and handling browser-specific quirks. This makes it an essential tool for developers looking to build robust WebRTC applications without worrying about compatibility issues.
A great resource to understand practical applications of WebRTC, including adapter.js, is the

RecordRTC GitHub repository

. This repository offers a collection of code samples and tools for recording and playing back WebRTC streams, demonstrating the versatility and power of WebRTC enhanced by adapter.js.
By using adapter.js, developers can focus on building feature-rich WebRTC applications while ensuring their code works smoothly across all major browsers.

Getting Started with the Code

Creating a WebRTC application involves several steps, from setting up the project structure to integrating essential libraries like adapter.js. This section will guide you through the initial setup and provide a foundation for building your WebRTC app.

Create a New adapter.js WebRTC App

To start, we need to set up a new project. Open your terminal and create a new directory for your project:

bash

1mkdir adapterjs-webrtc-app
2cd adapterjs-webrtc-app
Next, create the essential files for your project:

bash

1touch index.html main.js

Install

Adapter.js can be included in your project by directly adding the script to your HTML file or by using a package manager like npm. For simplicity, we will include it directly in our HTML.
Add the following line to your index.html file within the <head> tag to include adapter.js:

HTML

1<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
This script ensures that the WebRTC API behaves consistently across different browsers.

Structure of the Project

Your project should have a simple structure to start with:
1adapterjs-webrtc-app/
2├── index.html
3└── main.js
The index.html file will contain the basic HTML structure and the script tags, while main.js will contain the JavaScript code for your WebRTC functionality.

App Architecture

adapter-js-webrtc
A basic WebRTC application with adapter.js involves setting up the HTML interface, initializing the WebRTC API, and handling media streams. The primary components include:
  1. HTML Interface: The user interface for your app, including elements like video tags for displaying media streams.
  2. JavaScript Initialization: Initializing adapter.js and the WebRTC API, and handling user interactions.
  3. Media Stream Handling: Capturing and managing media streams to enable real-time communication.
By following this structure, you'll ensure your app is organized and scalable. In the next sections, we will dive deeper into each component, starting with setting up the initial HTML structure and integrating adapter.js for cross-browser compatibility.

Step 1: Get Started with Initial Setup

To begin building your WebRTC application with adapter.js, you need to set up the initial HTML structure and include the necessary scripts. This step forms the foundation upon which the rest of your application will be built.

Create an HTML File index.html

Create an HTML file named index.html in your project directory. This file will serve as the main entry point for your application. It will include the necessary HTML elements and scripts to initialize adapter.js and the WebRTC API.
Here is an example of a basic index.html setup:

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 App with adapter.js</title>
7    <script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
8    <script src="main.js" defer></script>
9</head>
10<body>
11    <h1>Welcome to WebRTC App</h1>
12    <video id="localVideo" autoplay playsinline></video>
13    <video id="remoteVideo" autoplay playsinline></video>
14</body>
15</html>
In this HTML file:
  • The <head> section includes the adapter.js script, ensuring that all WebRTC functionality will work consistently across different browsers.
  • The <script src="main.js" defer></script> line includes your main JavaScript file, which will contain the core logic for your WebRTC application.
  • The <body> section contains two video elements, localVideo and remoteVideo, which will display the local and remote media streams respectively.
By setting up this basic HTML structure, you prepare your application to handle WebRTC operations with adapter.js, providing a consistent experience across all browsers. In the next step, we'll wireframe all the components in the main.js file to start building out the functionality of your WebRTC app.

Step 2: Wireframe All the Components

With the initial HTML setup in place, the next step is to wireframe the components of your WebRTC application using JavaScript. This involves initializing adapter.js and setting up the basic structure for handling media streams and peer connections.

Set up the basic structure in main.js

Create a JavaScript file named main.js in your project directory. This file will contain the core logic for your WebRTC application.
Here’s an example of how to set up the basic structure in main.js:

JavaScript

1// Variables to hold the local and remote video elements
2const localVideo = document.getElementById('localVideo');
3const remoteVideo = document.getElementById('remoteVideo');
4
5// Variables to hold the local media stream and the peer connection
6let localStream;
7let peerConnection;
8
9// Configuration for the peer connection
10const peerConnectionConfig = {
11    'iceServers': [
12        { 'urls': 'stun:stun.l.google.com:19302' }
13    ]
14};
15
16// Function to initialize the media stream
17async function initLocalStream() {
18    try {
19        localStream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
20        localVideo.srcObject = localStream;
21    } catch (error) {
22        console.error('Error accessing media devices.', error);
23    }
24}
25
26// Function to initialize the peer connection
27function initPeerConnection() {
28    peerConnection = new RTCPeerConnection(peerConnectionConfig);
29
30    // Add local stream tracks to the peer connection
31    localStream.getTracks().forEach(track => peerConnection.addTrack(track, localStream));
32
33    // Set up event handlers for the peer connection
34    peerConnection.onicecandidate = handleICECandidateEvent;
35    peerConnection.ontrack = handleTrackEvent;
36}
37
38// Handler for ICE candidate events
39function handleICECandidateEvent(event) {
40    if (event.candidate) {
41        console.log('New ICE candidate:', event.candidate);
42        // Send the candidate to the remote peer
43    }
44}
45
46// Handler for track events
47function handleTrackEvent(event) {
48    remoteVideo.srcObject = event.streams[0];
49}
50
51// Initialize the local stream and peer connection
52initLocalStream().then(initPeerConnection);
In this JavaScript file:
  • Variables: Local and remote video elements are selected, and variables for the local media stream and peer connection are defined.
  • Configuration: The peerConnectionConfig object contains the ICE servers configuration.
  • initLocalStream Function: This function accesses the user's media devices to capture the video and audio streams and sets the local video element's source to this stream.
  • initPeerConnection Function: This function initializes the RTCPeerConnection, adds the local stream tracks to the peer connection, and sets up event handlers for ICE candidates and track events.
  • Event Handlers: The handleICECandidateEvent and handleTrackEvent functions handle ICE candidates and remote media streams, respectively.
By setting up this basic structure, you have wired all the essential components to start building your WebRTC application. In the next step, we will implement the join screen to allow users to initiate or join a WebRTC session.

Step 3: Implement Join Screen

The join screen is a crucial component of your WebRTC application. It allows users to initiate or join a WebRTC session, making the user experience seamless and intuitive. This step involves designing the join screen and integrating adapter.js to ensure cross-browser compatibility.

Add Simple Join Screen Interface

First, update your index.html file to include a simple join screen interface. Add the following HTML code inside the <body> tag:

HTML

1<body>
2    <h1>Welcome to WebRTC App</h1>
3    <div id="joinScreen">
4        <button id="startButton">Start Session</button>
5        <button id="joinButton">Join Session</button>
6    </div>
7    <div id="videoContainer" style="display: none;">
8        <video id="localVideo" autoplay playsinline></video>
9        <video id="remoteVideo" autoplay playsinline></video>
10    </div>
11</body>
In this updated HTML:
  • Two buttons, "Start Session" and "Join Session," are added within a div with the ID joinScreen.
  • The video elements are now enclosed in a div with the ID videoContainer, initially hidden with display: none.

Handle the Join Screen

Next, update your main.js to handle the join screen functionality. Add the following code:

JavaScript

1// Variables for join screen elements
2const startButton = document.getElementById('startButton');
3const joinButton = document.getElementById('joinButton');
4const joinScreen = document.getElementById('joinScreen');
5const videoContainer = document.getElementById('videoContainer');
6
7// Event listeners for the buttons
8startButton.addEventListener('click', startSession);
9joinButton.addEventListener('click', joinSession);
10
11// Function to start a new session
12function startSession() {
13    joinScreen.style.display = 'none';
14    videoContainer.style.display = 'block';
15    initLocalStream().then(initPeerConnection);
16}
17
18// Function to join an existing session
19function joinSession() {
20    joinScreen.style.display = 'none';
21    videoContainer.style.display = 'block';
22    initLocalStream().then(initPeerConnection);
23}
24
25// Initialize the local stream and peer connection (existing code)
26async function initLocalStream() {
27    try {
28        localStream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
29        localVideo.srcObject = localStream;
30    } catch (error) {
31        console.error('Error accessing media devices.', error);
32    }
33}
34
35function initPeerConnection() {
36    peerConnection = new RTCPeerConnection(peerConnectionConfig);
37    localStream.getTracks().forEach(track => peerConnection.addTrack(track, localStream));
38    peerConnection.onicecandidate = handleICECandidateEvent;
39    peerConnection.ontrack = handleTrackEvent;
40}
41
42function handleICECandidateEvent(event) {
43    if (event.candidate) {
44        console.log('New ICE candidate:', event.candidate);
45    }
46}
47
48function handleTrackEvent(event) {
49    remoteVideo.srcObject = event.streams[0];
50}
51
52// Call functions to initialize the application (existing code)
53initLocalStream().then(initPeerConnection);
In this JavaScript code:
  • Event Listeners: Event listeners are added to the "Start Session" and "Join Session" buttons to handle user interactions.
  • startSession and joinSession Functions: These functions hide the join screen and display the video container. They also initialize the local stream and peer connection.
  • Display Logic: The join screen is initially displayed, and the video container is shown only after a session is started or joined.
By implementing the join screen, you provide users with an interface to start or join a WebRTC session easily. This ensures a smooth user experience while maintaining cross-browser compatibility using adapter.js. In the next step, we will implement the controls for the WebRTC application.

Step 4: Implement Controls

Adding controls to your WebRTC application enhances user interaction by allowing them to manage their media streams effectively. In this step, we will implement basic controls such as muting the microphone and turning off the camera.

Add Control Buttons

First, update your index.html file to include control buttons. Add the following HTML code inside the videoContainer div:

HTML

1<div id="videoContainer" style="display: none;">
2    <video id="localVideo" autoplay playsinline></video>
3    <video id="remoteVideo" autoplay playsinline></video>
4    <div id="controls">
5        <button id="muteButton">Mute</button>
6        <button id="cameraButton">Turn Off Camera</button>
7    </div>
8</div>
In this updated HTML: A div with the ID controls contains two buttons: "Mute" and "Turn Off Camera".

Handle the Control Button

Next, update your main.js to handle the control buttons. Add the following code:

JavaScript

1// Variables for control buttons
2const muteButton = document.getElementById('muteButton');
3const cameraButton = document.getElementById('cameraButton');
4
5// Event listeners for the control buttons
6muteButton.addEventListener('click', toggleMute);
7cameraButton.addEventListener('click', toggleCamera);
8
9// Function to toggle mute
10function toggleMute() {
11    localStream.getAudioTracks().forEach(track => {
12        track.enabled = !track.enabled;
13        muteButton.textContent = track.enabled ? 'Mute' : 'Unmute';
14    });
15}
16
17// Function to toggle camera
18function toggleCamera() {
19    localStream.getVideoTracks().forEach(track => {
20        track.enabled = !track.enabled;
21        cameraButton.textContent = track.enabled ? 'Turn Off Camera' : 'Turn On Camera';
22    });
23}
24
25// Variables for join screen elements (existing code)
26const startButton = document.getElementById('startButton');
27const joinButton = document.getElementById('joinButton');
28const joinScreen = document.getElementById('joinScreen');
29const videoContainer = document.getElementById('videoContainer');
30
31// Event listeners for the buttons (existing code)
32startButton.addEventListener('click', startSession);
33joinButton.addEventListener('click', joinSession);
34
35// Function to start a new session (existing code)
36function startSession() {
37    joinScreen.style.display = 'none';
38    videoContainer.style.display = 'block';
39    initLocalStream().then(initPeerConnection);
40}
41
42// Function to join an existing session (existing code)
43function joinSession() {
44    joinScreen.style.display = 'none';
45    videoContainer.style.display = 'block';
46    initLocalStream().then(initPeerConnection);
47}
48
49// Initialize the local stream and peer connection (existing code)
50async function initLocalStream() {
51    try {
52        localStream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
53        localVideo.srcObject = localStream;
54    } catch (error) {
55        console.error('Error accessing media devices.', error);
56    }
57}
58
59function initPeerConnection() {
60    peerConnection = new RTCPeerConnection(peerConnectionConfig);
61    localStream.getTracks().forEach(track => peerConnection.addTrack(track, localStream));
62    peerConnection.onicecandidate = handleICECandidateEvent;
63    peerConnection.ontrack = handleTrackEvent;
64}
65
66function handleICECandidateEvent(event) {
67    if (event.candidate) {
68        console.log('New ICE candidate:', event.candidate);
69    }
70}
71
72function handleTrackEvent(event) {
73    remoteVideo.srcObject = event.streams[0];
74}
75
76// Call functions to initialize the application (existing code)
77initLocalStream().then(initPeerConnection);
In this JavaScript code:
  • Event Listeners: Event listeners are added to the "Mute" and "Turn Off Camera" buttons to handle user interactions.
  • toggleMute Function: This function toggles the audio track's enabled state and updates the button text accordingly.
  • toggleCamera Function: This function toggles the video track's enabled state and updates the button text accordingly.
By implementing these basic controls, you enhance the user experience by providing them with the ability to manage their media streams effectively. This step ensures that users have greater control over their WebRTC sessions while maintaining cross-browser compatibility using adapter.js. In the next step, we will implement the participant view to display other users in the WebRTC session.

Get Free 10,000 Minutes Every Months

No credit card required to start.

Step 5: Implement Participant View

Displaying participants in your WebRTC application is crucial for enabling effective communication. In this step, we will implement the participant view to show remote video streams alongside the local video.

Includes the Necessary Video Elements

First, ensure your index.html file includes the necessary video elements. You should already have the local and remote video elements set up:

HTML

1<div id="videoContainer" style="display: none;">
2    <video id="localVideo" autoplay playsinline></video>
3    <video id="remoteVideo" autoplay playsinline></video>
4    <div id="controls">
5        <button id="muteButton">Mute</button>
6        <button id="cameraButton">Turn Off Camera</button>
7    </div>
8</div>

Handle the Addition of Remote Video Stream

Next, update your main.js to handle the addition of remote video streams. Add the following code:

JavaScript

1// Variables for video elements
2const localVideo = document.getElementById('localVideo');
3const remoteVideo = document.getElementById('remoteVideo');
4
5// Variables for join screen elements (existing code)
6const startButton = document.getElementById('startButton');
7const joinButton = document.getElementById('joinButton');
8const joinScreen = document.getElementById('joinScreen');
9const videoContainer = document.getElementById('videoContainer');
10
11// Variables for control buttons (existing code)
12const muteButton = document.getElementById('muteButton');
13const cameraButton = document.getElementById('cameraButton');
14
15// Event listeners for the control buttons (existing code)
16muteButton.addEventListener('click', toggleMute);
17cameraButton.addEventListener('click', toggleCamera);
18
19// Event listeners for the join screen buttons (existing code)
20startButton.addEventListener('click', startSession);
21joinButton.addEventListener('click', joinSession);
22
23// Function to start a new session (existing code)
24function startSession() {
25    joinScreen.style.display = 'none';
26    videoContainer.style.display = 'block';
27    initLocalStream().then(initPeerConnection);
28}
29
30// Function to join an existing session (existing code)
31function joinSession() {
32    joinScreen.style.display = 'none';
33    videoContainer.style.display = 'block';
34    initLocalStream().then(initPeerConnection);
35}
36
37// Function to toggle mute (existing code)
38function toggleMute() {
39    localStream.getAudioTracks().forEach(track => {
40        track.enabled = !track.enabled;
41        muteButton.textContent = track.enabled ? 'Mute' : 'Unmute';
42    });
43}
44
45// Function to toggle camera (existing code)
46function toggleCamera() {
47    localStream.getVideoTracks().forEach(track => {
48        track.enabled = !track.enabled;
49        cameraButton.textContent = track.enabled ? 'Turn Off Camera' : 'Turn On Camera';
50    });
51}
52
53// Initialize the local stream and peer connection (existing code)
54async function initLocalStream() {
55    try {
56        localStream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
57        localVideo.srcObject = localStream;
58    } catch (error) {
59        console.error('Error accessing media devices.', error);
60    }
61}
62
63function initPeerConnection() {
64    peerConnection = new RTCPeerConnection(peerConnectionConfig);
65    localStream.getTracks().forEach(track => peerConnection.addTrack(track, localStream));
66    peerConnection.onicecandidate = handleICECandidateEvent;
67    peerConnection.ontrack = handleTrackEvent;
68}
69
70// Handler for ICE candidate events (existing code)
71function handleICECandidateEvent(event) {
72    if (event.candidate) {
73        console.log('New ICE candidate:', event.candidate);
74        // Send the candidate to the remote peer
75    }
76}
77
78// Handler for track events (existing code)
79function handleTrackEvent(event) {
80    remoteVideo.srcObject = event.streams[0];
81}
82
83// Call functions to initialize the application (existing code)
84initLocalStream().then(initPeerConnection);
In this JavaScript code:

Remote Video Handling

The handleTrackEvent function assigns the remote stream to the remoteVideo element, ensuring that the remote participant's video is displayed.
By implementing the participant view, you enable your WebRTC application to show both local and remote video streams, facilitating effective real-time communication. This step ensures that users can see each other during the session, enhancing the overall user experience. In the next step, we will finalize the application by running the code and testing its functionality.

Step 6: Run Your Code Now

With all the components in place, it's time to run your WebRTC application and test its functionality. This step involves setting up a simple server to serve your files and testing the application in a browser.

Setting Up a Local Server

To run your application, you need a local server. You can use a simple HTTP server using Node.js or Python.
Using Python:

bash

1# For Python 3
2python -m http.server 8000
Using Node.js:

bash

1npm install -g http-server
2http-server -p 8000

Testing Your Application

  1. Open your browser and navigate to http://localhost:8000.
  2. Click "Start Session" or "Join Session" to initiate a WebRTC session.
  3. Verify that your local and remote video streams are working correctly.
By running your application on a local server, you can test its functionality and ensure that all components, including adapter.js, are working seamlessly across different browsers. This final step ensures that your WebRTC application is ready for deployment.

Conclusion

In this article, we have walked through the process of building a WebRTC application using adapter.js. We covered the initial setup, implementing a join screen, adding controls, and displaying participant views. By following these steps, you can create a robust WebRTC application that works consistently across different browsers.

Want to level-up your learning? Subscribe now

Subscribe to our newsletter for more tech based insights

FAQ