DEV Community

Cover image for Building a Video Chat App with Socket.io, PeerJS, Codesphere
Lior Ben-David for Codesphere Inc.

Posted on • Edited on

16 8

Building a Video Chat App with Socket.io, PeerJS, Codesphere

The Coronavirus pandemic has made video chat the primary method of communication that we rely on. Whether it’s for school, work, or just talking to friends and family, we end up video chatting almost every day in the age of Coronavirus. Contrary to popular belief, building your own video chat app is not too difficult.

In this tutorial, I’ll be outlining the structure of building a basic chat app with the following technologies:

  • Node.js
  • ExpressJS
  • EJS
  • Socket.io
  • WebRTC(w/ PeerJS)
  • Codesphere

If you’d like to follow along line by line, you can find the source code here:

https://github.com/LiorB-D/VideoChat

You can also try out a demo for yourself here!
Once the app loads in, you can run it with
npm ci && node server.js

Explanation of Different Technologies We’ll Be Using in this Project

Part of the reason I loved building this project is that it allows us to dip our toes into a number of useful technologies.

Node.js

Node.js is a runtime environment that allows us to run JavaScript code independent of a web browser. It is essential to practically any web application that involves more than basic JavaScript integration.

If you haven’t worked with it before, you can download it here:

https://nodejs.org/en/

Since we are building our app in Codesphere, Node.js is already installed.

ExpressJS

ExpressJS is a server framework for Node.js. It is the go-to framework that is used in building web applications and APIs and is pretty intuitive to use. We are going to be using Express to run our video chat server.

EJS

When you first learn web development, you learn how to embed JavaScript within an HTML page. For websites that need limited back-end logic, this is a super convenient way to develop the app. More frequently, however, we see developers using frameworks, like React and Angular, that generate the HTML pages from JavaScript. This allows developers to have a lot more freedom when building web applications since they are not limited by the skeleton of an HTML file.

EJS on the other hand is a simple framework that will allow us to render and serve HTML files from our Express server. This is where our videos will be shown.

UUID

When a user first opens our chat app, we want to create a room for them with a unique identifier.

UUID is a protocol for generating a random unique identifier. There is a UUID package that can generate it automatically for us.

Socket.io

Socket.io is a relatively easy-to-use JavaScript library for creating real-time communication apps. While it has both a client-side and a server-side library, we are going to be using the server-side library to manage communication between users.

WebRTC and PeerJS

WebRTC (Real-Time Communication) is the underlying technology in browsers and mobile apps that allows for real-time communication.

Since working with WebRTC directly can be a bit complex, we are going to be using PeerJS which allows us to use WebRTC in an incredibly easy way.

PeerJS also provides a free cloud service that we will be using to host our rooms.

Codesphere

Lastly, Codesphere is an online collaborative programming environment and cloud provider. With Codesphere you can build your web app ready to scale without all the cumbersome config. In this project, Codesphere is going to allow us to create and deploy our Video Chat app seamlessly.

https://codesphere.com

Installations

First, we need to set up the base of our Node project with
npm init -y

Next, install all our necessary packages
npm i express ejs socket.io uuid peer

In addition to these packages, we are going to install a tool called Nodemon. Nodemon will automatically update our deployed code whenever we save, so
npm i --save-dev nodemon

Finally, in our package.json file, we are going to add a script to start our server.



"scripts": {
    "watch": "nodemon server.js"
 },


Enter fullscreen mode Exit fullscreen mode

Creating our Server

We are going to create a file called server.js with the following code:

//Create our express and socket.io servers
const express = require('express')
const app = express()
const server = require('http').Server(app)
const io = require('socket.io')(server)
const {v4: uuidV4} = require('uuid')
app.set('view engine', 'ejs') // Tell Express we are using EJS
app.use(express.static('public')) // Tell express to pull the client script from the public folder
// If they join the base link, generate a random UUID and send them to a new room with said UUID
app.get('/', (req, res) => {
res.redirect(`/${uuidV4()}`)
})
// If they join a specific room, then render that room
app.get('/:room', (req, res) => {
res.render('room', {roomId: req.params.room})
})
// When someone connects to the server
io.on('connection', socket => {
// When someone attempts to join the room
socket.on('join-room', (roomId, userId) => {
socket.join(roomId) // Join the room
socket.broadcast.emit('user-connected', userId) // Tell everyone else in the room that we joined
// Communicate the disconnection
socket.on('disconnect', () => {
socket.broadcast.emit('user-disconnected', userId)
})
})
})
server.listen(3000) // Run the server on the 3000 port

