Skip to content

WebNinjaDeveloper.com

Programming Tutorials




Menu
  • Home
  • Youtube Channel
  • Official Blog
  • Nearby Places Finder
  • Direction Route Finder
  • Distance & Time Calculator
Menu

Build a Angular 14 Socket.IO Zoom Clone WebRTC Video Calling Conferencing App in Node.JS & Express Using Javascript

Posted on January 25, 2023

 

 

Welcome folks today in this blog post we will be building the zoom clone in socket.io using angular at the frontend and we will be having the express server running at the backend. All the full source code of the application is shown below.

 

 

Get Started

 

 

In order to get started you need to make a zoom clone directory and inside it we will be making separate directory for frontend and backend as shown below

 

 

mkdir zoomclone

 

 

cd zoomclone

 

 

 

Making the Backend

 

 

Now we will be making the backend first as shown below

 

 

mkdir backend

 

 

npm init -y

 

 

npm i express

 

 

npm i socket.io

 

 

Now you will see the below directory structure of the express app as shown below

 

 

 

 

Now you need to copy paste the below code inside the index.js file as shown below

 

 

index.js

 

 

JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const express = require('express');
const app = express();
const server = require('http').Server(app);
const io = require('socket.io')(server);
const path = require('path');
app.use('/static', express.static('public'));
app.get('/**', (req, res) => {
    return res.sendfile(path.join(__dirname + '/public/index.html'));
});
io.on('connection', socket => {
    socket.on('join-room', (roomId, userId) => {
        socket.join(roomId);
        socket.to(roomId).broadcast.emit('user-connected', userId);
        socket.on('disconnect', () => {
            socket.to(roomId).broadcast.emit('user-disconnected', userId);
        })
    });
})
server.listen(3000);

 

 

As you can see we are importing the express and socket.io libraries at the top and then we are making the public directory as static and then we are listening on for every socket connection using the connection event and we are listening on for join-room event and inside it we are allowing the user to join the room using the join() method. And we are getting the roomId and then we are broadcasting the message to all sockets present in that roomId that the user is connected. And when the user disconnects we are listening using the disconnect event we are broadcasting the message that the user is disconnected.

 

Now you can start the backend express server using the below command as shown below

 

 

node index.js

 

 

Making the Frontend

 

 

Now guys we will be making the angular frontend. For this we need to make the frontend directory as shown below

 

 

mkdir frontend

 

 

cd frontend

 

 

ng new sampleapp

 

 

cd sampleapp

 

 

And now we will be installing the packages that are needed for this application

 

 

npm i ngx-socket-io

 

 

Now you will see the below directory structure of the angular app as shown below

 

 

 

 

Now we need to make two components for this frontend zoom clone first one is the home page and secondly we need to make the room component as shown below

 

 

ng generate component home

 

 

ng generate component room

 

 

This will create two folders namely home and room as shown above in the figure.

 

 

Now you need to go to app.module.ts file and copy paste the following code

 

 

app.module.ts

 

 

TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { RoomComponent } from './room/room.component';
import { HomeComponent } from './home/home.component';
import {SocketIoModule} from "ngx-socket-io";
@NgModule({
  declarations: [
    AppComponent,
    RoomComponent,
    HomeComponent,
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    BrowserAnimationsModule,
    SocketIoModule.forRoot({
      url: '/'
    })
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

 

 

As you can see we are importing the ngx-socket-io module and also the different components inside the imports array and the declarations array.

 

 

And now we need to go to home.component.html file and copy paste the following code

 

 

home.component.html

 

 

1
2
3
4
5
<h1>Welcome to video chat!</h1>
<div>
  Want to chat?
  <button (click)="createRoom()">Create a new Webrtc video chat room now!</button>
</div>

 

 

As you can see we have a simple button to create the room and join it.

 

And now we need to copy paste the below code inside the home.component.ts file as shown below

 

 

home.component.ts

 

 

TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import { Component, OnInit } from '@angular/core';
import uuidv4 from 'uuid/dist/v4';
import {Router} from "@angular/router";
@Component({
  selector: 'app-home',
  templateUrl: './home.component.html',
  styleUrls: ['./home.component.scss']
})
export class HomeComponent implements OnInit {
  constructor(
    private router: Router,
  ) { }
  ngOnInit() {
  }
  createRoom() {
    console.log('createRoom');
    this.router.navigate([`/${uuidv4()}`]);
  }
}

 

 

As you can see we are redirecting the user to the random id which is actually the roomId. For this we are importing the uuid package at the very top.

 

 

Now guys we will be writing the html code inside the room.component.html file as shown below

 

 

room.component.html

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<div class="video-grid">
  <div *ngFor="let video of videos">
    <span *ngIf="video.userId !== currentUserId; else currentUserVideoLabel">{{ video.userId }}</span>
    <ng-template #currentUserVideoLabel>
      <span>You</span>
    </ng-template>
    <video
      [srcObject]="video.srcObject"
      (loadedmetadata)="onLoadedMetadata($event)"
      [muted]="video.muted"
    >
    </video>
  </div>
</div>

 

 

As you can see inside the div section we are displaying the grid section where we will be rendering the webcam of users which are connecting inside the room. And for this we are using the ngFor directive to loop through all the user’s `webcam and display it inside the browser.

 

 

And now we need to copy paste the code inside the room.component.ts file as shown below

 

 

room.component.ts

 

 

TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
import { Component, OnInit } from '@angular/core';
import {ActivatedRoute} from "@angular/router";
import { Socket } from "ngx-socket-io";
import { v4 as uuidv4 }  from 'uuid';
declare const Peer;
interface VideoElement {
  muted: boolean;
  srcObject: MediaStream;
  userId: string;
}
@Component({
  selector: 'app-room',
  templateUrl: './room.component.html',
  styleUrls: ['./room.component.scss']
})
export class RoomComponent implements OnInit {
  currentUserId:string = uuidv4();
  videos: VideoElement[] = [];
  constructor(
    private route: ActivatedRoute,
    private socket: Socket,
  ) { }
  ngOnInit() {
    console.log(`Initialize Peer with id ${this.currentUserId}`);
    const myPeer = new Peer(this.currentUserId, {
      host: '/',
      port: 3001,
    });
    this.route.params.subscribe((params) => {
      console.log(params);
      myPeer.on('open', userId => {
        this.socket.emit('join-room', params.roomId, userId);
      });
    });
    navigator.mediaDevices.getUserMedia({
      audio: true,
      video: true,
    })
      .catch((err) => {
        console.error('[Error] Not able to retrieve user media:', err);
        return null;
      })
      .then((stream: MediaStream | null) => {
        if (stream) {
          this.addMyVideo(stream);
        }
        myPeer.on('call', (call) => {
          console.log('receiving call...', call);
          call.answer(stream);
          call.on('stream', (otherUserVideoStream: MediaStream) => {
            console.log('receiving other stream', otherUserVideoStream);
            this.addOtherUserVideo(call.metadata.userId, otherUserVideoStream);
          });
          call.on('error', (err) => {
            console.error(err);
          })
        });
        this.socket.on('user-connected', (userId) => {
          console.log('Receiving user-connected event', `Calling ${userId}`);
          // Let some time for new peers to be able to answer
          setTimeout(() => {
            const call = myPeer.call(userId, stream, {
              metadata: { userId: this.currentUserId },
            });
            call.on('stream', (otherUserVideoStream: MediaStream) => {
              console.log('receiving other user stream after his connection');
              this.addOtherUserVideo(userId, otherUserVideoStream);
            });
            call.on('close', () => {
              this.videos = this.videos.filter((video) => video.userId !== userId);
            });
          }, 1000);
        });
      });
    this.socket.on('user-disconnected', (userId) => {
      console.log(`receiving user-disconnected event from ${userId}`)
      this.videos = this.videos.filter(video => video.userId !== userId);
    });
  }
  addMyVideo(stream: MediaStream) {
    this.videos.push({
      muted: true,
      srcObject: stream,
      userId: this.currentUserId,
    });
  }
  addOtherUserVideo(userId: string, stream: MediaStream) {
    const alreadyExisting = this.videos.some(video => video.userId === userId);
    if (alreadyExisting) {
      console.log(this.videos, userId);
      return;
    }
    this.videos.push({
      muted: false,
      srcObject: stream,
      userId,
    });
  }
  onLoadedMetadata(event: Event) {
    (event.target as HTMLVideoElement).play();
  }
}

 

 

As you can see we are using the peerjs library to connect the different clients together to build a simple zoom clone video chat and then we are using the navigator api in the browser to allow the permission to use the webcam of the user. And then we are using the socket.io library in angular to send different events to the backend express server whenever there is a new client connected inside the application and when socket are joining different room, And when the user disconnects from the video chat we are removing it from the grid.

 

 

 

 

And now guys we need to do some routing to connect these two components together. For this we need to go to app-routing.module.ts file and copy paste the below code

 

 

app-routing.module.ts

 

 

TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import {HomeComponent} from "./home/home.component";
import {RoomComponent} from "./room/room.component";
const routes: Routes = [
  {
    path: '',
    component: HomeComponent,
  },
  {
    path: ':roomId',
    component: RoomComponent,
  }
];
@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

 

 

As you can see we are providing the path for each home and room component. And for this we are importing the built in angular routing library.

 

 

And now we need to go to app.component.html file and copy paste this line

 

 

app.component.html

 

 

1
<router-outlet></router-outlet>

 

 

Now you also need to start the peerjs server at port 3001 as shown below

 

 

npm i -g peer

 

 

peerjs --port 3001

 

 

 

 

And now you can start the angular app at port 4200

 

 

ng serve

 

 

 

Recent Posts

  • Android Java Project to Capture Image From Camera & Save it in SharedPreferences & Display it in Grid Gallery
  • Android Java Project to Store,Read & Delete Data Using SharedPreferences Example
  • Android Java Project to Download Multiple Images From URL With Progressbar & Save it inside Gallery
  • Android Java Project to Capture Image From Camera & Save it inside Gallery
  • Android Java Project to Crop,Scale & Rotate Images Selected From Gallery and Save it inside SD Card
  • Angular
  • Bunjs
  • C#
  • Deno
  • django
  • Electronjs
  • java
  • javascript
  • Koajs
  • kotlin
  • Laravel
  • meteorjs
  • Nestjs
  • Nextjs
  • Nodejs
  • PHP
  • Python
  • React
  • ReactNative
  • Svelte
  • Tutorials
  • Vuejs




©2023 WebNinjaDeveloper.com | Design: Newspaperly WordPress Theme