We have seen a major increase in the usage of virtual meetings in the past year and the existing platforms cannot cater to everyone's custom needs. Also, building a custom feature-rich solution for your need from scratch is not a great option as you need to manage a lot of tasks, this is where VideoSDK.live comes to rescue.
With video SDK you can build a customized Video Chat App with all features your need. We will see in this guide, how you can build a video chat app using Javascript.
Prerequisites
- Node.js v12+
- NPM v6+ (comes pre-installed with newer Node versions)
- You should have a video SDK account to generate token. Visit video SDK dashboard to generate token.
Project Structure
root
├── index.html
├── assets
│ ├── css
│ │ ├── index.css
│ ├── js
│ ├── index.js
Implementation
Step 1: Adding VideoSDK
Update the index.html
file with the <script ... />
as shown to add the Javascript SDK to your project.
<html> | |
<head> | |
.... | |
</head> | |
<body> | |
..... | |
<script src="https://sdk.videosdk.live/js-sdk/0.0.20/videosdk.js"></script> | |
</body> | |
</html> |
If you don't want to use <script ... />
you can use npm
to install video SDK in your project.
npm install @videosdk.live/js-sdk
//or you can use yarn
yarn add @videosdk.live/js-sdk
Step 2: Creating the UI
For the interface, we will have simple Join and Create Meeting meeting buttons which will join and create a new meeting room respectively.
The meeting room will show the local participant view, remote participant view and show buttons to toggle mic, webcam, and leave the meeting.
<html> | |
<head> | |
<!--favicon--> | |
<link | |
rel="shortcut icon" | |
href="https://videosdk.live/favicon/favicon.ico" | |
/> | |
<meta charset="UTF-8" /> | |
<link rel="stylesheet" href="./assets/css/index.css" /> | |
<!--add necessary bootstrap links here --> | |
... | |
</head> | |
<body class="bg-secondary"> | |
<!--join-screen--> | |
<div | |
id="join-screen" | |
class="flex flex-row align-items-center justify-content-center h-100" > | |
<button | |
class="btn btn-primary" | |
id="btnCreateMeeting" | |
onclick="meetingHandler(true)" > | |
New Meeting | |
</button> | |
<input | |
type="text" | |
id="txtMeetingCode" | |
placeholder="Enter Meeting Code .." /> | |
<button | |
id="btnJoinMeeting" | |
onclick="meetingHandler(false)" | |
class="btn btn-primary" > | |
Join | |
</button> | |
</div> | |
<!--grid-screen--> | |
<div id="grid-screen"> | |
<div> | |
<input | |
type="text" | |
class="form-control navbar-brand" | |
id="lblMeetingId" | |
readonly | |
/> | |
<button class="btn btn-dark" id="btnToggleMic">Unmute Mic</button> | |
<button class="btn btn-dark" id="btnToggleWebCam">Disable Webcam</button> | |
<button class="btn btn-dark" id="btnLeaveMeeting">Leave Meeting</button> | |
</div> | |
<br /> | |
<div id="videoContainer"></div> | |
<div | |
style="position: absolute; | |
top: 10px; | |
right: 0px; | |
height: 50%; | |
overflow-y: scroll;" > | |
<h3>Participants List</h3> | |
<div id="participantsList"></div> | |
</div> | |
</div> | |
<!--scripts--> | |
<script src="./assets/js/config.js"></script> | |
<script src="./assets/js/index.js"></script> | |
<script src="https://sdk.videosdk.live/js-sdk/0.0.20/videosdk.js"></script> | |
</body> | |
</html> |
You can get the complete custom CSS style from here.
We will declare all the DOM variables we will need in the index.js
file.
//DOM elements | |
let btnCreateMeeting = document.getElementById("btnCreateMeeting"); | |
let btnJoinMeeting = document.getElementById("btnJoinMeeting"); | |
let videoContainer = document.getElementById("videoContainer"); | |
let btnLeaveMeeting = document.getElementById("btnLeaveMeeting"); | |
let btnToggleMic = document.getElementById("btnToggleMic"); | |
let btnToggleWebCam = document.getElementById("btnToggleWebCam"); |
Step 3: Meeting Implementation
To start the meeting implementation, we will need the token so if you don't have one, you can generate it from here.
-
Now update your token in the
index.js
file as shown to add the token in the script and add a validator.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters//variables let token = "YOUR_TOKEN_HERE"; //handlers async function tokenGeneration() { //Update this function with a server-side token generation for Production version if (token != "") { console.log("token : ", token); } else { alert("Please Provide Your Token First"); } } -
We have got the token. Now we will add the meetingHandler which will create or join to a meeting room.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters//variables let meetingId = ""; async function meetingHandler(newMeeting) { let joinMeetingName = "JS-SDK"; tokenGeneration(); if (newMeeting) { const url = `${API_BASE_URL}/api/meetings`; const options = { method: "POST", headers: { Authorization: token, "Content-Type": "application/json" }, }; const { meetingId } = await fetch(url, options) .then((response) => response.json()) .catch((error) => alert("error", error)); document.getElementById("lblMeetingId").value = meetingId; document.getElementById("home-screen").style.display = "none"; document.getElementById("grid-screen").style.display = "inline-block"; startMeeting(token, meetingId, joinMeetingName); } else { document.getElementById("lblMeetingId").value = meetingId; document.getElementById("home-screen").style.display = "none"; document.getElementById("grid-screen").style.display = "inline-block"; startMeeting(token, meetingId, joinMeetingName); } } -
Now the meetingId is either generated or updated with the value user entered. After this, startMeeting is triggered which is responsible to initialize the meeting with the required configuration and join the meeting.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersfunction startMeeting(token, meetingId, name) { // Meeting config window.ZujoSDK.config(token); // Meeting Init meeting = window.ZujoSDK.initMeeting({ meetingId: meetingId, // required name: name, // required micEnabled: true, // optional, default: true webcamEnabled: true, // optional, default: true maxResolution: "hd", // optional, default: "hd" }); //join meeting meeting.join(); } -
Now we will create the local participant view.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersfunction startMeeting(token, meetingId, name) { //...Meeting intializating and joining code is here //create Local Participant createParticipant(meeting.localParticipant.id); //local participant stream-enabled event meeting.localParticipant.on("stream-enabled", (stream) => { setTrack( stream, localParticipant, localParticipantAudio, meeting.localParticipant.id ); }); } //createParticipant function createParticipant(participant) { //create videoElem of participant let participantVideo = createVideoElement( participant.id, participant.displayName ); //create audioEle of participant let participantAudio = createAudioElement(participant.id); //append video and audio of participant to videoContainer div videoContainer.appendChild(participantVideo); videoContainer.appendChild(participantAudio); } // creating video element function createVideoElement(id, name) { let videoFrame = document.createElement("div"); videoFrame.classList.add("video-frame"); //create video let videoElement = document.createElement("video"); videoElement.classList.add("video"); videoElement.setAttribute("id", `v-${id}`); videoElement.setAttribute("autoplay", true); videoFrame.appendChild(videoElement); //add overlay let overlay = document.createElement("div"); overlay.classList.add("overlay"); overlay.innerHTML = `Name : ${name}`; videoFrame.appendChild(overlay); return videoFrame; } // creating audio element function createAudioElement(pId) { let audioElement = document.createElement("audio"); audioElement.setAttribute("autoPlay", false); audioElement.setAttribute("playsInline", "false"); audioElement.setAttribute("controls", "false"); audioElement.setAttribute("id", `a-${pId}`); audioElement.style.display = "none"; return audioElement; } //set the video or audio stream in the video element function setTrack(stream, videoElem, audioElement, id) { if (stream.kind == "video") { // enablePermission(id); const mediaStream = new MediaStream(); mediaStream.addTrack(stream.track); videoElem.srcObject = mediaStream; videoElem .play() .catch((error) => console.error("videoElem.current.play() failed", error) ); } if (stream.kind == "audio") { if (id == meeting.localParticipant.id) return; const mediaStream = new MediaStream(); mediaStream.addTrack(stream.track); audioElement.srcObject = mediaStream; audioElement .play() .catch((error) => console.error("audioElem.play() failed", error)); } } -
To show the remote participants, we will add the event listeners on meeting which will notify us when a participant joins or leaves the meeting.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersfunction startMeeting(token, meetingId, name) { //... Meeting intialization and joining //... creating the local participant //participant joined meeting.on("participant-joined", (participant) => { //create viewe for participant who joined createParticipant(participant); //listen for the stream change participant.on("stream-enabled", (stream) => { setTrack( stream, document.querySelector(`#v-${participant.id}`), document.getElementById(`a-${participant.id}`), participant.id ); }); }); // participants left meeting.on("participant-left", (participant) => { let vElement = document.querySelector(`#v-${participant.id}`); vElement.parentNode.removeChild(vElement); let aElement = document.getElementById(`a-${participant.id}`); aElement.parentNode.removeChild(aElement); participants = new Map(meeting.participants); //remove it from participant list participantId; document.getElementById(`p-${participant.id}`).remove(); }); } -
At last, we will add the event listeners to the toggle buttons and leave button.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersfunction startMeeting(token, meetingId, name) { //... Meeting intialization and joining //...creating local participants //...remote participant listeners addDomEvents(); } function addDomEvents() { btnToggleMic.addEventListener("click", () => { if (btnToggleMic.innerText == "Unmute Mic") { meeting.unmuteMic(); btnToggleMic.innerText = "Mute Mic"; } else { meeting.muteMic(); btnToggleMic.innerText = "Unmute Mic"; } }); btnToggleWebCam.addEventListener("click", () => { if (btnToggleWebCam.innerText == "Disable Webcam") { meeting.disableWebcam(); btnToggleWebCam.innerText = "Enable Webcam"; } else { meeting.enableWebcam(); btnToggleWebCam.innerText = "Disable Webcam"; } }); btnLeaveMeeting.addEventListener("click", async () => { // leavemeeting meeting.leave(); document.getElementById("join-screen").style.display = "inline-block"; document.getElementById("grid-screen").style.display = "none"; }); }
Run and Test
To run the app you will need live-server
. If you don't have it installed already you can do it using:
npm install -g live-server
Once you have the live-server
installed, just run it using:
live-server
Conclusion
With this, we successfully built the video chat app using the video SDK in Javascript. If you wish to add functionalities like chat messaging, screen sharing, you can always check out our documentation. If you face any difficulty with the implementation you can connect with us on our discord community.
Top comments (2)
I'm guessing that VideoSDK is based on the WebRTC technology?
All these Zoom, Loom, Slack, Squad apps have their video calling tech based on that.
In case anyone needs to test a video calling app, here are some tips.
endtest.io/guides/docs/testing-web...
I was expecting WebRTC tutorial that possibly showed how to connect more them one people.
Some comments may only be visible to logged-in visitors. Sign in to view all comments.