video sdk logo

Login

JavaScript Video Calling Tutorial

Add real-time 1:1 and group video calling to any web page with plain JavaScript, no framework required. You keep direct control over media tracks and room events, while adaptive bitrate, simulcast, and active-speaker detection are handled for you.
JavaScript Video Calling Tutorial

Trusted by 300+ global business & tech leaders

Build a Video Call in Plain JavaScript, Step by Step

Learn how to build a video call in plain JavaScript using the VideoSDK JavaScript SDK. This step-by-step tutorial covers meeting creation, participant joining, stream rendering, and camera, microphone, and screen-share controls, no framework required.


AI prompt
1 Help me add video calling to my plain JavaScript app with VideoSDK.
21. Create an empty folder and run `npm install @videosdk.live/js-sdk`, or load
3   `https://sdk.videosdk.live/js-sdk/1.1.1/videosdk.js` with a script tag.
42. Create `index.html` with a join screen and a video grid, and `config.js` holding
5   `TOKEN`.
63. Create `index.js` that calls `VideoSDK.config(TOKEN)`, creates a room by POSTing
7   to `https://api.videosdk.live/v2/rooms`, then calls `VideoSDK.initMeeting({...})`
8   and `meeting.join()`. Handle `meeting-joined`, `meeting-left`,
9   `participant-joined`, `participant-left`, and each participant's `stream-enabled`
10   to attach video and audio elements. Wire mic and webcam toggles to `muteMic`,
11   `unmuteMic`, `enableWebcam`, `disableWebcam`.
124. Ask me for a token from app.videosdk.live/api-keys and set it as `TOKEN` in
13   `config.js`.
145. Run `live-server --port=8000` and open http://localhost:8000.
15
16REFERENCE
17https://docs.videosdk.live/javascript/guide/video-and-audio-calling-api-sdk/quick-start.md
18

Prerequisites

Before proceeding, ensure that your development environment meets the following requirements:

  • Video SDK Developer Account (Not having one, follow Video SDK Dashboard)
  • Have Node and NPM installed on your device.

Important: One should have a VideoSDK account to generate token. Visit VideoSDK dashboard to generate token

Getting Started with the Code!

Follow the steps to create the environment necessary to add video calls into your app. You can also find the code sample for quickstart here.

First create one empty project using mkdir folder_name on your preferable location.

Install Video SDK

Import VideoSDK using the <script> tag or Install it using the following npm command. Make sure you are in your app directory before you run this command.

1<html>
2  
3  <body>
4
5<script src="https://sdk.videosdk.live/js-sdk/1.1.1/videosdk.js"></script>
6  </body>
7</html>
8

npm:

1npm install @videosdk.live/js-sdk
2

yarn:

1yarn add @videosdk.live/js-sdk
2

Structure of the project

Your project structure should look like this.

Project Structure
1  root
2   ├── index.html
3   ├── config.js
4   ├── index.js
5

You will be working on the following files:

  • index.html: Responsible for creating a basic UI.
  • config.js: Responsible for storing the token.
  • index.js: Responsible for rendering the meeting view and the join meeting functionality.

Step 1: Design the user interface (UI)

Create an HTML file containing the screens, join-screen and grid-screen.

index.html
1<!DOCTYPE html>
2<html>
3  <head> </head>
4
5  <body>
6    <div id="join-screen">
7      <!-- Create new Meeting Button -->
8      <button id="createMeetingBtn">New Meeting</button>
9      OR
10      <!-- Join existing Meeting -->
11      <input type="text" id="meetingIdTxt" placeholder="Enter Meeting id" />
12      <button id="joinBtn">Join Meeting</button>
13    </div>
14
15    <!-- for Managing meeting status -->
16    <div id="textDiv"></div>
17
18    <div id="grid-screen" style="display: none">
19      <!-- To Display MeetingId -->
20      <h3 id="meetingIdHeading"></h3>
21
22      <!-- Controllers -->
23      <button id="leaveBtn">Leave</button>
24      <button id="toggleMicBtn">Toggle Mic</button>
25      <button id="toggleWebCamBtn">Toggle WebCam</button>
26
27      <!-- render Video -->
28      <div class="row" id="videoContainer"></div>
29    </div>
30
31    <!-- Add VideoSDK script -->
32    <script src="https://sdk.videosdk.live/js-sdk/1.1.1/videosdk.js"></script>
33    <script src="config.js"></script>
34    <script src="index.js"></script>
35  </body>
36</html>
37