Our server.js file is going to run our video chat server. From it, we are going to redirect the user to a room and show them the ‘room’ webpage.

Building our EJS Page

Next, we are going to make a views directory and create an EJS file in it, called room.ejs

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- Set the ROOM_ID variable to the roomId that is passed in by server.js -->
<script>
const ROOM_ID = "<%= roomId %>"
</script>
<!-- CDN in the peerJS and socket.io libraries -->
<script defer src="https://unpkg.com/peerjs@1.3.1/dist/peerjs.min.js"></script>
<script src = "/socket.io/socket.io.js" defer></script>
<!-- Load in script.js, our client-side script -->
<script src = "script.js" defer></script>
<title>Video App</title>
<!-- Make a grid of 300x300 videos -->
<style>
#video-grid {
display: grid;
grid-template-columns: repeat(auto-fill, 300px);
grid-auto-rows: 300px;
}
video {
width: 100%;
height: 100%;
object-fit: cover;
}
</style>
</head>
<body>
<!-- Make our video grid -->
<div id="video-grid"></div>
</body>
</html>
view raw room.ejs hosted with ❤ by GitHub

This is a template that will be used to render the HTML page for the user. This HTML page is going to use the script.js file to connect to the server.

Our Client-Side Script

Finally, we are going to create a directory called public and within it create a script.js file:

const socket = io('/') // Create our socket
const videoGrid = document.getElementById('video-grid') // Find the Video-Grid element
const myPeer = new Peer() // Creating a peer element which represents the current user
const myVideo = document.createElement('video') // Create a new video tag to show our video
myVideo.muted = true // Mute ourselves on our end so there is no feedback loop
// Access the user's video and audio
navigator.mediaDevices.getUserMedia({
video: true,
audio: true
}).then(stream => {
addVideoStream(myVideo, stream) // Display our video to ourselves
myPeer.on('call', call => { // When we join someone's room we will receive a call from them
call.answer(stream) // Stream them our video/audio
const video = document.createElement('video') // Create a video tag for them
call.on('stream', userVideoStream => { // When we recieve their stream
addVideoStream(video, userVideoStream) // Display their video to ourselves
})
})
socket.on('user-connected', userId => { // If a new user connect
connectToNewUser(userId, stream)
})
})
myPeer.on('open', id => { // When we first open the app, have us join a room
socket.emit('join-room', ROOM_ID, id)
})
function connectToNewUser(userId, stream) { // This runs when someone joins our room
const call = myPeer.call(userId, stream) // Call the user who just joined
// Add their video
const video = document.createElement('video')
call.on('stream', userVideoStream => {
addVideoStream(video, userVideoStream)
})
// If they leave, remove their video
call.on('close', () => {
video.remove()
})
}
function addVideoStream(video, stream) {
video.srcObject = stream
video.addEventListener('loadedmetadata', () => { // Play the video as it loads
video.play()
})
videoGrid.append(video) // Append video element to videoGrid
}
view raw script.js hosted with ❤ by GitHub

This is the client-side code that handles interacting with the server and other users.

Deploying in Codesphere

Deploying in Codesphere is as easy as running
npm run devStart
In terminal

We can then access the deployed web application like so:

Alt Text

Next Steps

And there we go! With this setup, we’ve made a very basic video chat app that you can use to communicate with friends!

Obviously, before making a commercial video chat app, you are going to want to implement a number of more complex features, including:

  • Privacy/Encryption
  • User Authentication
  • An appealing UI
  • Group calls
  • Using your own servers, instead of relying on PeerJS’s free cloud service

Top comments (3)

Collapse
 
eliasgroll profile image
Elias Groll

Awesome :) I love it!

Collapse
 
naonvl profile image
naonvl

how to end the call?

Collapse
 
liorbd profile image
Lior Ben-David

Hi Naonvl,
To disconnect from the PeerJS Server, and then end the call, you can set up a button in the ejs file that triggers the following command:
myPeer.disconnect()
You can read more about handling disconnects in the peerJS documentation here:
peerjs.com/docs.html#peerdisconnect

Best,
Lior