Output

Step 2: Implement Join Screen

Configure the token in the config.js file, which you can obtain from the VideoSDK Dashbord.

config.js
1// Auth token will be used to generate a meeting and connect to it
2TOKEN = "Your_Token_Here";
3

Next, retrieve all the elements from the DOM and declare the following variables in the index.js file, along with a small showJoinScreen() helper that returns the user to the join screen with a status message whenever creating or joining a meeting fails. Then, add an event listener to the join and create meeting buttons.

index.js
1// Getting Elements from DOM
2const joinButton = document.getElementById("joinBtn");
3const leaveButton = document.getElementById("leaveBtn");
4const toggleMicButton = document.getElementById("toggleMicBtn");
5const toggleWebCamButton = document.getElementById("toggleWebCamBtn");
6const createButton = document.getElementById("createMeetingBtn");
7const videoContainer = document.getElementById("videoContainer");
8const textDiv = document.getElementById("textDiv");
9
10// Declare Variables
11let meeting = null;
12let meetingId = "";
13let isMicOn = false;
14let isWebCamOn = false;
15
16function showJoinScreen(message) {
17  document.getElementById("join-screen").style.display = "block";
18  document.getElementById("grid-screen").style.display = "none";
19  textDiv.textContent = message ?? "";
20}
21
22async function initializeMeeting() {}
23
24function createLocalParticipant() {}
25
26function createVideoElement() {}
27
28function createAudioElement() {}
29
30function setTrack() {}
31
32// Join Meeting Button Event Listener
33joinButton.addEventListener("click", async () => {
34  document.getElementById("join-screen").style.display = "none";
35  textDiv.textContent = "Joining the meeting...";
36
37  roomId = document.getElementById("meetingIdTxt").value;
38  meetingId = roomId;
39
40  await initializeMeeting();
41});
42
43// Create Meeting Button Event Listener
44createButton.addEventListener("click", async () => {
45  document.getElementById("join-screen").style.display = "none";
46  textDiv.textContent = "Please wait, we are joining the meeting";
47
48  // API call to create meeting
49  const url = `https://api.videosdk.live/v2/rooms`;
50  const options = {
51    method: "POST",
52    headers: { Authorization: TOKEN, "Content-Type": "application/json" },
53  };
54
55  try {
56    const response = await fetch(url, options);
57    if (!response.ok) {
58      throw new Error(`Failed to create meeting: ${response.status}`);
59    }
60    const { roomId } = await response.json();
61    meetingId = roomId;
62    await initializeMeeting();
63  } catch (error) {
64    console.error("Failed to create meeting", error);
65    showJoinScreen(
66      "Unable to create the meeting. Check your token and try again."
67    );
68  }
69});
70

Step 3: Initialize meeting

Following that, initialize the meeting using the initMeeting() function and proceed to join the meeting.

From v1.0.0, all these methods are asynchronous and return a Promise, so await them and handle failures with try / catch. For details, see the Migration Guide.

index.js
1// Initialize meeting
2async function initializeMeeting() {
3  try {
4    // VideoSDK.config and initMeeting are synchronous in 1.x — no await.
5    window.VideoSDK.config(TOKEN);
6
7    meeting = window.VideoSDK.initMeeting({
8      meetingId: meetingId, // required
9      name: "Thomas Edison", // required
10      micEnabled: true, // optional, default: true
11      webcamEnabled: true, // optional, default: true
12    });
13
14    // Creating local participant
15    createLocalParticipant();
16
17    // Register all event handlers before joining so no event is missed.
18    // Setting local participant stream
19    meeting.localParticipant.on("stream-enabled", (stream) => {
20      setTrack(stream, null, meeting.localParticipant, true);
21    });
22
23    // meeting joined event
24    meeting.on("meeting-joined", () => {
25      textDiv.textContent = null;
26
27      document.getElementById("grid-screen").style.display = "block";
28      document.getElementById(
29        "meetingIdHeading"
30      ).textContent = `Meeting Id: ${meetingId}`;
31    });
32
33    // meeting left event
34    meeting.on("meeting-left", () => {
35      videoContainer.innerHTML = "";
36    });
37
38    // Remote participants Event
39    // participant joined
40    meeting.on("participant-joined", (participant) => {
41      //  ...
42    });
43
44    // participant left
45    meeting.on("participant-left", (participant) => {
46      //  ...
47    });
48
49    // `join()` resolves when the join request is accepted — wait for the
50    // `meeting-joined` event before calling other meeting methods.
51    await meeting.join();
52  } catch (error) {
53    console.error("Failed to initialize meeting", error);
54    videoContainer.innerHTML = "";
55    showJoinScreen("Unable to join the meeting. Please try again.");
56  }
57}
58

Note await meeting.join() resolves when the join request is accepted — not when the meeting is fully joined. Wait for the meeting-joined event before calling other meeting methods.

Output

</center>

Step 4: Create the Media Elements

In this step, Create a function to generate audio and video elements for displaying both local and remote participants. Set the corresponding media track based on whether it's a video or audio stream.

1// creating video element
2function createVideoElement(pId, name) {
3  let videoFrame = document.createElement("div");
4  videoFrame.setAttribute("id", `f-${pId}`);
5
6  //create video
7  let videoElement = document.createElement("video");
8  videoElement.classList.add("video-frame");
9  videoElement.setAttribute("id", `v-${pId}`);
10  videoElement.setAttribute("playsinline", true);
11  videoElement.setAttribute("width", "300");
12  videoFrame.appendChild(videoElement);
13
14  let displayName = document.createElement("div");
15  displayName.innerHTML = `Name : ${name}`;
16
17  videoFrame.appendChild(displayName);
18  return videoFrame;
19}
20
21// creating audio element
22function createAudioElement(pId) {
23  let audioElement = document.createElement("audio");
24  audioElement.setAttribute("autoPlay", "false");
25  audioElement.setAttribute("playsInline", "true");
26  audioElement.setAttribute("controls", "false");
27  audioElement.setAttribute("id", `a-${pId}`);
28  audioElement.style.display = "none";
29  return audioElement;
30}
31
32// creating local participant
33function createLocalParticipant() {
34  let localParticipant = createVideoElement(
35    meeting.localParticipant.id,
36    meeting.localParticipant.displayName
37  );
38  videoContainer.appendChild(localParticipant);
39}
40
41// setting media track
42function setTrack(stream, audioElement, participant, isLocal) {
43  if (stream.kind == "video") {
44    isWebCamOn = true;
45    const mediaStream = new MediaStream();
46    mediaStream.addTrack(stream.track);
47    let videoElm = document.getElementById(`v-${participant.id}`);
48    videoElm.srcObject = mediaStream;
49    videoElm
50      .play()
51      .catch((error) =>
52        console.error("videoElem.current.play() failed", error)
53      );
54  }
55  if (stream.kind == "audio") {
56    if (isLocal) {
57      isMicOn = true;
58    } else {
59      const mediaStream = new MediaStream();
60      mediaStream.addTrack(stream.track);
61      audioElement.srcObject = mediaStream;
62      audioElement
63        .play()
64        .catch((error) => console.error("audioElem.play() failed", error));
65    }
66  }
67}
68

Step 5: Handle participant events

Thereafter, implement the events related to the participants and the stream. These handlers replace the placeholders in initializeMeeting() and, like the other handlers, are registered before await meeting.join().

Following are the events to be executed in this step:

  1. participant-joined: When a remote participant joins, this event will trigger. In the event callback, create video and audio elements previously defined for rendering their video and audio streams.

  2. participant-left: When a remote participant leaves, this event will trigger. In the event callback, remove the corresponding video and audio elements.

  3. stream-enabled: This event manages the media track of a specific participant by associating it with the appropriate video or audio element.

index.js
1// Initialize meeting
2async function initializeMeeting() {
3  try {
4    // ...
5
6    // participant joined
7    meeting.on("participant-joined", (participant) => {
8      let videoElement = createVideoElement(
9        participant.id,
10        participant.displayName
11      );
12      let audioElement = createAudioElement(participant.id);
13      // stream-enabled
14      participant.on("stream-enabled", (stream) => {
15        setTrack(stream, audioElement, participant, false);
16      });
17      videoContainer.appendChild(videoElement);
18      videoContainer.appendChild(audioElement);
19    });
20
21    // participants left
22    meeting.on("participant-left", (participant) => {
23      let vElement = document.getElementById(`f-${participant.id}`);
24      vElement.remove(vElement);
25
26      let aElement = document.getElementById(`a-${participant.id}`);
27      aElement.remove(aElement);
28    });
29
30    await meeting.join();
31  } catch (error) {
32    // ...
33  }
34}
35

Output

Step 6: Implement Controls

Next, implement the meeting controls such as toggleMic, toggleWebcam and leave meeting.

index.js
1// leave Meeting Button Event Listener
2leaveButton.addEventListener("click", async () => {
3  try {
4    await meeting?.leave();
5  } catch (error) {
6    console.error("Failed to leave meeting", error);
7  }
8  document.getElementById("grid-screen").style.display = "none";
9  document.getElementById("join-screen").style.display = "block";
10});
11
12// Toggle Mic Button Event Listener
13toggleMicButton.addEventListener("click", async () => {
14  try {
15    if (isMicOn) {
16      // Disable Mic in Meeting
17      await meeting?.muteMic();
18    } else {
19      // Enable Mic in Meeting
20      await meeting?.unmuteMic();
21    }
22    isMicOn = !isMicOn;
23  } catch (error) {
24    console.error("Failed to toggle mic", error);
25  }
26});
27
28// Toggle Web Cam Button Event Listener
29toggleWebCamButton.addEventListener("click", async () => {
30  try {
31    if (isWebCamOn) {
32      await meeting.disableWebcam();
33    } else {
34      await meeting.enableWebcam();
35    }
36  } catch (error) {
37    console.error("Failed to toggle webcam", error);
38    return;
39  }
40
41  isWebCamOn = !isWebCamOn;
42  const vElement = document.getElementById(`f-${meeting.localParticipant.id}`);
43  if (vElement) {
44    vElement.style.display = isWebCamOn ? "inline" : "none";
45  }
46});
47

Run your code

Once you have completed all the steps mentioned above, run your application using the code block below.

1live-server --port=8000
2

Final Output

You have completed the implementation of a customized video calling app in Javascript using VideoSDK. To explore more features, go through Basic and Advanced features.

You can checkout the complete quick start example here.

Summarize with

ChatGPTClaudePerplexityGoogleGrok
Prefer VideoSDK on Google

Table of Contents

Start Building Now With $20 Free Balance

No credit card required to start.

Documentation →Github Repo →

Share This Article

TwitterLinkedInFacebookHacker News

Get started for free today

Grab your API keys and start building with free $20 included in your account. Need enterprise-grade security or custom workloads? Talk to an expert!

Book a demo

Video SDK Logo

United States

28 Geary St, Suite 650,
San Francisco, CA 94108, United States

India flag

India

18th Floor, 1812, The Junomoneta Tower,
Adajan-Hazira Rd, Surat, Gujarat 395009, India

Video SDK Logo
Video SDK Logo
Video SDK Logo
Video SDK Logo
SOLUTIONS

Video KYC

Video Banking

Virtual Claim

Video MER

Telehealth

Astrology

Gaming

Dating

Live Commerce

Auto Proctoring

Interview-as-a-service

Virtual Events

Live Audio Streaming

Ed-Tech

PRODUCTS

AI-Agents

Real-time Audio & Video SDK

Interactive Live Streaming SDK

Real-time Transcription SDK

Character SDK

Open Source Examples

SUCCESS STORIES

Examedi

Coderschool

TYHO

ForagerOne

Immigo

DEVELOPERS

Documentation

Code Samples

Developer Updates

Developer Hub

TOP ARTICLES

What is WebRTC?

Build a React Native Video Calling App

Build a Flutter Video Calling App

RESOURCES

The Protocol by Video SDK

AI Apps

Creator Program

LEGAL

Terms Of Service

Privacy Policy

Cookie Notice

CCPA Notice

Subprocessors

DPA

RSS

COMPANY

Contact Us

Pricing

Support

Blog

Press Kit