Transcript for:
WebRTC and Video Calling Applications

[Music] hey what's going on everyone so it's dennis ivey here and i'm filling in for yet another video on brad's channel and in this one i'm going to be teaching you about webrtc while we build out two awesome video calling applications so what i'm going to do here is first give you a high level overview of what webrtc is and how it works and then we're going to move on to our first project to implement everything that we learned so the first project is going to be a simple peer-to-peer video call an application and in this project we're just going to have a lobby where we can go ahead and join a room and invite another user with some kind of room id into our room so in this room you're going to be able to have video calling functionality like muting your mic toggling the camera and that's about it for that application and all in all that project is going to take up about an hour and 10 minutes of this video now in the second part we're going to move on to a group video calling application now this second project is going to take up a little bit more time in the video and that is because of all of the functionality that i added into it i really wanted to make this special so in this project we have a group video called application with the ability to actually have full controls along with the ability to share your screen with that we're also going to be able to see all the room participants and have real-time chat inside of that room so users can actually communicate in real time and one of the cool things i wanted to add in here is that feeling for like a discord clone maybe a twitter spaces application where users can join the application and all their video frames will be in those little circles and if you actually click on one of those circles you can actually put a user into focus and then switch between any user that you want to put into that frame and then you can also go back into group video calling mode where everybody is inside of that frame one thing i wanted to mention is that there should be a link to the live demo down in the video description for project number two so if you want to go ahead and test that out i'd highly recommend checking it out before getting started and just check out all the features and see how things work before we continue so with that being said while project number one does use webrtc project number two will use a third-party service called agora so agora will give us an sdk for the group video calling functionality along with real-time messaging and agora is a paid service but they do have a great free tier that gives us 10 000 free use minutes each month and that should be more than enough to complete this video and have your own demo so test that out and agora actually does not require a credit card on file to get started so anybody should be able to go ahead and get started now if you're only interested in one of the projects in this video i'm gonna make sure there's timestamps down in the video description so you can actually find out where we work on each project and just skip through because project number one and project number two don't have anything to do with each other so you can actually start straight with project number two and continue there so if you're here just for webrtc you can only complete the first part you don't have to worry about the second part or you can just go ahead and start at part two so now that we have that taken care of let's go ahead and run through a couple of slides and we're just gonna go ahead and do an introduction to webrtc and i'll give you that high level overview before we get started with project number one okay so before we get started i just wanted to let you know that these slides right here will be linked to the video description if you want to access them along with a detailed article to go with this so that'll be all linked up you can go ahead and check that out once the video is posted and let's go ahead and get started so first of all what is webrtc so as i have it in the definition here webrtc is a set of javascript apis that allows us to establish a peer-to-peer connection between two browsers to exchange data such as audio and video all in real time so this is real-time communication so the thing that makes webrtc so special is the fact that the connection is between two browsers and the data that transmits between the browsers never actually reaches a server so it doesn't mean that we don't need a server involved at all but as this picture shows right here let's say we have two clients that data is transmitted directly once the connection is made and never has to reach that server so this makes webrtc ideal for exchanging audio and video because any latency that would be added by having to hit the server first would actually cause a slight delay so i know some of you are thinking if it's real-time communication this sounds a lot like web sockets so websockets and webrtc are similar in a lot of aspects so i want to break down what websockets are and how that functions and then we'll make some comparisons on when you might want to use one over the other so websockets the connection is peer to server right here so we have a browser to server connection versus webrtc where the connection is browser to browser so one is peer-to-peer one is peer to server so they both allow us to communicate in real time but with websockets let's say peer one right here wanted to send a chat message to pr2 they would send the message that message would go to the server and then out to peer two now if peer two wants to respond they would send that message to to the server and then out to peer one so there's a little bit of latency with websockets and having to first go to the server but it happened so fast that half a second or maybe a second wouldn't really make much of a difference for something like a chat message or a notification but if we wanted to send audio and video data through websockets that latency would be very noticeable i'm sure we've all been on a call or seen a video where the two people are talking there's a little bit of latency and they're talking over each other so that creates a pretty big issue right there so this is where webrtc thrives so by not having to reach the server first the connection is directly between the two browsers and that video data can be exchanged or audio data can be exchanged much faster now with that webrtc uses udp as its protocol to transport the data and udp is very fast and more on that in a second so if webrtc is so fast why would we need websockets if they both give us real-time communication and one can transfer data faster for us it seems like webrtc is the optimal path here any more speed in our application always seems like a better thing here so there's some limitations to webrtc so let's talk about this and then when you might want to use one over the other so first of all webrtc uses udp and udp is not a reliable protocol for transferring important data so the way udp works is that it sends data really fast but it never validates if the data was received so because of that if we're sending something like an audio or video data if we lose a couple of frames from our video it's no big deal it'll catch up your video might look a little bit funny for a second but everything will turn out and it's not a big issue if we're sending something like a file to someone if we lose a couple of bytes of data from that file that entire file can be corrupted so because of that udp is not reliable you would not want to use it for sending important data now also with webrtc there is no built-in signaling so this means that you can't just use webrtc and make a connection webrtc actually leaves it up to us to make that initial connection and then once a connection is made then webrtc takes over so this is where websockets and webrtc actually go good together we would use a process called signaling with websockets we would send the information between the two peers the connections made and then webrtc takes over so webrtc has its limitations in that way so how does all of this work so i'm going to give you a quick example first and then we'll actually start breaking down the details of this but at a high level overview we have two peers and if they want to communicate we would have one peer initiate a connection and they would say hey you want to chat and they would have to send this message out to peer 2. so they would send out a message it doesn't matter how it's done this can be done through signaling they can send that message through an email a tweet it's irrelevant but that message would contain some information about this peer right here so they would have to send their network information to peer two now if peer two accepts this offer they would go ahead and send some information back to peer one the means of communication right now is not relevant but the second that information is exchanged data can begin to flow and now the two peers do not need a server involved they can start transmitting data so this can be audio and video data and that connection is made so what exactly are we sending between the two peers and how are we sending this data so as i mentioned earlier that means of communication in the beginning is actually irrelevant that could be a tweet or an email but usually this is through a process called signaling that's a more practical way of putting this into an application and that can be through websockets or some kind of third-party signaling service it doesn't really matter so you'd get the two peers in some kind of room so they can at least know about each other then they would transmit the initial data now in that data there are two main components there's a session description protocol an sdp and some ice candidates so first of all both peers will need to exchange a session description protocol one in the form of an offer one in the form of an answer and a session description protocol is simply an object containing some kind of information about both peers usually like the codec address media type audio and video so just a bunch of information about your network and how to connect to you so both peers will need to know about each other's network now along with exchanging session description protocols between the two peers both peers will also exchange a series of ice candidates and an ice candidate is simply a public ip address and port that could be potentially used as an address to receive data so each peer will typically have multiple ice candidates that are gathered by making a series of requests to a stun server and they are exchanged between the two peers so we'll actually get into this exchange process and we'll go more into detail about what's happening so exchanging session description protocols and ice candidates so right here we have two peers right here and we have a server and then we also have stunt servers that we're going to connect to so we'll go through that process again the one that we went through in that high level overview and the first peer will say hey let's connect here's my session description protocol so it'll be sent to the server through a signaling process and sent to peer two now peer two will receive that sdp and they can accept it and they'll say hey sure here's my stp let's connect the second that's exchanged the two peers are connected but data can't start flowing yet they've simply made that connection so before data can start flowing between the two peers we still need to coordinate the discovery of our public ip addresses so that's because most devices nowadays sit behind firewalls and nat devices so to do this we're going to use a method called ice so this means we're going to make a series of requests to a stun server get our ice candidates and then transfer them between the two peers and a stun server is not something that you're going to have to worry about setting up it's really cheap and easy to maintain so there's a lot of publix done servers and in our example we'll use a google stunt server so let's go ahead and continue with this process so what's going to happen here is once the sdp offer and answer are exchanged this peer right here will go ahead and ask the stunt server hey what's my public ip address now the stunt server will reply and they'll send over a series of ice candidates to this pier and this pier will send over the ice candidates to pier 2. now pier 2 will do the same they're going to go ahead and request make a request to the stun server and say hey what's my ip address or public ip stun server will respond and the ice candidates are exchanged now once we find where the network finds an optimal path to communicate through with these ice candidates data can start flowing between the two peers and that entire connection is complete now i wanted to talk about one more thing and that is trickling ice candidates so how you send over ice candidates is up to you but there's a few ways you can go about doing this and let's go ahead and take a look at this process all over again and i'll try to break this down so when we first create our stp offer we also make a series of requests to a stun server to generate our ice candidates now what we could do is create our stp offer wait till all our ice candidates are generated and then send over the stp offer and the ice candidates all together now more traditionally what you want to do is not have to wait for this process to complete you want to send over your stp offer and then trickle in your ice candidates as they're created so you'll notice that we sent over the stp offer and then we don't get back our ice candidates in a batch they're sent over individually so we would signal over each ice candidate over to our peer as they are generated so we would send over the offer send over an ice candidate and another and another and then that process would continue so that is called trickling ice and we're actually going to run through this so it's going to help us fix a little bit of a delay here so let's go ahead and jump to the next slide and i want to show you this in action before we start coding so i have a live demo here so this is actually a gif in that live demo and let's go ahead and click on this and go over how this exchange works between the sdp offer and answer so let's go ahead and open this up make sure my camera is on so the gif is actually at the top of the link right here so here i am this is all live right now source code is all here and what i'm going to do here is open up two tabs side by side so i want to make a connection between two peers okay so we have pier one and pier two so if we go through these instructions what we're gonna do is first create an offer so this is what an offer looks like so i'm going to create an offer and you see this object right here is just a bunch of gibberish about our network we're just going to go ahead and copy the entire offer and i'm going to paste this over to pier 2. so we're creating an offer from pier one peer one will initiate that offer pass it over to peer two and this is the more manual way of doing this you wouldn't actually do this in a real application i'm just trying to or i built the demo so you can see that exchange process so peer two will get this offer and then we're going to create an answer so whenever we added an offer here from pier one let's go ahead and create an answer from pier 2. so now we have an answer and you can see the object one's an offer one's an answer let's go ahead and copy this entire answer and send it to peer two okay so we've exchanged those and once we've exchanged the offer and answer i'm gonna go ahead and add the answer to peer two and now we see a connection both peers are connected so we have peer one and peer two and then pier two and pier one right here okay so one thing you'll notice that there was no ice candidates exchange at this point and that's because we're not using signaling so by the time the ice candidates were generated they were actually added to the offer so i didn't need to actually generate those on my own those were created so i didn't have to actually send those over because we're not using signaling so that's the demo uh check this out before you get started and let's go ahead and get right into coding this okay so let's start with a quick walkthrough of project number one before we start building this out so this is my room right here it's full screen and here are my controls and here is the room name so it's simply a parameter added in the url so at this point i have full control here with my camera and i can toggle this on and off with a mic and i'm just going to go ahead and leave the room and to create a room we can just go ahead and add in any value here let's go ahead and give it a name and we're just going to call this test so this is peer-to-peer i can only have one other person in this room and let's say i want to share this url with someone and i want to tell them to join the room i'll just go ahead and give them the url to the website let's say they try to join the room without an extra parameter here so no room name it's going to redirect them back to the lobby so the room name is test here so they're gonna have to add that in here so we'll just go ahead and do test and here we go they are now in the room so we have two peers in the room and here are my full controls sorry about the quality is going to take a minute to adjust we do actually cover this in the video where we exchange or change up the video size here but we don't go into much detail about it so here we have two peers both peers can see each other so if i go ahead and mute the camera here for pier one the entire camera feed will be hidden here so my current user where i'm at is always going to be in the top left and peer number two so the person i'm talking to will always be in the big frame so that's why right here if this pier has their camera muted we're not going to see anything here so if i unmute this we're actually going to see that video frame so that goes both ways and both piers can actually have full controls here so if i'm inside of this room right here or let's say pier 1 is inside of their application and pier 2 leaves what's going to happen here is my video frames automatically going to go into full screen and pier one leaves so we can join a room and leave it and that's pretty much the application so let's go ahead and close this out and start building this okay so we're going to be building this project out from scratch so the first thing i want to do is go ahead and create an empty folder on my desktop and give it a name and i'll just call my project peer chat so once i have this folder i'll open this up inside of my text editor i'm using vs code here and i'm just going to go ahead and find the project folder right here and open things up so here's my project i'll close this out and the first thing i want to do is go ahead and add in an index.html file that's going to be our room page eventually and then i'll add in a main.css file and a main.js file okay so inside of the index.html file i'm going to set up that boilerplate html code so in vs code if i start typing in html i should get this drop down select menu i'll click this one right here and that should build all of this out for me so if you don't get this just go ahead and type it out from scratch follow along with this video pause it whatever you need to do here so for the css file and the javascript file those are already linked up because i'm using the same name here so the file path is all set so we have our main.js and main.css but what i like to do is i like to get this script tag right here and bring this down below my closing body tag okay so we have this and next i want to go ahead and build out our video frames and the container for those so we'll create a div here and this div is going to have the id of videos so this will hold the two video frames we're doing only peer to peer so it's not going to be group video calling we're only doing a peer-to-peer connection so inside of this we're we're going to use a video tag and inside of this video tag i'm going to go ahead and give it the class of video dash player and then for the id we'll just give it an id of user dash 1. so we're only going to have a user 1 and user 2. now for the other properties here we're just going to go ahead and do auto play so auto play and then we'll do plays in line okay so once we have this tag right here we'll go ahead and copy this bring it down here and we're just going to change this to user two so we have our two users and that looks good so that's all we need inside of our html template right here for now so let's go into our main.css and i just want to make sure that we can see this that we're actually outputting this and then we'll actually start adding in our our camera stream or video feed into these tags here so inside of main.css let's go ahead and grab our videos tag or the video frame the container and then we're just going to go ahead and display it as a grid and for this for the actual columns we'll do grid template columns of one fraction and one fraction so i just want to make sure that both videos take up exactly half of that container so we'll just go one fraction one fraction that's going to make that 50 each for each container now we'll also want to make sure that there's some space between them so we'll do gap of 2em and that's it for the videos so for the actual video containers so for these elements right here for the video player let's go ahead and grab this and we'll style this so we can actually see this so we'll do video dash player and for this we're just going to go ahead and set a background color so we can actually see it so we'll make this black and then for the width we're just going to set this to 100 so i just want to make sure that the width is full width of that container right here so i'll let the actual column specify the width of that video player so we'll set that to 100 and for the height let's just do 300 pixels for now and we'll leave it at that so we'll save this and let's go ahead and take a look at this so i'm going to use live server we'll open up our index.html file give it a second and this is what you should have right here so we should just have those two players and that's it and the next thing i want to do is output my camera's video stream inside of our page here so i want to get access to my camera's audio and video and output it here so for this let's jump back into our code here and let's move on to our main.js file so in here i want to create a variable for my local stream and i'll leave it undefined and then one for my remote stream so local stream will be my local camera's video feed and my microphone's audio and then the remote stream once we're connected to another user that will be that remote users camera and audio data so once we have that let's go ahead and create a function called init and let's make sure it's an async function so with this function we're just going to go ahead and call this right away so this will be the function that starts everything up and when we load our page what's the first thing that we want to happen so typically when you go to a website that takes your camera's audio and video usually it has to request permission and actually get access to that so we're going to call a function that's actually going to ask for permission for access to our cameras video and audio and then we're going to go ahead and actually add this to the dom so for this we're going to call local stream and then we're going to set this value to await navigator.mediadevices.getusermedia so the getusermedia function what this is going to do is this will actually request permission to our camera feed and our audio and we can actually pass in the parameters and what we want access to so for now we'll just do video this will be true so we do want access to the video and for audio we can also set this to true but for now i'm going to set this to false because when we're testing this i don't want to hear an echo back inside of the mic so i just want to make sure that it doesn't mess with us but we'll change this later so we want access to the camera but not the audio so that's what this will do and it will actually set this value inside of local stream so once we have this and i'll actually go over where you can find these functions in a second so let's just finish this up so once we have access to that let's go ahead and call document.getelementbyid and we're just going gonna get user one so this is gonna be our camera's audio and video feed and the video tag has a property called source object and we're just gonna set this to local stream so i'm gonna save this and this specifically is to this video tag so once we have that let's just go ahead and test this out so let's open this up here and i'm going to refresh the page and it looks like i've already accessed or denied the camera's audio and video because i think it refreshed it by default so this is actually a good teaching moment we're going to go ahead and go into settings and we'll go into privacy and security we'll go into site settings and we're just going to go into camera so we're just going to go ahead and remove this so by default it already blocked access we're just going to remove that and then now on that first load this is what you're going to see so that's what that function calls it actually requests access to our camera so once we allow this we should see our camera feed in a second and there we go so now you can see me we've successfully displayed our camera's video feed here so we don't have the audio right now so let's open this up in another tab here so now we have two users but the two users can't see each other so we haven't created that peer-to-peer connection all we did was got access to our camera and displayed it so let's move on to that in the next section here so before we do that though i want to go ahead and show you where you can find these functions because in your projects you're probably not going to be doing the same exact thing that i'm doing eventually so you want to be able to actually find this so i'll show you a great resource for this so let's go to webrtc or let's do a search and in this site right here i'm going to go ahead and link this up in the video description this is where you're going to find access to everything so if we look down here here are all the functions we're going to use this rtc pureconnection function read about it see how to use it and this will tell you a lot more so i'm not going to go into the details about all of these i'm going to expect you to do that research but i just want to make sure you know where to find it okay so let's go ahead and close this out and let's move on to the next section okay so now it's time to start the process of connecting two peers together and creating an offer and then sending that offer over to the other peer along with the ice candidates so let's go ahead and create another function right here just below the init function and let's call this create offer so when we first load up our page and another peer joins we want to create an offer and send it over to them so we'll create the function let's make sure it's a async function here and the first thing we want to do inside of our create offer function is create the initial peer connection here so we're going to create a variable called peer connection and we're going to set this right here inside of create offers so we're going to create a new peer connection and essentially this is that interface that stores all the information between us and that remote peer and it provides us with a bunch of methods to actually connect to that peer so this will be that core interface to actually connect to another user so we're going to go ahead and set this up and we're going to call this new rtc peer connection we'll call the function and there we go so in create offer we're going to establish that peer connection object and then what we're going to do is we're going to get our remote stream and we just want to set up a media stream inside of this video player 2 right here so inside of this other video tag we want to set up the media data or the actual media stream and then we'll add data to it later so we're just going to go ahead and do new media stream we're going to call this and then we're going to do the same thing that we did right here so we're going to take this right here paste this below and set this to user 2 and then take our remote stream and set it up so again there's no actual video feed in there just yet because we haven't gotten that from our user but we're setting it up and just creating it and making sure it's ready for that so once we create our peer connection and set up our media stream let's go ahead and actually create an offer so we're going to go ahead and create a variable called offer and this is going to be a weight peer connection so this is what's given us access to all this all these methods right here we're going to call peer connection dot create offer so each peer connection will have an offer and an answer we're creating that offer and then we're just going to go ahead and go to our peer connection and we're going to set the local description so we are the local description so we're going to peerconnection.set local description and we're throwing in our offer so every peer connection is going to have an offer and answer okay so once we have that let's just go ahead and console this out so we'll do console.log and let's just see our offer i just want to show you what it looks like right now so we'll just do offer and let's just go ahead and call this so at this point i'm just going to call this on init so we're going to call it immediately once we load up our page so if i go in here let's open this up refresh it make sure it's all working let's go into inspect maybe we have something wrong here okay it's all working we see our video feed and here we go we see our offer so we see an object we see the offer here's our string we see the type of offer and there we go so we just successfully created it now i also want to set up my stunt server and as i mentioned earlier we're going to use a free stunt server from google so we'll set that up and then pass that into our peer connection so locally you actually don't need to set this up if we're just doing this in testing but eventually once you're actually using this project in a production environment you'll need to actually set this up here so let's just go ahead and do servers we'll set up the servers and i don't need equals right there so we'll set up the servers and this is simply going to be an object and we're going to start with ice servers let's just do this slowly so do i servers and then this will be an array right here and inside of this array we're just going to go ahead and create an object here and we're going to specify the urls and for this i'm just going to go ahead and copy the actual google stunt servers so you can find a lot of different stunt servers we're just going to go ahead and use these so i'm going to copy this from some notes that i have here and paste this in so the urls here we set up two of them right here and there we go so just copy that information and paste it exactly as is so there's our servers and we just need to go ahead and pass this into our rtc peer connection okay so once we set up our stun servers we still have some more work to do so i'm going to create some space between our offer and this section right here and i just want to get my local tracks and actually add them to the peer connection so i have my local stream right here and i just want to loop through all the audio and video tracks and actually add them to the connection so my remote peer can actually get them so we'll just do local stream and we're just going to go ahead and call get tracks here so get tracks and we're going to loop through every single track so we're going to call four for each here let's see i need to throw each not fro four each and let's just go ahead and loop through every single track so we're getting all the tracks we're gonna use an arrow function here and we're just gonna go into pier connection dot add track so that's a method that that object gives us and we're just going to add the track and then local stream okay so on create offer we're going to add our tracks to that peer connection and the next thing i want to do is listen for when our peer actually as their tracks too so anytime the remote peer as their tracks we want to also add it to the peer connection and then listen for that event so there's going to be an event listener here we're going to call this pure connection and then we'll do on track so when our remote pier as their tracks we're going to go ahead and grab the event here so we'll grab the event set up the arrow function and then here we're just going to go ahead and do event dot streams and then we're grabbing index zero and then we're just gonna go ahead and do get tracks again calling the same thing and we're just gonna do dot for each we're looping through every single track from our remote pier and we're just gonna go ahead and set that to the remote stream okay so we're doing remote stream dot add track okay so remember when we set up our remote stream right here and we pass that into the video container well now we're actually adding the track so any track we add will be added to this object right here to that video tag so that's why we set it up and then we actually add in the tracks okay so we have another thing to do and that is when we create an offer we still need to create ice candidates so anytime we set the local description by default we have an event listener that can fire off here and it's going to go ahead and actually start generating our ice candidates so at this point we're just going to generate them and then console them out and then we'll deal with signaling them over later here so let's go ahead and also go into peer connection and then we'll call dot on ice candidate so that's the event listener every single time we create an ice candidate we're gonna go ahead and create an async function and we're gonna take that event and in here we're first going to check if we actually have an ice candidate so event dot candidate so we want to make sure that the current event is a candidate here and then once we get that candidate let's just go ahead and console out console.log and then we'll just say new ice candidate and then here we'll just do event dot candidate okay so i can't spell candidate let's just copy that bring that in here and then we'll go there we go okay so let's recap here so we create an offer and we set the local description when we set the local description it's going to trigger the ice candidates to start it's going to start making a series of requests to the sun server and it's going to create the ice candidates so set local description we'll fire this off and we'll just have a few candidates created now we also create our where we take our local stream get all the tracks and we add them to our remote pier and then we also want to listen for any time our remote peer adds tracks and we're going to add them to that remote stream so we're just kind of connecting a few things here okay so let's save this again and let's try this let's see the ice candidates so if i refresh this here we're going to see an offer and then we see new ice candidate new ice candidate and new ice candidate so there we go we created the offer and the ice candidates so now we need to take this information and send it over to our remote peer to establish that initial connection so we're going to take this offer right here along with each ice candidate that we generate and we're going to send that data over to that peer now once that remote peer gets this information they're going to create an sdp answer with their information send it back to us and once that exchange takes place the two peers are now connected and data can begin flowing between the two peers now this is usually done through a process called signaling where essentially you would get some users in a room together and then use something like websockets to exchange this data in real time so it all happens seamlessly in the background now instead of building out our own signaling server and using web sockets manually what we're going to do is we're going to use a third-party service called agora that gives us an sdk to make all this possible without having to actually build all this out on our own now agora is a paid service but they do have a great free tier that's going to allow us to use a lot of their service before we actually cross that page threshold so let's go ahead and actually create an account on agora i want you to sign up and we're just going to go ahead and create an app there grab our app id and then make this connection happen now with agora you also don't need a credit card on file so that's one of the cool things so you can actually sign up before you actually need a card for that paid tier so go ahead and actually sign up create your account and once you create an account you're going to see your agora console here and in here what you're going to want to do is actually go into your project section and you're going to want to create a new project so go ahead and create a project here specify the name we'll just call this peer chat and then for the use case we're just going to specify social and then chat room now for the authentication mechanism we're going to select testing mode app id so this way we don't need a token you'll need this in production but for now we just want to use the app id so i'm not going to create my app go ahead and submit this i already have an app that i want to use so i'm just going to go ahead and grab one of these now to actually download the sdk let's go back into our console and we're going to go into downloads and we're just going to grab the web platform so there's different platforms that we can use this for we're just going to go ahead and select web and we're going to grab the real-time messaging sdk so let's go ahead and click on this open this up this will download a zip file and we're going to want to open up this folder right here we'll go into libs and we're just going to grab this one right here so the dot js extension now i'm going to go ahead and minimize all of this bring this into my desktop close this out and we're going to drag this into our peer chat folder so once we have that in our folder let's go ahead and actually make this connection so we want to actually connect the sdk itself and we're going to bring this down here so we're going to copy main.js and then bring that just above the main.js file so we're going to go ahead and click rename i just want to get the name here so make sure it's like that with that js extension now remember you could build out your own signalling server you don't need to use something like agora if you're just looking for the concept here what i'd recommend to do here is just go ahead and actually do it this way use that free tier and then if you want to try using your own signaling server go ahead and do that that's completely up to you i just want to make sure it's easier for this video and i don't limit this to anybody with a specific backend because there's ways to do this with python with nodejs and all these other platforms this way we have one universal way to do it okay so we have the agora sdk so that's all set up here it's in our folder right here so the file path is connected and what i want to do next is actually go into our main js file here or main.js and we're going to use our app id from agora and i'm actually going to blur this out so when this does go live nobody can use my app id you want to make sure that this is actually hidden here so let's go ahead and specify the app underscore id and then we'll add this in here and that's also where when you go into production you're going to want to use token authentication and i actually have more videos on my channel about how to do that so let's go ahead and actually grab an app id so go back to your project here so we'll go into projects whatever you just created and you're just going to click on the app id here so i click on it it copies that immediately we're going to go ahead and bring that in we'll paste it in here and that is now set up now the next thing we'll want to do here is set up our token value and at this point because we set up our authentication to use app id only we can actually set this as null and we'll leave it there but i do want to at least set the value so i can pass it in where i need and then when you want to update this you can actually change it directly here so once we have our token we also need a uid for each user so every single user in our application when you join a channel this is how we identify who's who so users can actually send messages to each other and we know how many users we have in each channel so how you do this is completely up to you you can use some kind of id from a database you can use a uid generator but in my case what i'm going to do here is just generate a random number and i'm going to make sure it's a big number so we have less of a chance of having some kind of duplicate so we're just going to go ahead and call math.random and we're just going to do this by 10 000. so we're going to generate some kind of random uid and that's it for now so i'll leave this up to you but in this case we also want to make sure that this is a string so we're just going to go ahead and wrap that so we'll do string math.floor math.random and that should give us our uid so on each click or anytime a user goes to this page we're going to have a new uid generated and then later on we can talk about storing this uid inside of session storage or local storage but for now let's just leave it like this so at this point what we need to do is go ahead and create a client object and this is going to be our client right here that we log in with and the client that we have access to all these functions with so it's actually a lot like this peer connection right here where we establish that object and this is going to give us access to everything that we need so when we create our clients we're also going to need a channel so this will be the channel that two users actually join so we're gonna create a channel and then join it and this is what's gonna allow us to send messages to this channel and no information about this specific channel okay so we set up our app id our token uid client and channel and now it's time to actually start triggering this so inside of our init function let's go ahead and set this up right here and the first thing we're going to create is going to be our client object so we're going to call agora rtm dot create instance and we can just throw in our app id here now the reason why we have access to all of these is because of the sdk that we added in here and if you have any questions about all these methods that we're using go ahead and go into your console and check out the agora documentation so we have the documentation right here and the api reference go ahead and just find web and then real-time messaging right here and any function that we use in this video just go ahead and reference it right here if you need to know more about it if you want to try something different this will tell you everything that you need here so we're later on going to use the send peer-to-peer message you're going to see the login method that we're about to use the create instant method that we just used and so on so go ahead and check this out this will also be linked up in the video description okay so let's go back into our project here and once we create our client object the next thing we need to do is actually log in so we're going to call await and then we'll do client.login and to log in we just need our uid and token but at this point because our authentication mechanism is set up to only app id we'll just throw in our token which will be a no value and that's it so we create our client we log in then we need to go ahead and create the channel so we're just going to do channel and this will be client dot create channel so this is all coming from that client object right here and to create a channel we're just going to go ahead and give it a name so this function will either find a channel by this name or it will create it and later on this will be some kind of dynamic value like room id something like that from the url and we're just going to go ahead and have that in our url like this so we'll just do something like index.html if i can type little slower and then we'll just throw in the room id and that's going to be some kind of number like that so for now we're only going to have one channel we're just going to call it main but i'll leave this right here so you can actually reference this and then we're going to update this okay so we're creating our channel and once we create a channel we need to join this channel so we're going to call await and then we can just do channel dot join okay so that's it we just created a channel we joined we logged in with our client and we are set up to go now what we need to do here is actually listen for whenever any other client actually joins the same channel so another user when they actually pull up the website we need to know about them joining the channel and then we need to send them a message so with this we actually have some event listeners that we can actually work with so using this channel method right here this channel object we can actually call channel.on and there is an event listener in the documentation that you can find called member joined so anytime somebody calls the join method on the other end we can trigger this and it's going to respond with the function that we're going to create and this is going to be called handle user joined so let's go ahead and test this out so let's go ahead and create this function and then we're going to check this in the console so we'll go ahead and bring this down here we'll call it handle user joined we'll make this an async function and by default we will get that member uid so we're getting that member's uid then we'll go ahead and just continue here and let's just console out a new user has joined so console.log a new user joined the channel okay so with this i also want to know their ids let's go ahead and throw this in and there we go so when a user calls a join method on the other end we're going to respond with this function right here and let's check this out so if i open this up let's go ahead and do inspect element let's see let's open up the console and i'm going to open this up in another tab we'll make this smaller so we can actually see the console and you're gonna see this respond in real time so we're gonna see this console get triggered right here so we'll go ahead and join and there we go so we see a new user has joined the channel and we see this user's id it's two five three five so the user just joined the channel and it triggered this function for us now when a user joins what's the first thing that we want to do well inside of our application what we need to do is we need to send this user a message from peer one so we have peer one and peer two so we need to send a message over with our original offer so right now let's just work on sending that message over so once a user joins we want to go ahead and actually create the offer so let's take this create offer method and let's move this down into the handle user joined so we'll go ahead and paste that in here and what i want to do here is actually send out a message from the create offer function so what i'll do is take this member id we'll pass that in as a parameter here and inside of the create offer function we'll bring that down here and let's go ahead and actually send out the message now at this point we deal with all the logic here with creating our peer connection we create an offer and we want to go ahead and actually send out the offer once it's created so we'll remove this right here and we can actually access the client object so we'll do client dot send message to peers so this is a function that we have here and we're going to go ahead and send the actual message so this will be the text value and then we can just say something like hey so we'll just throw that in like that and then we need to know what peer to send this to so to actually send it to appear we're just going to go ahead and get this member id and that's how we know who to direct message here so we'll go ahead and add that in and this will send a message to appear with this id now on the other end we need to respond to this message so we need a way to listen for that event and then actually process that specific message so let's go ahead and access our client object inside of the init function and the event listener that we want to handle is going to be message from here so anytime somebody calls the send message function specifically to us so our client object we're going to go ahead and respond here and the function i'm going to create here is going to be called handle message from here and let's go ahead and actually create the function so we want to process the actual message so let's go ahead and create that handle message from here we'll make that an async function and in here we're just going to get the actual message and we're going to get the member id so we want to know who sent this message so let's go ahead and finish up the arrow function and at this point i just want to console it out so we're going to get that message value and specifically we need to get the message dot text value so message dot text and that's going to be this part right here so let's go ahead and test this out we'll open up two browsers side by side once we have this set up here we'll actually be able to start sending the offers and answers okay so let's go ahead and close this out so if i join with another peer so if i refresh this we're gonna get an alert that tells us a peer has joined and then peer two should get a message from this user so right now if i open this up we should see this alert we see a new peer has joined the channel and then on this pier's end we should have received a message so let's check this out and here we go we see the message of hey and it looks like everything's working so awesome so let's actually go ahead and send an offer instead of just a message like that so we do need to send a string here so let's go ahead and actually call json.stringify so we'll stringify an actual object and this will be the message that we send and then we'll parse it on the other end so at this point i want to send some more information with the message so i want to send a type so later on when we're sending offers and answers and ice candidates i want to know the type of message that i'm receiving so this type will be of an offer and then we send the actual offer and that is going to be this offer right here so we're just going to go ahead and send this offer to our peer now on the other end let's go ahead and actually get this offer so we'll just go into handle message from pier and i'm just going to get that message value and we're just going to do json.parse and we're just going to parse the actual message here so let's just go ahead and grab this bring this into here and then change this to just message because we already parsed the text value and let's just try this one more time so i'm going to need to refresh a few things and let's see so at this point this is pier 2 now because i just refreshed this here we have a message type of offer and then the actual offer and we sent that out to the peer now with the offer i also want to send the ice candidates as they get generated so this is going to be that trickle ice method so let's take this entire method right here let's copy that and let's bring this in right here so let's just paste this over this console.log right here and at this point we're going to send a message of candidate and then the actual message itself so the type will be candidate we're going to change that to candidate and then the type of or the actual message will be event dot candidate so let's check this out okay so type is candidate and then we send over the ice candidates all right so let's try that one more time let's refresh this and then we'll refresh this so this will be peer two so here we have an offer then we have the actual candidates so we have one two and three so each candidate was trickled in one by one so there we go that looks good awesome okay so there's one more thing that i want to fix up before we move on to the next section so we're about to start creating an answer and at this point ever since we removed the create offer function from init to handle user joined for some reason the local stream doesn't get created right away if i refresh the page too fast so essentially we're calling localstream.gettracks and every once in a while when the page refreshes this is basically a null value and then we can get the tracks and then it throws some kind of error so let's go ahead and actually fix this so what i'm going to do here is just underneath our remote stream we're just going to ask a quick question and we're going to say if not local stream so we're using the not operator if we don't have it on create offer let's just quickly create the local stream and append it to the dom so if we happen to not have it it's kind of like an extra check here i'm sure there's different ways to do this but that should be a quick fix okay so that's it for the create offer function and let's go ahead and start creating the answer so just below our create offer function let's go ahead and create a new function and we're going to call this one create answer so we'll call this create answer and at this point we'll make this an async function and we also want to pass in the member id here so we have this function and a lot of the logic inside of the create offer or create answer function is going to be very similar to the create offer function so in the create answer function on peer two side we still need to create that pure connection we still need to set up that remote stream which will be user two for peer two so technically on the receiving end the user that sent the offer will be user two and so on so it kind of flips it and they still need to go ahead and create all these local tracks the ice candidates and the peer co or not the ice candidates but the uh basically set up the local and remote tracks and then send out ice candidates now this is the part that's going to be a little bit different for create answer so let's take everything that's going to be repeated in both of these functions and let's put that into its own function and have that fire off before we start repeating all of our code here so let's go ahead and go above create offer and you'll see what i'm doing here in a second if it doesn't make sense so let's go ahead and create a function called create so create peer connection okay so we'll go ahead and make this an async function and inside of pure connection we still need a member id so we're basically just going to keep passing this down and we'll create the function finish this up here and inside of create pure connection let's go ahead and take this pure connection from create offer all the way down to on ice candidates so we'll copy this let's remove that we'll clean up the function here let's bring that into create peer connection so now we can take all this logic inside of create pure connection and we can move this down into create offer so let's just go ahead and call await create peer connection and pass in the member id so that's what we're trying to do we're trying to take all that logic that was repeated bring that in here and now inside of create offer or create answer i can do the same right here so we create that peer connection and there we go so we just cleaned up a bunch of code just to make sure you can recap this we took everything from peer connection down to on ice candidates so both users will have to do the same thing okay so now inside of create answer let's go ahead and continue here and actually process this so in create answer we're going to get the member id and we're also going to get the offer from our user so let's go ahead and get that offer and when we call this function you'll see where this actually comes from so we're getting an offer on create answer when a user sends this over and what we need to do with this offer is go ahead and set this as a remote description so every single peer will have a local description and they will have a remote description so each peer connection has two descriptions so let's go ahead and call this so we'll do pureconnection dot set remote description and we're just going to go ahead and pass in the offer so the offer for peer two will be that or the remote description for peer two will be the offer now peer two also needs to create an answer to send back to peer one so we'll go ahead and create an answer and the answer will be await and we're just going to go ahead and call peer connection dot create answer so earlier we called create offer and for peer 2 or the remote peer we're calling create answer i guess i shouldn't call this peer to i should just say the the receiving peers so basically the second period that joined okay so we create an answer and now we need to set our local description for that second peer so we're gonna go ahead and call pureconnection.set local description so remember we called this for peer one on create offer we took that local description and set that as the offer but in peer two's case we're going to go ahead and throw in the answer so for pier 2 the remote descriptions the offer and the local description is the answer all right so once the peer once the second peer creates the answer what do they need to do well they need to send back their sdp answer so let's go ahead and do this so we're going to take this entire method right here and we're just going to send this back to peer1 or the first peer okay so we're calling client.send message to peer and the type will be of answer and then we're going to take the actual answer let's copy that and we'll send the answer to peer one we have the member id because we passed that in here okay so now we actually want to call create answer so let's go into our handle message from peer function and this is where we need to process everything so we'll minimize all this so we'll minimize the create offer and create answer and then inside of handle message from peer let's go ahead and ask a few questions so the first question we're going to ask is going to be the type of message so we're going to say if message dot type remember we're sending that entire object so we actually have this type value so if the message is an offer let's go ahead and actually create an answer so we're going to go ahead and grab this function right here so we pass in the member id and the offer into it so if we received an offer let's go ahead and paste this in so we'll go ahead and throw in the member id so we need to know who to send this message back to so we'll just go ahead and do message dot offer okay so that's the member id and that's the offer and that's where we get it so this offer will be set as a local description now we want to ask this question one more time and what i want to do here is also process the answer so when this peer creates the answer and sends the answer back we need to also process this from handle message from peer so let's copy all of this right here and we're going to say if the message type is of an answer let's go ahead and actually add the answer so we're going to create this function right now so we'll call this add answer and let's go ahead and actually process it so in add answer we're just going to do message.answer we're going to get this back from up here and we don't need the member id anymore because we're not sending a message back so that's going to complete that transaction so let's go ahead and actually add the answer so we'll go down here just below create answer so this will be that first peer the peer that initiated the offer they're going to get back the answer and then they need to process this so we'll go ahead and call add answer and we'll make sure that this is an async function and let's go ahead and grab the answer we'll complete the function and all we need to do here is go ahead and set the answer so we're first going to check if we have a current remote description so we'll use a not operator so if pure connection dot current remote description so this is actually how we can get it so if pureconnection.currentremote description just want to type that slowly so if we currently don't have a remote description let's go ahead and set it so we'll just do pure connection dot set remote description and we're just going to throw in the answer so i just want to recap this real quick so remember peer one sends out the offer so let's see we create everything oh that's pure connection let's go into crete offer so peer one sets their local description and sends out the offer and then peer two when they get that offer sets their remote description and their local description but the last thing in this process is peer one still needs to set their remote description which is going to be that answer okay so pier one or the first pier is going to go ahead and set that remote description and that almost completes the process but we still want to do one more thing we still want to set the ice candidates so let's take this right here and let's copy and paste it and we're going to say if the message type is of candidate so remember inside of create peer connection both peers will send out these candidates as the candidates get trickled in so let's go ahead and go up here so if the message type is of candidate what do we want to do here so we're just going to go ahead and check if we have a pure connection so let's just double check this so we'll say if we currently have a good peer connection let's go ahead and call peer connection dot add ice candidate so this is actually how we add a candidate to our peer connection so we're just going to call message.candidate so we sent out the candidate right here so we're just calling message dot candidate all right so let's just recap a few things let's make sure everything's going good let's see let's go back up here so we get the offer we create the answer and we get the answer we add the answer and then when we get a candidate we go ahead and set that candidate now there's one other thing that i noticed here so inside of our create peer connection function i actually forgot to add in a track on peer connection on track so let's take this track and to the remote stream let's throw that in and at this point everything should be working well so we're going to open up our two tabs and we're going to see that peer connection take place let's hope everything went well so let's go ahead and give this a shot so we'll open up our browsers and we're going to need a refreshing everything here so i'm sure we're going to have some kind of errors at first so let's just refresh everything and it looks like it's already working awesome so here we go we'll close this out so you notice how pier 2 i guess it's on this side i'm frozen right there that's because i left and we're going to deal with that leave functionality in a second but at this point if i refresh this we're only going to see pier one so that looks good now when another peer joins we're going to see pier 2 join and then on that other peer side we're going to see pier 2 or pier 1. so essentially we just successfully exchanged the offer the answer the ice candidates and everything's working well so if that part didn't fully make sense go through that slowly and just kind of try to understand everything and watch the beginning of the video where i talk about that exchange process but we just finished that up and everything is working well so let's go ahead and actually deal with this because right now if i make a funny face i'll just do this and there we go so i'm frozen awesome so that's what i wanted to do so we want to fix that okay so let's go back in here and the first thing i want to do here is actually go into my css file and i want to make sure user one is hidden until user or user two is hidden until we join so we're gonna go ahead and grab this second video container or the video tag and we're going to hide it and then display only when appear joins so we're just going to go ahead and do user 2 not video but user 2 and we're just going to do display and we're going to set that to none okay so user 2 is going to be hidden and now we can go back into our code or into our website and there we go we see user 2 is hidden now the next thing i want to do is go ahead and actually display that user as a block element once they actually join the stream so we want this for the local and remote users so both peers want to trigger this so let's go ahead and go into create peer connection and we're just going to copy this right here and we're going to paste this just below and we're going to call user2.style.display and we're going to display this as a block element so when they join we're just going to go ahead and display that as a block element or when they're about to join and that'll go ahead and get that taken care of now the next thing i want to deal with is when i use our leaves so anytime they leave i want to go ahead and hide that again and there is another event we can listen for with this channel so we're going to call channel on and we can listen for member left so that's the event and then we'll add in the function and that's going to be handle user left okay so let's create the actual function we'll bring this down here and we'll just create this called handle user left and this is going to actually take in the member id we don't need it here but i figured i want to show you how to pass that in if you want to actually maybe remove a specific dom element so we'll pass in that id and then you can use this later if you need it so at this point all we need to do is go ahead and grab that user2 value so we'll do document.getelementbyid and we're just going to go ahead and grab user-2 dot style dot display and this is going to be set to null or actually we'll just do none okay so on user leave we're going to go ahead and hide it but we still have a few things we need to take care of so at this point when a user tries to leave here we're actually not leaving with that user from the channel so we can close out the browser but by default if agora sees any activity after about 20 or 30 seconds then that user left will be triggered so we want to actually trigger this whenever the user actually leaves so it happens right away so let's go down here and let's actually log a user out and leave the channel so we're going to create another function called leave channel and we're just going to go ahead and make this an async function again and at this point let's go ahead and call the two functions so the first thing we want to do is create or call await channel dot leave so we want to leave the channel and then we want to log out as a user so we're just going to go ahead and call client dot log out okay so leave channel should be called anytime a user leaves now one thing we could do is we could just go ahead and actually trigger this anytime a user clicks a leave button but we all know users don't actually work that way they usually just close their laptop or click x up here and we want to account for that so at this point let's just go ahead and call leaf channel on window so we'll add an event listener to the actual window and we can add an event listener called before unload so basically before the website actually closes we can call before unload and anytime a user closes this out we're just going to call leave channel so that will ensure that a user leaves or actually leaves a channel whenever their window just closes out now at this point i'm going to need to wait a little bit because we've technically joined with a new user every single time that channel is refreshed so go ahead and give this about maybe 30 to 40 seconds let every single user leave the channel if you've been refreshing it and then we'll just test this out from scratch because what's going to happen is if a user joins right now and then we actually try to join with another user it's going to toggle that handle user left function anytime another user leaves and if we have two users in there but a third user leaves it's going to remove that dom element so i think that might have been about 30 seconds let's go ahead and just check this out let's see so we're seeing this handle or this member left function being called so it looks like it completed all of those and let's go ahead and just try this out so we'll open this up in another tab so we see two users perfect and now when we leave so let's just close this out there we go it removes it and that looks good so let's try this one more time let's give this a test there we go it looks good awesome okay sweet yeah we just needed to do that delay because by default again if we don't call a function in fact let me just show you because uh i guess while we're here i want it to make sense here so let's remove that and let's not actually call the leave channel function so let's open this up here and let's join with the user and then let's really quickly leave with this user and join with another so at this point we see two peers in the chat and they're connected they should be able to communicate but the problem here is about after 30 seconds because i left with that original user they are going to leave the channel by default after that time and it's just going to remove this window so that's why we need to call it manually because then agora will just respond on its own so i might just fast forward here give it some time there we go it just triggered it and we're supposed to be still in our conversation but because that other user that just left left the channel officially now that element was removed for the dom so yeah that was kind of our issue so now if i actually go ahead and remove this or comment this out or take out that comment now the user will be removed whenever we tell them to so the next thing i want to do here is go ahead and start creating dynamic rooms so up until now we only have one room name and that's main and i told you earlier that we were going to go ahead and append a room name to the url so we can actually create multiple rooms and have people join those rooms so how we're going to do this is we're going to create another page and this will be a lobby and essentially we're going to have a form where we can enter a room id share that room id with somebody and then they can go ahead and enter that same name and join this specific channel so let's go ahead and create a new template here and we're going to call this lobby.html and inside of this page let's go ahead and set up our boilerplate code here so we're just going to start typing in html we'll set this up i'll go ahead and set the title here and in here inside of the body i'm just going to go ahead and actually remove that script tag and we're going to change this to lobby.css because we're going to add in our own css in a bit so let's actually add that right now so it doesn't throw any errors so we're going to call this lobby.css okay so inside of lobby.html let's go ahead and build out our form so we're going to go ahead and create some tags here so for this we're just going to go ahead and do form give it an id and the name is going to be join dash form so with this form let's go ahead and add in some input fields so we'll do input and for this value this will be a type of text here so that's going to be the actual invite code and for the name i'm just going to call it invite link so we'll do invite underscore link and you can call this whatever you want i just made it like that when i built out the demo so we're going to leave it like that and i'm going to make sure that this field is required so you can't join a room without an invite link so you have to enter that so the next input value this is going to be the actual submit form so we're going to go ahead and set the type to submit so type is going to be equal to submit and then the value so we want to actually have some text here we're just going to say join room and that's going to be it okay let's create some space here so let's go ahead and check out the page make sure that we have this set up here so we're just gonna do lobby and we'll close out the second one so here's our join form and we are ready to add in some functionality here so we're gonna throw in our invite code here or let's say we have a room name like learn code or something like that whatever we want it's up to us we're just going to throw in some kind of values in here so let's go ahead and move to this part here and we're just going to go ahead and add in our javascript code directly in line so we're just going to go ahead and create the script tag and in here the first thing i want to do is go ahead and get my form so the forum is going to be document.getelementbyid and we're just going to get the join form so we'll get that join form once we have that let's go ahead and add an event listener to it so we'll do form dot add event listener and specifically we want to listen for the submit event and we're going to create the function in line here so we're just going to go ahead and create the arrow function and when we submit this form the first thing i want to do is actually just go ahead and prevent that default action so i don't want the form submitting anything i want to actually take care of what happens next so what i'm going to do here is just go ahead and grab the invite code so we'll set a variable so we'll do invite code and this is going to be e.target so we're going into the form and we're just going to grab the value of invite link so invite underscore link so we're going to grab that invite link right there and we're going to set that to invite code and then we just want to redirect our user to the next page so we're going to do window.location and this is going to be with the backticks we're going to do index.html and then we're just going to throw in the room id so we'll do room is equal to and then we'll throw in the invite code okay so when you're actually creating this invite code try making sure that it's only one word you could just go ahead and actually parse it make sure there's no spaces in there because that could throw issues i'm not going to focus on that in this video but i would highly recommend forcing users to make this one word because if they're spaces when we're actually creating that room id that might be an issue okay so we have the form and when we submit it it should go ahead and redirect a user to the index page with the invite code so we still have some more work to do but let's go ahead and test this out so we'll just test this by adding in some kind of id we'll do one two three we'll do join room and it looks like we have an error so let's go ahead and see what's going on there so for some reason the invite code is not actually working so we did e.target oh we need the value so there we go i thought that looked weird because we actually got the object okay so we'll refresh that we'll do one two three join and there we go so we have index.html in the room of 123. so to actually make this room we need to go ahead and get this value from the url so back in index.html or main.js actually let's go ahead and open this up and at the top of the page let's go ahead and actually get that url so just underneath our uid actually we'll bring bring this right here let's go ahead and parse the url and get the values so the first thing i want is the actual url parameters so we'll go ahead and set up the query string here and this is going to be window.location.search and then once we set this up we're going to go ahead and get the url parameters so we'll do url params and this is going to be new url search params and we're just going to throw in query string in here okay so we're going to get all that and then we're going to go ahead and get the room id so we're going to do let room id so we'll set this variable up and this will be url params and we'll just call get and then throw in the actual room value so once we get that room id i just want to make sure that a user does have a room id before they actually go to a specific room so we're going to use a not operator and we're going to say if there is currently not a room id so let's say that a user tries to go to this page they just try to go to index.html or whatever we call that page we just want to redirect them back to the login page whenever they don't have a value in here so we'll do window.location and we'll just do lobby.h okay so we have our room id and let's go ahead and bring this in here so on channel we're no longer going to join the main channel we're just going to go ahead and throw in that room id so that's going to replicate this right here so let's go ahead and just remove that now so let's go ahead and open up our page and check this out so at this point let's just go into index.html and this should redirect us so if i click this it redirects us back to lobby so we cannot join index.html without some kind of room id so let's go ahead and add in one two three we'll join and here we go we see ourselves here so let's go ahead and open up another tab here and let's go ahead and send the user to the lobby page so we'll just do lobby.html and let's say this user right here wants to invite user number two we'll give them this invite code we'll just do one two three and then when they join there we go we're in the same room now if this user leaves back to the lobby and let's say they join with one two three four they're gonna join a room but they're gonna be in their own room because that room name actually changed so perfect everything's looking good this is exactly what we wanted here and at this point what i'm ready to do before we actually start styling this is i'm ready to add in some controls so we're gonna add in some controls here to toggle our mic toggle our camera and actually leave our room and then we'll actually start making this application look pretty awesome so a lot of good work so far if you got this far you should be proud of yourself let's go ahead and move on to the fun stuff of actually making this thing functional and look good so we'll go back into index.html here and in here what i want to do is go ahead and create a container for my controls so we'll add this just below our videos we'll go ahead and create a div here and i'm just going to go ahead and give this the id of controls so we'll do controls here and then let's go ahead and actually add in some controls so we'll create some space here and each control item is going to be wrapped inside of a div so we'll create a div and i'm going to give this a class of control dash container so we have our control container and this specific item right here will be the camera button so we'll do camera btn and i'm actually going to give you some images to work with if you want to use your own icons you can go ahead and throw those in but i want to give you some specific images to use here so we have our control container and our camera button and let's go ahead and add in the image and then we'll drag it in here so we'll go ahead and do image close this out and then we'll set up the source here now for the images i'm going to go ahead and refer you to the github repo if you want to use the same exact icons that i'm using so i'll go ahead and drag this into frame and this is going to be linked up in the video description so go ahead and download this entire repo so at this point i just want to walk you through this just to kind of make sure you see all the steps here and let's just go ahead and bring this into our desktop so we have the folder we'll go ahead and open up the zip folder grab the icons folder and drag that out here and then let's close this up here so we'll close the repo again find that in the video description and then drag this into your peer chat folder okay so now we have the icons here and here we have a camera uh invite a mic and a phone icon so we'll just have a few of them in there and we're just gonna use the ones that we need right now so at this point let's go ahead and actually set up the images here so we'll go into the icons folder and the first one i want to grab is going to be camera.png so these are all in here so we see camera.png and once i have that i just want to go ahead and duplicate this two times and set up the other icons here so this one's gonna be a mic button and we're gonna do mic dot png and then for the leave button we're gonna go ahead and change the id to leave so leave btn and then this is just gonna be a phone so it's going to be like we're hanging up the phone and there we go we have our icons so these still won't be visible because the icons are white and our background is currently white so let's go ahead and actually create some styling so we have this container here for the actual buttons here and this wrapper so let's go ahead and style these up so we'll go into not lobby.css but main.css here and let's go ahead and actually style the controls so the first thing i want to do is actually get the buttons here so we'll do control dash container and for this let's go ahead and give this a background color so i'll do background color and i actually have an rgb color that i want to use here so we'll just do rgb and i'll just add in these values so 179 and then we'll do 102 and then we'll set some opacity here so 249 and then the opacity of point nine here so i just want to make sure that you can sort of see through them because we are going to be placing the icons over in the video frame okay so we have a background color let's go ahead and add in some padding so for the padding we'll just do 20 pixels all around set that up here and then we'll just make sure there's circles so we'll do border radius 50 and then after that we also want to make sure that the button is centered or the actual icon image is centered and set in inside of this container so we'll just do display flex here so we'll display flex and then we'll just do justify content and that's going to be centered and then we'll do align items centered so we want to make sure it's centered horizontally and vertically okay so we're going to center that and we also want to make sure the buttons look clickable so we'll do cursor pointer and then we want to style the actual images inside of this container so let's go ahead and take the control container and we'll just take the image tag and we'll give it a heightened width of 30 pixels so 30 pixels and then width is going to be 30 pixels and then for the leave button i want to make sure that this is red so we're just going to go ahead and give this a different background color so leave button we're just going to do background color and this is going to be an rgb color so rgb and the value here is going to be 255 and then we'll do 80 80 and then we're just gonna set that to point nine actually for this let's just not give it any opacity okay so the next thing i wanna do is style the actual container so inside of our page here index.html we're gonna style the controls wrapper so we just want to make sure we position this at the bottom of the page and we want to make sure that it's centered so let's go ahead and get that container so we'll do controls and we're going to do position fixed here so fixed position so this way we can place it anywhere in the page and we want to make sure this is at the bottom but not exactly touching the bottom so we'll just set that at 20 pixels and then we want to center this so we'll do left 50 and then we can do transform and this will be transform x right there and then we can do negative 50 so that's going to go ahead and center that and then we want to inline all the items so we'll do display flex and we can just do column of one em which want to make sure that we're not call them but we wanna add in a gap so some spacing between each item and we'll just do one em all right so let's go ahead and check this out now let's see what we have let's join the page we'll just join the room of one and there we go so we have our control buttons and everything looks good so let's go ahead and actually link this button right here up to the page so we'll go ahead and actually make this a link so we'll just wrap it so just above the div here let's go ahead and create a href here and we'll just point this to lobby.html so on the leave button all the users going to do is just go back to the lobby so just go ahead and wrap this entire item fix the indentation and we should be good to go okay so if i click this there we go we can leave if i go ahead and add in a room name we're here and that should be good to go okay so let's add in the functionality to these buttons so right now if i'm clicking it nothing actually happens so let's go ahead and create some functions here and toggle a few things so let's go back into our main.js file and at the bottom of the page just before or just after leave channel let's go ahead and create this function so the first thing we want to do is toggle the camera so we're going to call this toggle camera and we'll make this an async function and in here what we need to do is first find the video track so the actual camera we'll just we're just going to go ahead and set this to video track and we're going to look inside of local streams and we're just going to search all the tracks we're going to call it get tracks and we are specifically looking for a track that has the value of video so we're going to call find and in here we're just going to loop through look for the track and specifically we're going to check if the track dot kind value is equal to video okay so let me just go ahead and close this out so we're getting the video track we're going into local streams we're finding the track video and once we have that we just want to check if this video track is enabled or not and then we want to switch what we're going to do with it so we're going to go ahead and check the video track and we're going to call enabled so enabled okay so we're checking enabled if the video track is currently enabled what we want to do is go ahead and turn it off so we're going to call video track dot enabled and we're going to set this to false that means we're disabling the track and the camera is going to be turned off temporarily it's going to be muted so once we do that we're just going to go ahead and change the color of the actual button so we're going to get the actual button value and this is going to be camera btn so camera button and we're just going to do dot style dot let's see we're getting background color and we're going to set this to rgb and the actual value is gonna be 255 and we're getting 80 and then 80. so that should go ahead and set that to red so if the camera track is currently not enabled that means it's currently muted then what we want to do is just go ahead and turn it back on so we're going to get these two values right here paste this in we're going to set the camera enable to true and then we're just going to change this color right here and we're going to do 179 and then we can do 102 and 249 and then we can actually do the 0.9 opacity okay so that should toggle the camera and we're simply toggling the actual video track and changing the color so once we do that let's go ahead and add in the event listener so we're going to get the camera button we'll add an event handler to it so we'll bring this down here dot add event listener and we're gonna we're gonna listen for a click event and we're gonna call toggle camera okay so that's all we need to do let's go ahead and test this out we'll open this up let's go ahead and click the camera button turns it off turns it back on and it looks like we're good to go okay so at this point we need to go ahead and toggle the actual mic so before we do that we're gonna go ahead and go up here and we're gonna set audio to true because we have not gotten the audio yet so that was because i didn't want the echo to occur so at this point i'm just going to be able to mute it once it starts happening but once we set that to true we're just going to go ahead and copy this toggle camera function and paste that down here and simply update a few values here so we're going to call this toggle mic now and we need to update this to audio track so we'll do audio track and then the type of track that we're looking for is going to be audio and let's go ahead and just update this so audio track enabled we're going to update the actual audio track here update this part right here and let's see we actually want to change this to mic button so mic button and then mic button okay so we're doing the same exact thing we're simply checking if it's enabled if it is we turn it off if it's not we turn it back on and we style the button okay so now that we have this toggle mic function let's just go ahead and copy this paste it and change this to mic button and then we're going to toggle the mic function here or the call the toggle mic function okay so at this point i'm going to hear some kind of echo and i might have some issues because we only allowed the video feed so if you're seeing this right here this is probably because we need to reset some settings here because originally we denied permission for audio but we need to go ahead and just reset this so let's go ahead and check this out we'll go into privacy and security go into site settings and at this point let's just reset everything here so we'll go into camera we'll reset all of this and then we'll go back here site settings and then we'll go into mic so we denied it earlier so now it's confused and why we're having access to it if we already denied it okay so now let's go ahead and refresh this and now it's going to ask me for the camera and microphone tracks before it only asks me for the camera and i'm going to get my headphones in here okay so let's go ahead and allow all and there we go okay so i hear the echo it's really loud it's throwing me off but now i can actually toggle the camera toggle the mic on and off and both users can do this so let's go ahead and try this out so let's mute it here so if user one turns off their camera user 2 is also going to see that user's camera turned off so if i turn this back on it's going to turn it on on both sides and that's it for the control functionality so we're almost done here we are ready to style up the page so what i'm going to do here is go ahead and just add in styling to the lobby page and this page we're going to go through this pretty quick and then we'll move on to setting the video quality so when the screen actually gets big you're going to notice the dimensions of the video itself are going to be a little bit messed up they're not going to be messed up exactly but the quality of the video is going to be not the best so we just want to reset that so that's the last step that we have to do so let's go ahead and finish this up and i'm going to go ahead and close this part out here okay so let's go ahead and go into our main.css file and let's start changing things up so the first thing i want to do here is go ahead and start resetting the global settings here so we'll just go ahead and set this for everything and we're going to set margin to zero so basically margin on every single item in our page is gonna be zero and then padding is also going to be zero and then we wanna go ahead and set box dash sizing to border box so just just so we actually fit the parameters of any item that we have so the padding isn't extended outside of the parent element i don't know why i'm teaching css here but yeah okay so for the videos we'll display this as grid we only want one frame here so we no longer want two frames because of the style that we're going for we don't need a gap anymore and we're just going to set the height to 100 viewport height so let's go ahead and just see what we have here so let's also set overflow to hidden so overflow just add hidden here okay and let's check this out so let's just see what we have so far so at this point we just adjusted a few things so let's see display grid height is 100 and let's just set the height of the actual video player to 100 so that's why i didn't fill it all the way out okay so this is more of what i wanted and let me just reset my camera here so it's more pointed at me now so this is the height and width that we currently have we just took up the entire frame so let's go ahead and just complete the styling so for the video player let's also do object fit and we'll do cover so i make sure that we cover the entire item so we'll just set that up and then for user two let's see we're going to leave this as is but we want to go ahead and style the remote user so the remote user will take up the big screen so as we're talking to someone we want to see them in frame but we want to make sure that our video frame is here in the top left right here so let's go ahead and actually add in a new class that we're going to append to our user so we're going to create a class called small frame here so we'll go ahead and set this up the position is going to be fixed because we need to position this anywhere we uh we need to position it wherever we want on the page so that will allow us to do this for the top here we're going to do 20 pixels from the top and then we're going to do left at 20 pixels okay so for the height let's just go ahead and set this to 170 pixels and for the width let's set this to 300 pixels so we want to make it sort of like a rectangle here and then we want to give it some round borders here so we'll just do a border radius of five pixels and let's see for the border itself i want to make sure that it's a purple border with two pixels make sure that it's solid and we need to add px right there and then for the hex value we'll just do b three and then we'll do six six f9 now i also wanna make sure that we have some shadow here so i'm gonna go ahead and just copy some values here and just paste them in so we added a box shadow and then a z index just to make sure we're at the top of the page that this element is always uh towards the front so it's not hidden somewhere so to actually adjust our video frame and add this class let's go back into main.js here and on pure connection we're gonna go ahead and actually add this class to our video frame so let's go ahead and grab our current user right here so now we're gonna get user one we're gonna copy and paste this right here let's create some space we'll change this to user one so that's us and we're just gonna go ahead and add in a class so we're gonna do classlist dot add and we're just gonna go ahead and add the class of small frame so that'll change our frame and then after this we also need to make sure that we remove it anytime a user leaves so on handle user left wherever that's at let's go ahead and actually update this somewhere here handle user left we're going to go ahead and grab that same element and at this point we're going to remove that class so essentially when a user joins we'll add in all this styling to our video frame when they leave we'll take it out okay so let's save all of that let's make sure we can refresh this and let's check this out okay so i'm in here alone i'm going to go ahead and mute my mic and we're just going to go ahead and add in a new user and there we go so look at that so when this user joined this video frame went up here so that's us right here at the top in this section and this is that remote user so if i leave now we're in that big frame so this is us right here and the remote user is no longer here so if i add this again let's go ahead and just watch this update so our video frame that's currently in here will jump to the top left so go ahead and add that and there we go so that's exactly what we're looking for i think that looks pretty dang cool and it kind of represents more of something that you would see on a phone or something like that so speaking of phones let's go ahead and fix that and i know the quality is kind of bad here so we're going to adjust a few things uh in a minute so let's go ahead and make this actually responsive so right now if i start bringing this down i don't want my face covered up here in fact i can just do inspect here and let's just bring this over so when this video frame actually gets small i want to make sure that we can adjust for that so let's go ahead and add in some styling and some media queries so we'll go into our main.css go down below the leave button and we'll just do at media screen and we are going to do and 600 pixels so max width 600 pixels so basically when we get to 600 pixels that's when we want things to adjust and we're just going to go ahead and get the small frame and we're going to set a height of 80 pixels and then the width of 120 i'll leave the actual details up to you whatever you want to do in your application is is a in your control here so i also want to make sure that the control buttons are small so control container image i want to make sure to actually restyle these so we're just going to set a height of width height and width of 20 pixels so we just want to shrink those down a little bit just so they look a little bit more proper save that and let's just go ahead and test it so here we go so that video frame is much smaller so if i adjust this there we go when we get to 600 pixels that looks a little bit better okay so for some reason the control containers don't seem like they're resizing i think they are i just can't really tell let's just try doing this at like one pixel or 10 pixels let's just see okay so that's actually working that's good that's just the button here okay i guess i could adjust the padding but we're not gonna focus on that okay so we're about to get to the video quality but before we finish this up i also want to style that form here so what i'm going to do here is actually copy and paste some values so i don't want to focus on html and css in a webrtc tutorial so i'll just go ahead and add in some of the main template code and then we'll style this up i'll just paste in some css or i guess we'll just see how things go so we're going to create a main container here and we're going to call this lobby container it's a lobby container and that's going to wrap the entire page and inside of this main tag right here we're going to create a div and this will be the form container so we're going to do id that's going to be form dash container and if you're looking for the actual code here you can just go ahead and check out the github repo and that'll all be in there so you can see the actual examples so in here inside of the form container we want a header so we're going to do form container header so form underscore container then we'll do underscore header and this these are double underscores so in here i'm just going to paste in a value so we're just going to throw in a p tag and it's going to have this wave emoji here and it's going to say create a new form so after that we're going to go ahead and create another section for the actual form so we're going to create another div and we're going to go ahead and call this form content wrapper so give it an id of form double underscore content and then we'll do double underscore wrapper and the form itself will actually go in here so let's take this join form copy that remove it and paste it into form content wrapper okay so that should be it for the actual template so let's go back in here and let's go ahead and leave the page here is our form and let's actually add in the css now so we'll open up lobby.css so in lobby css we're going to go ahead and add in some background color to the body so i'm going to paste in some code so that'll give that'll give it a dark background so i'll save this and we'll just check this out so that changes the styling here so once we have that we'll take the form container so i'm going to paste in some more code here so it's going to be the form container centered with a width here so we have a border radius a background color and we position it in the center so if we open this up here is our form container we'll just go ahead and bring this down and again i'm doing this for the sake of speeding up the video because this is not what we want to focus on so the next thing i want to style is going to be the actual form container header so in lobby.css we'll throw that in we're going to add in some border radius a different background for the actual form header some font size and so on so here we go we see that container there and for the form content wrapper we'll add in some padding so we're going into form content wrapper let's save that we're going to add in some padding and then the actual input fields so we're going to go ahead and grab two input values so we're getting the actual input field here we're styling it up and we're going to change the submit button to be purple okay so that looks much better and that is our join form so let's say we want to join our room we'll just call this one two three again we'll join here is our page and when we go back that looks a lot better okay so the last thing we have to do here is actually add in some quality to this video chat here so let's go ahead and fix this up here so this is going to be very easy all we're going to do is go ahead and add in some constraints here so we'll go into main.js here and i'm going to create a variable just above my init function and i'm going to call this constraints so constraints and we're just going to go ahead and throw this into let's actually fix this up real quick so we have our constraints and we're just going to throw this into our get user media function so let's see where is that that was on create pure connection oh no that was uh i think was in the init function yeah right here so we're just going to go ahead and throw this in here we'll take all those constraints and throw those in here and let's just go ahead and recreate these so the first thing i'm going to do is go ahead and set the video so instead of just setting video and audio to true or false we're actually going to add in some values so when we add the values it'll be assumed that these values are true so we'll go ahead and throw in the width of our video and what we can do is set a minimum value so we'll do min as 640 for the frame size and then we'll set an ideal width this is what we actually want our frame to be and we'll set that to 1920 and then for the max we're going to set this to 1920. so ideally we want that video as big as possible because when we load it up in here we want to make sure that it's not stretching a small video frame so that's why the quality is so bad right now besides maybe other settings but we're not going to deal with anything else other than height and width here so we'll set the width and then we're going to set the height here so let's just go ahead and copy all of this we'll change this to height and for this the min is going to be 480 so we're making a rectangle and then we're going to do 1080 and 1080. and then for the audio we're just going to set that to true that's going to be it so this is how you can actually adjust it so we just specify values for our video so we throw in this entire object and we throw that into get user media so now if i save this let's look at the difference so look at the video quality it's more zoomed out now the frame is bigger and everything looks a lot better so that's it for this project we are done with this application and what i want to do next is spend a moment talking about group video calling with webrtc so at this point our application only handles two users so it's just peer-to-peer and group video calling is an entirely different topic in webrtc and there's multiple ways we can go about doing this so we're not going to build anything out but i do want to give you a high level overview of how to handle that before we move on to the next project in this video so how do we take a technology that's fundamentally designed to handle peer-to-peer connections and go beyond that well it turns out there's actually three different approaches to solve this exact issue so let's go ahead and go over them the first approach we have is called the mesh network and with the mesh network we are creating a network of peers that will all maintain a peer-to-peer connection with every other peer inside of a group video call so let's say we have a call with five peers well peer one will have to create a peer-to-peer connection to peer two then to peer three four and five and all the other peers in the network will have to create that same connection with every other peer inside of that network so it kind of meshes them together and this will happen for every other peer that has to join after this so every new peer that joins will have to create this connection and all the other peers will have to connect back to this peer so every single time a new peer joins we now have to maintain a new connection and this technically does accomplish what we need here but this approach has a massive downside and does not scale at all the problem here is that every single time a new peer joins we now have to upload our stream and maintain that connection so this requires a lot of upload bandwidth so once you go beyond four to five peers you're gonna start seeing a lot of issues here so this approach is not recommended if you're looking to scale now the second approach we're going to talk about is less of a peer-to-peer architecture and more of a peer-to-server architecture and that is the mcu approach which stands for multi-conferencing unit so how this works is now each peer will be connected to the server and unlike the mesh network we're going to upload our stream to that server and that server will take in all the incoming streams process them mix them together and then send them back to the peers now the upside to this is that it does scale better and you only have to upload your stream once the downside is that that server that you're going to send this to has to be very beefy it has to process all of this so it could get very expensive to actually handle this so it scales better but the downside is is that it's going to be more expensive for the third and final approach we have the sfu method which stands for selecting forwarding unit and this is very similar to the mcu approach in the sense that we still have appeared to server architecture and we're still only uploading our stream once to that server now where this differs from the mcu approach is that we're no longer going to mix those streams together and process them the server will now act as a router and simply just make sure that we get all of our peer streams and that all of our peers get our stream so it's simply going to direct traffic and that's all it's doing so the benefit to this is that because we're no longer mixing and processing all those streams we now don't need such a beefy server and it's going to be a lot less expensive now while this approach does have a lot of benefits to it one thing to be mindful of is that we're still only uploading one stream but we are now having to download multiple streams so if we start having a group video call with maybe 20 30 40 people you do have to be mindful of people's download speed most people nowadays would probably be fine with this but it's just something to consider is the fact that you do need download speed in order to start downloading all those streams so the benefit that the mcu approach has is that users only need to upload once and download once with the sfu method the upload once and then download multiple times now in part two of this video we are going to be creating a group video called an application but instead of using one of the three approaches that i talked about we're gonna go another route and we're just gonna use a third-party service so anytime you're building out an application like this you have to decide do i want to build all this out by myself do i want to set up that infrastructure and deal with scaling this application or is there a third party service that can make my life a lot easier so the trade-offs is that usually a third-party service is going to cost you some money but that can also save you money down the line if you're having to still pay engineers or spend time on building this out so i wanted to make sure that you can see both approaches make that decision for yourself and if you're going to follow along for the next project go ahead and stick around and i'll see you in part two hey welcome to part two of this video i have no way of knowing if you just skipped ahead to this part or if you followed all the way along up until this point but either way this is a great place to start for project number two and with that being said let's go ahead and do a full walkthrough of the project i'll show you where to get the source code and we're just going to start coding this up so if you want to get the source code for the project there will be a link in the video description go ahead and download this i'll add in instructions on how to actually get started there's also a starter template here we're going to cover this and that's all down in the description so this is our project and a little back story behind this is i actually ran a open source project for a project called mumble and it was a social network that we built out and after about 20 days and 500 plus prs it just got way too overwhelming to manage it so i just kind of let it go but i did want to keep the name because i love the logo love the name so i decided to make this like a version of mumble so that's going to be the name of our project here and that logo and all that's going to be also in the source code so right away when we join this website we're going to see or when we join a room we're going to see a mumble bot send us a message and it's always going to welcome us to the room here so we have a chat room right here i can say hey and then hi that's all real time and any other user in this room will actually see this and i also see my name right here now if i actually want to share my screen i can go ahead and toggle this right here select the screen i want to share i have two screens let's say i'm teaching someone a code or i'm doing a tutorial we can actually go through and share everything that we have so if another user wants to join this room what we're going to do is go ahead and copy and paste this link right here and immediately if they don't have a name it's going to redirect him to the lobby so let's say user number two is paul and the room name is one so we see dennis in the room as a participant and when paul joins the room immediately paul is going to see dennis's stream so if paul wants to put dennis's stream into focus all he has to do is click on this and this will expand it into focus and we can always toggle it just keep changing that and it's just going to adjust our actual video frames basically change what's in focus so let me actually hide this so when paul joins we see welcome paul and now we see two participants here so paul will not see any messages that were in the room prior to his stream or him joining the room and if we go back to this room dennis is going to see that paul has joined the room so if paul actually wants to join as a presenter here we're not going to deal with invites maybe in the live demo we'll actually add this but at this point anybody can join the room so if paul joins now this is paul's stream so if paul wants to show his face in the frame here we can toggle back and forth here and if we go back to dennis's screen dennis can also toggle back and forth so dennis can now see paul now if paul leaves the room we can go ahead and just leave like that by closing this out we'll actually get an alert that paul has left the room so that's the core functionality we just have participants we have full capability of toggling the mic toggling screen sharing changing up our video frame and actually one thing is when you actually go from sharing your screen back to presenting i made sure that your mic is muted and that your camera is muted i don't know i just thought of this funny situation that if you were sharing your screen but maybe you had someone in the room with you and then you went back to just turning off your screen sharing and your camera turns back on that might be a little bit awkward it just might create a funny situation so you'll actually have to toggle your camera i'll just show you that one more time so for sharing and then we go back to here so we toggle the camera or screen sharing off camera starts as off and so does the mic so a little warning there anybody can create a room and again the messages are in real time so if i message something right here everybody in the room will actually see the message so source code live demo and with that being said let's go ahead and close out this demo and let's get started okay so to get started with this project we're going to want to download the initial project files get that starter template and then we'll just start building on top of this so this github repo is linked up in the video description go ahead and clone it or download it from here we'll download it as a zip file in this case and we're going to want this starter template right here so this is the code for the entire project specifically though we want this starter template and this is what we're going to build on top of so let's go ahead and drag this into our desktop and i'm going to close out the github repo and we'll open up this folder and specifically i want this starter template so we'll bring this out here and at this point i'm just going to delete the zip folder because i don't need it if you want to actually test it out get that project up and running you can go ahead and do that but i'll just remove that and let's just rename this to mumble 2. okay so i'm going to open this up in vs code whatever you want to use as your text editor that's your choice i'm going to use vs code because i like live server so let's go ahead and open this up and we'll just check out what this project looks like and we'll recap the files that are in here right now so the first file i want to look at is going to be room.html i'm going to open this up in live server because i first want to see the output and then we'll actually take a look at the code here so we have a header bar right here a sidebar for the chat some stream controls and participants on the left now if we go to create room we have a lobby.html page and this is simply a form with a form to enter your username the room name and then an actual button to go to that room so not much of a template there's just some functionality added here so let's go ahead and actually go back to the room so we'll go back to room.html and let's take a look at some of the javascript that was added here so we do have responsiveness right here if we move that in our participants can be opened up by toggling this right here and we can also toggle the chat by clicking this right here so we're actually going to build in this section in the middle right here for the actual stream right here this is something that we're going to take care of and actually hand code out i just left it empty for now but that's what this looks like right here pretty simple template so we'll just review some of the code here and actually take a look at the ids and classes that we're using and how all this was done so let's go ahead and start with room.html i'll try to zoom out a little bit here so we can see that we have our header bar right here that's repeated on both pages right here not much to it we're not really going to mess with this section we can actually leave it alone if we go down to our container we have a room container that encapsulates the entire room and we have a section for our members right here so this members container actually goes down pretty far if you can see this line right here it actually goes down pretty far with the demo users that we currently have in here and if i minimize this let's go ahead and minimize just this section right here so we have the members container and right after the members container we actually have the stream container so this is that middle section right here so this is that stream container let's just see what we have in here so we have some buttons we have the stream actions with buttons right here we have svgs for the actual icons we can minimize those stream actions we'll minimize the stream container and then we have our messages container right here so that's an id that wraps the actual messages and then each message uses the class of message wrapper so if i scroll down we're going to see a lot of repeated messages eventually we're going to get rid of all this dummy code here at least this stuff right here this will be auto-generated when users type in a message but if i minimize that there we go that closes this section out and here's the entire page here so our navigation bar main container room container we have a members container stream container and messages so left side middle and right so if we go to the lobby let's go ahead and check this out i'll minimize the head tag right here and in here we have our navigation let's go ahead and actually fix that i want to delete something and screw something up right away so we'll minimize the header then we have our main container which is the room lobby container here we have a form container which is going to make up this section right here that actually contains the entire form there's some styling there and if we minimize this section i guess i don't need to minimize it so that's our header and then we actually have the actual form so lobby forms the class we have our field wrappers and then the actual input fields so not much to the template right there we just have some styling that's in styles.css we have a main.css file this is going to be the entire template code we're not going to mess with this at all actually this is just our base code in our navigation bar basically any code that's repeated now any code that's associated with the lobby and room let's go ahead and just save those i hope i didn't delete anything inside of the room right here this is any code to the actual room here we have the styling for our members the participants the messages all that sort of stuff here and inside of lobby.css our form container and so on now i mentioned javascript here so we do have a little bit of javascript for our room and our lobby so inside of our room we need to make sure that messages as they're added are always placed at the bottom here so we did add a little bit of javascript to actually make sure that when someone types this scroll bar automatically goes to the bottom so we want to see the latest message here so here we have some functionality that actually scrolls and make sure that the message is at the bottom and let's see let's just kind of go over the chat container here we have the functionality to open and close that chat container and then also for the participants right here so the members container this is that functionality for that responsiveness we added something that can toggle this and make sure that it opens and closes so that's really the entire template right here you can kind of go through that familiarize yourself with everything not much that we're going to do here in fact we won't touch this at all and any code that's currently in here we're not going to modify anything we're just going to simply add to it so let's just go ahead and get started and what i want to do is go into the room.html page and i want to start by making a container for our video frames and then the actual large display that's giving the frame that takes up that focus of the entire page so all the streams will sit inside of this section right here so stream container let's go ahead and open this up we'll minimize this and then let's just go ahead and add this just above stream actions so we have our controls on the bottom i'm also going to minimize those right there we'll create some space here and we're going to create a div and this div is going to have the id of streams underscore container or double underscore so make sure there's two underscores right there and inside of this section we're going to create another div and these will be the actual circles that you'll see so each circle is going to have a video double underscore container class okay so we have a video container class and then the actual containers themselves will have ids this will be how we know which containers associated with which user and when to actually add in a video frame so we need a way to actually identify them so for now let's go ahead and give each user an id we'll just do user dash container and this user will have the id of one so later on this will be dynamic but for now we'll just do one and then we'll just increment those so we have the actual container now we're gonna add in a video element here later but for now let's just add in an h1 tag so we can actually see it and then we'll just go ahead and throw in the number one so let's go ahead and create some space here and what i'm going to do here is copy each container and i'll just duplicate this a few times so let's just go ahead and copy and paste a few change the id to 2 the actual value to 2 here and then we'll do three three and four so we're gonna go ahead and style this so we can see it and here we go we have our stream container or streams container and then the actual video container now above this whenever we click on one of these streams right here just like just like i showed in the demo we're actually going to take that video stream and then present it into the big container so let's say someone's sharing their screen or you want to put focus on someone this is going to be that section where we actually add them in and display them in a larger frame so let's go ahead and create the div here and this id will just be stream whoops made this outside of the quotes here we'll do stream double underscore box like that okay so that's all we need for this section let's go ahead and just check this out let's display it and here we go we have some elements here we don't see much here so let's go ahead and just style that so we'll go into our room.css file so inside of styles we'll go to room.css and let's just go ahead and find where we want to put this so i want to put this underneath the stream container section to kind of keep that hierarchy so we have the main container and we'll just add the styling here so we have an id there so we'll just do stream double underscore box and in this section at this point let's just go ahead and add in some background colors so we can see it and for the color i'll just pick one of these i guess let's just go ahead and grab that color and let's just set a height of 60 viewport height okay so let's just take a look here we go so that's going to represent that big frame right there and then we actually want to style these video containers so let's go ahead and underneath here let's just grab the video double underscore container section a little bit slow there okay so we have the video container we have the actual circles here let's grab that and let's go ahead and start styling this so the first thing is i have that number inside of them so let's just go ahead and make sure that's centered so we'll do display flex and let's just go ahead and do justify content we want to make sure it's centered this is technically not really relevant to what we're doing but let's just go ahead and center it so even the demo looks good align items let's center this horizontally and vertically and then for the actual item let's go ahead and set a border we'll set this to two pixels solid and then i have a shade of purple that i want to use it's going to be a hex and this is going to be b 3 6 6 f 9. so that should give us a purple shade right there okay so we should see output let's just make sure it's connected here we go so they're not circles yet but we'll fix that now underneath here let's go ahead and make that a circle we'll just do border dash radius and we'll set that to 50 that'll give us a circle and then underneath that let's go ahead and do cursor pointers so we want to make sure users do know that they can click on that to expand it so we want to make sure it's clickable and that will visually show someone that that is and then after that let's just go ahead and set overflow to none or let's just do hidden so the video frame itself will be a square and we want to make sure it doesn't actually overflow from this container so we want to make sure anything that overflows just gets cut out and let's just finally end this with a height of 300 pixels and a width of 300 pixels and we'll make sure this is responsive too okay so we have a height and width here and here we go we have our circles so now we want to style that container and actually put these in line so they're actually correctly placed inside of that frame so i'm a little bit zoomed in so that's why it looks funny normally on a screen you'd see it maybe around this width right here but because i'm so zoomed in it's going to look like they're being crushed down so what i might have to do is make those responsive a little bit sooner than i planned so let's just go ahead and start with the container and we'll just see how that looks so now we want to style this actual streams container that holds all those circles so we'll just do streams double underscore container and let's first display it as flex i want to make sure it's a flex and then we'll do flex wrap so whenever they whenever it starts collapsing in on the circles they actually go down and actually wrap the element so do flex wrap set that to wrap justify content let's just set that to center and align items let's also center that see why do i have space here okay so i do want to set a margin at the top let's just go ahead and give some space so margin top let's set that to 25 pixels and then margin bottom the reason i want to set some margin at the bottom we'll actually do 225 pixels the reason why i want to do this is that if we start zooming in or let's say we get a lot of frames if someone scrolls to the bottom if we have no margin here the buttons will actually cover that person's video frame so we just want to make sure that we actually have some space here so that's why i added that now i should actually add in a gap here so let's do that too so let's just do gap create some space between the users set that to 2 em and there we go okay so let's crush down here i want to make sure this is responsive so this is what it would actually look like right here the demo always looks funnier when i'm videotaping okay so let's go ahead and actually update this so let's see the video container let's create our own media query right here so let's just copy and paste that and i'm going to set this at 1400 pixels so what i'm going to do is grab this video container copy and paste that in because i'm too lazy to type it out and let's just update the height and width so whenever we're at 1400 pixels we'll set that to 200 pixels in width for the video container and then let's just go ahead and copy this again and then at around 768 pixels i just want to go ahead and make these 100 pixels in height and width okay so let's check this responsiveness out so if i start crushing that down there we go it starts shrinking and then they just slowly adjust based on whatever the current width is so i'll just keep the zoom at about right here i think that's a good size if anything i'll just reduce them down to 200 pixels permanently and we'll leave those at least for this video okay so now that we have our template ready it's time to move on to displaying our video stream to the page and also connecting to other members inside of this video chat application so we want to see our own face on here and our peers so let's talk about how we're going to do this so in the first part of this video if you built out that webrtc peer-to-peer chat application i talked about how we can scale webrtc to group video calling as opposed to just peer-to-peer and i mentioned the three main approaches and we have the mesh network that we can build out that has a lot of cons it's very limited and we also have the sfu method and mcu method now instead of going into all of that and building out our entire network what we're gonna do is use a third-party service called agora that builds on top of webrtc and agora provides us with a lot of functionality to actually build out and scale our applications so with agora we're going to create an account download an sdk and then just use the agora sdk to build all the video part of this application out along with the real-time messaging so this chat box participants that's all going to be real time using agora so agora is a paid service they do have a free tier to it we have up to 10 000 free minutes that we can use when we get started so that should be plenty for this video to actually build out your demo so let's go ahead and get started you don't need a credit card on file so we'll just go ahead and get started with agora and i'll just teach along the way so let's go ahead and go to agora io the first thing we're going to want to do is create an account so do that we're going to create an app on agora and then download and connect the sdk so let's go ahead and go to login once you're logged in and you have an account you'll have this console right here and in this console we have our projects here so go ahead and go to more create a new app here so i already have a few i'm going to use one of these but at this point go ahead and create an app and go ahead and give it the name of something like mumble 2. for the use case we'll do social live stream i guess we can just do chat room and then for the authentication mechanism go ahead and select testing mode app id if we select this method right here that's going to require us to have a token that we have to keep refreshing so we don't want to deal with that right now let's just go ahead and authenticate with only an app id so once you have this set up go ahead and click submit i'm not going to click this because i already have an app so go ahead and submit that and then you're going to see your app right here so what we're going to need is this app id so you can actually click on this and that will just go ahead and copy that app id so we'll get back to this in a second so at this point once you've created your app go ahead and go back to the main console and you should see this download section right here so agora has two main sdks we have let's see we'll actually want to select web here so let's go ahead and do that get this out of the way so agora has rtc so that's one of the sdks that stands for real-time communication which will give us video and audio and then we have rtm which is for real-time messaging so we'll use that one later but for now let's start with video sdk and select four point x so there's a three point x here we wanna make sure we're at four point x go ahead and click on this go into this section and we're gonna want this file right here so i'm going to minimize this and i'm going to drag this into my desktop and then i'll drag it into the mumble 2 folder so that's the sdk we're just going to want to go ahead and download that and then connect it now at this point i'm just going to grab this sdk and drag that into my javascript folder i just wanted to move that in there i'm kind of doing things manually but i like to keep it very simple so if you're more advanced you can use your more advanced methods but this way everybody can follow along so once we have the sdk let's go ahead and bring that in here copy and paste this script tag right here and we'll want to make sure that we take this agora sdk let's just copy the name and paste this above this room.js script tag so we'll want to make sure that's above it and now that we have the sdk let's go ahead and actually create a javascript file specifically for all the logic dealing with agora so at this point inside of the javascript file we're going to call this room underscore rtc rtc.js so that's the agora sdk that's agora rtc so at this point we're creating a javascript file that's going to have all of this logic with agora so the first thing we need is our app id to actually use an app with agora so we'll just go ahead and create a variable called app underscore id and in here we're going to need to get that app id so we can actually authenticate our user so back in your console let's go ahead and actually go to the console itself so we'll actually go back in here and we'll find our app here so go back to your projects and go ahead and grab the app id here that you just created so go ahead and copy that i'm obviously going to blur out my app id because if somebody gets this they can start using it and then use up all of your minutes so let's go ahead and copy that app id we'll bring that in here and then i'm going to hide this later but make sure it's at the top of your page and just paste that in and that's how we connect the agora sdk to our project right here along with the app inside of our console so inside of this room underscore rtc js file let's go ahead and start configuring everything that we need here so we have our app id and at this point what we need for each user is a uid so each user has to have some kind of identification here so what i'll do here is we're going to generate a user if there's not one inside of session storage so the first thing i want to do is go ahead and go into session storage and we just want to do dot get item and we want to get a value of uid so at this point we don't have this user so we're gonna run a check here and we're just gonna say if we don't have a uid so we're gonna use the not operator let's go ahead and just generate a uid so we're gonna set this variable right here so we either have nothing or we're gonna create it and we're just going to do string so we want it to be a string value and we're just going to generate a random number between 1 and 10 000 so later on if you had a full scale application this can be like a uid in the back end or something that actually connects to your user so it's more permanent but at this point we're just going to generate a number and i'll leave that other part up to you and actually i'm going to have a lot of videos on my channel about how to do this so just go ahead and check those out and i'll actually scale this up so anyways i'm going on a side tangent okay so we're just going to generate a number between 1 and 10 000. so if we don't have a uid in the session storage go ahead and generate a uid and then what we want to do is go ahead and take this newly generated uid and we're just going to do session storage dot set item so we get an item with get item and then set it with set item we have the key and then the actual value which is going to be the uid so each user is going to need a uid this is how we know who's who in a stream this is how we can identify each other so once we create a uid we'll store it in the session storage and now we don't have to create a new uid every single time a user goes back to the page so let's go ahead and set a few more values we also need a token and in production this is how you would generally authenticate users with agora we have a token that we would actually generate but for now because our authentication mechanism was set to app id only this is going to make this process a little bit easier so we can focus on the core functionality of agora and get our application working and i actually do have a few videos on my channel on how to do this specific thing with the token but we're not going to worry about that right now so after our token we're going to need a client object at this point we're just going to set the variable we'll leave it like this and the client object will be like that core interface for all the functionality for audio and video streaming it's going to store all the information about our user and basically any functionality that we need to actually start this video stream with so we're not going to worry about this until we actually start coding this up so the next thing i want to do is make sure that we can get a room id from some kind of url parameter so our url will look like this will be room id and then room and then let's say our room name will be some kind of number like that let's just do 234. so what we're going to need to do is go to our url and get this room value and then find out what our room is going to be named so let's go ahead and get this value we're going to need to parse that url so let's go ahead and first get the url value we'll get the query string we'll do window.location.search we first want to get this value then we want to set the url parameters value and then get the actual room value out of this so we'll do url params and this will be new url search params and we're going to throw in the query string into this so we have the query string and then we can actually set the room value by doing room id is equal to url params.get and we're going to look for this room value so let's just go ahead and pass that in and this is how we create custom rooms this is how we can actually have multiple rooms inside of the same app right here so what i'm going to do at this point is if we don't have a room so if not room id we're going to use a not operator typically we could just redirect our user to the login page but at this point i'm just going to go ahead and set a room id for this purpose so we're not having to constantly create a room id in the url and we're just going to call it main so if we don't have an id in there we'll just go ahead and call the room main and that's it so we'll change this later okay so we have our room id we're just setting up some core code here and what i want to do here is go ahead and set some variables for our local tracks here so local tracks this will be our actual audio and video stream we're going to store this inside of an array here now we also want a place to store remote tracks so when users join a stream we want to get their audio and video streams and also store them in a variable here so we'll do remote users and this will be an object here so we'll have like a key value store where we'll have like the user's id let's say the user's id is 54 and then we'll just have like the audio and video track like this which will be automatically created for us so we're not going to worry about that but this is why we're going to make it an object so it can be key value pairs okay so now that we have the track set up our room id and url setup let's go ahead and actually get going with having a user join a room and displaying their stream here so first let's set up a function called join room init and let's make sure it's an async function and in here we're going to go ahead and make sure this function triggers right away so on load we're just going to call join room init and the user will join the room so join room init we're going to create the function trigger it right away now in here we're going to take that client variable that we just set up right here and we're just going to go ahead and call agora rtc dot create client so we're going to use a create client function and the reason why we have access to this is because of the sdk that we downloaded and added right here so we're calling agora.createclient which will create the client object and in here we have two required parameters so first we have the mode which can be live or rtc and this is simply the optimization algorithm to be used and then we have the codec here which is simply the encoding method that the browser will use for encoding so we'll set codec and this will be vp8 okay so first piece of agora that we use right here are the agora sdk and at this point what i want to do is reference the agora documentation and i just want to make sure that you know where to find everything so from your console or even from the agora website we have the documentation here that you can follow so go ahead and select the platform that you're using or the product we're using video calling right here select the actual platform so we're using web and here you have a bunch of documentation along with the api reference here so here you have it again select the platform and at this point we're using video calling use the 4x documentation and go ahead and look at all the methods here so right now we just called create client if you have questions click on it read about it and here are going to be all the functions and everything that we're doing here and this will explain a lot more so i'll go over what i can and i'll explain the functions as we're using them but i would highly recommend looking through and actually studying up the details here so i'm also going to link this up in the video description so make sure to check that out here okay so let's get back to it so we created a client object and what i want to do now with a client is actually join a room so we're going to go ahead and call await here and using this client object we're going to call client.join so this will actually join a room here so we need to use some credentials here so the first thing we need is our app id so we set the app id at the top let's go ahead and use that now then we want to go ahead and throw in the room id so what room do we want to join and then we want to go ahead and pass in the token so token at this point is null so we can leave it just like that we set the variable right here so we have the token and then we want to throw in our uid so this is how we join with a specific user so we joined a room and we're ready to go so let's go ahead and save this and let's just make sure that everything works so far so i'm going to go ahead and click inspect element and if you're seeing something like this it means it's working well agora is going to consol console out a few things for us so that means we're connected and ready to go so now that we joined a room i actually want to display our video stream here so what i'm going to do is create another function that's actually going to output our stream so we're going to get our camera feed or camera and audio and then actually display it to the dom so underneath here let's go ahead and create another function we'll call this join stream so we joined the room but now we want to join a stream as like another presenter so we'll make this an async function and let's just go ahead and continue here okay so we have the join stream function which will be called on the initial room joining here so later on we'll actually join through a button here we'll actually click a button to join the actual room as a as a presenter but we'll call it on load right now and let's just go ahead and continue here so when a user wants to join we first want to go ahead and get our audio and video tracks now i think this should say tracks here so let's make sure that's plural let's add an s i thought that looked a little bit funny and let's just go ahead and call await agora rtc dot create microphone i'm going to type this slowly some microphone and camera tracks okay kind of a long word there okay so create micro phone and camera tracks so we're going to call this function and what it's going to do is it's actually going to ask a user if this is your first time on the site it's going to ask you for access to your audio and video feed so make sure you have some kind of webcam added to your laptop or if you have a desktop make sure you have a webcam and this will actually trigger that and request that permission on the first load so it's going to get your audio and video tracks add it to local tracks and this will be stored in an array so that's why we added it like that so we're going to get that feed and then what we want to do is create an actual video player so we're going to go ahead and do player and we're going to use backticks here so we can actually add in some dynamic values and let's just go ahead and grab one of these video containers so we'll grab this right here and we're just going to paste that in and let's just go ahead and format it so at this point we don't want the h1 tag in the middle what we do want to do here though is make sure that the id is based off of our user so we want to remove the one and then we'll just pass in our uid now inside of this div what we're going to do is go ahead and create another div for the actual video player and this is where that player will be added so we're going to create a div here and this player will have a class of video dash player so we'll do video dash player and then we we also want a custom id sorry my voice squeak there we want a custom id and we're going to call this user dash and then the user's uid so this way we actually have some unique values that we can actually access so we're going to create like the container for the video frame it's not going to be in here just yet now once we create that container where we're going to store this we want to go ahead and add this video player to this streams container right here so we'll just do document dot get element by id we'll get streams underscore container double underscore and then we'll just do insert adjacent html and we'll just throw in the player before end and then we'll actually throw in the player as a second parameter so that's going to take this player right here that we created and add it to the dom here so we're simply going to add it and once we do that we want to go ahead and actually play this video stream right here so we're gonna call local tracks and the audio track is stored in index zero the video track is stored in index one so we're gonna get the video track and we're gonna call the play method and what this play method needs it's actually gonna create like a video tag for us and it needs a place to append that actual video stream here so what we're going to do is go ahead and append it to this div right here so we're going to take this value so user uid make sure you're using backticks right here and we're just going to call play and that's going to add in that video stream into this video player so let's go ahead and make sure that this file is linked up here so let's just take this script tag right here copy and paste it and then add in underscore rtc so we first want to make sure that this file is connected let's save that let's save this file and we're going to run into one small issue here so i want to show you it and then we'll fix that so let's go ahead and open this up here so at this point on load we're going to see our 4 and then our fifth circle once that user joined so we call this function and we added this player to the dom but we're not seeing the video stream here so one thing also i guess i wanted to show you something else here let's go ahead and actually deny access to our camera feed so that way you can see what it might look like on the first load here so let's go ahead and move all these websites out we'll just remove those and then if i refresh this on first load it's going to ask me for access to my camera we'll click allow so if you're doing this for the first time you'll see that and here we go so that actual video element doesn't have a height and width so we want to make sure it actually fills out that container so we'll go into our styles room.css and let's go up here inside a video container and let's just go ahead and style this up so just below video container we're going to get that video dash player value and we're going to set a height and width so we'll do height 100 percent and then we'll just copy and paste that we'll just do with and then we'll just leave it at 100 so i forgot the h there so with that we also want to make sure the video tag is selfish style so we'll do video dash player and then we'll get that video tag let's get this value and then we'll just do border radius so we want to make sure it doesn't overflow just in case we also want to make sure that that's cut off okay so let's try this one more time so we added a height and width to this video player let's make sure that that's saved so now when we actually add that item to the dom we should see it okay so here we go if i refresh that again we got access to the camera it takes a second and there we are so that looks good we're actually able to display our stream so if i add in another tab here let's go ahead and load that up we're also going to see that video stream but the two users can't see each other so we want to start adding that in next here so what i want to do is actually hide this display because eventually we're going to be able to click on these frames right here and add whatever frame that we clicked on to this big stream here so let's go back into room.css and for the stream box at this point let's just do display none and then it's going to look a little bit better and there we go so let's just go ahead and move on to the next step let's go back into the code so for this next step what we're going to want to do is actually publish our stream and then create a function that's going to handle the event whenever another user publishes a stream so right now we're simply taking our local track and we're playing it but we're the only ones that can see it so we need to publish this track to the channel and then anytime another user joins and publishes their track we're going to need this function to run as like a side effect and then take that track from that user and display it into our stream so this is how we're going to get remote users and view them inside of our browser here so let's go ahead and create the function that's going to handle this so this will be handle user published so we'll create the function let's make sure it's an async function and for the actual parameters we are going to get the user value so the actual tracks here from the user and then the media type so media type here and this function is going to run as a side effect so what we're going to do is go ahead and place this function inside of the join room init so we're going to create like an event listener and this is going to take this client object then we have an event listener so we can go ahead and call on user dash published so anytime a user publishes we're going to listen for that and we're going to call handle user published so this is the function that's going to trigger whenever a user publishes now let's go ahead and actually build out the function and then we're going to publish our track and we'll actually see this in effect so when a user publishes what we're going to do is go ahead and call remote users so this object right here that we set up right here we're going to go ahead and get that and we're going to add the user to this track so we'll go ahead and call user.uid so we get that user's uid and then we'll just throw in the entire user object now once we add that user to the list of remote users or i guess this key value store of remote users we're going to go ahead and subscribe to that user's track so we can call client.subscribe and then for this we're going to subscribe to the user and then we'll just throw in the media type right here so we're going to subscribe to that users track and then what we're going to do next here is create a player for that user and this player will be the exact same player that we have right here so let's copy this value right here let's paste that in and we are going to need to update a few things here so let's just go ahead and change this uid to be the uid from the user that was just added here so we'll just change this to user.uid and we'll do the same here we'll just do user dot uid so we have the player then we also need to append that to the dom so let's just grab this we'll bring this down here paste that in we'll fix the indentation and there we go we just added the player now when we add a new player we also want to make sure that this player doesn't already exist in the dom just so we're not having duplicates so let's go ahead and actually make a query for this player by the user container and then if this player doesn't exist then we'll add it so let's go ahead and remove this value right here so we'll remove the let right there and we'll actually set the variable up here and we'll just do document.getelementbyid we'll just take all of this in right here let's go ahead and copy that let's use backticks right here paste that in and then we'll just ask a question here so we'll just say if the player is equal to null so if it doesn't exist let's go ahead and create a new one so we'll just finish that statement here and we'll just copy all of this and bring this in here so that's just making sure that we don't have any duplicates and i know it's a little bit repetitive and now i'm just copying and pasting a bunch of stuff so we check if the player exists if the player doesn't we just go ahead and set this value grab that element and append it to the dom so we add the player to the dom but now we need to actually play that track so let's go ahead and check the media type so we're gonna have two media types come through we're gonna have an audio track and a video track here so let's go ahead and first check the media type we'll just do if media type is equal to video then what we'll want to do here is go ahead and take that user object access to video track parameter so video track and then we'll just call the play method here and in here we'll use backticks and we're just going to append that video tag to the user dash and then the id value here so this is the same exact thing that we did right here for the local track where we actually create a video tag and then append it to this tag right here into this div so we're doing the same thing for the remote user at this point we just call user.video track and then play so we also have the media type of audio so we'll just go ahead and get that we'll do if media type is equal to audio then let's just go ahead and play the audio at this point we're not doing anything with the dom so we'll just do user dot audio track and then we'll just call dot play and that'll just play the audio okay so we have the listener for or the event listener for the user published event and now what we need to do is go ahead and actually publish our local tracks here so whenever a user joins the room we call join stream and we have our local track so the last thing we need to do here is actually publish that track so we'll just go ahead and call await then we'll do client dot publish and we need to specify the tracks that we want to publish here so we're going to throw in an array so we can publish multiple tracks and at this point we're gonna publish our local tracks audio first so audio is index zero so we're publishing the audio track and then we're gonna publish the video track right here so we're gonna do index one for video and then audio right here so we're publishing those and this function right here is what's going to trigger this event listener so whenever we call publish every other user inside of the stream is going to go ahead and trigger this and then call handle user published and that's what's going to trigger this function right here so let's go ahead and test this out i'm going to save it we'll open up the browser here okay so we see our track in here and at this point what i'm going to do is go ahead and just copy and paste it and we're just going to go ahead and back to this user and we're going to wait a second and here we go now we see another track and for the remote user we also see this track so i'm using the same webcam if i go ahead and actually publish more we're gonna see three right here so those events are firing off and everything's working good we're actually seeing remote users here now we do have one issue and that is i actually hear an echo right now so i can actually get my headphones and bring that to the speaker i don't know if you can hear that so essentially whenever a user leaves we have nothing that tears down this element from the dom so right now if i close this one out we're gonna see this go black here because the video tag is no longer functioning it's not published anymore and if i close this one out here we go now we just see ourselves so we need to handle that event of not only publishing a track but also removing that track from the dom whenever a user leaves so we're listening for the publish event let me just refresh this so we're the only ones in here okay there we go so we're listening for that event but now we want to go ahead and just remove that element so we're going to add in another function here so we'll just go ahead and create a handle user left function so we'll do handle user left so that's gonna be the name of the function it'll be an async function and at this point we're just gonna throw in the user object here so we know what user left and we need to add in an event listener so let's go ahead and actually go up here to the join room init function we'll copy this and the client object has another event that we can listen for and at this point this is going to be for user left so whenever a user leaves we're going to call handle user left so this is the function that's going to respond to that leave event now let's go ahead and bring this down here and let's finish up this function here so in this function what we want to do is first delete the user from that object of users so we're going to go into remote users and we're just going to go ahead and delete user dot uid so that's the key we're going to remove that user and let's see what did i not do here oh i didn't finish the arrow function okay so there we go so we're deleting the user and then we just want to go ahead and remove that user's frame from the dom so we'll do dot get element by id and we're just going to go ahead and get the user dash container we'll throw in user dot uid okay so here we go let's go ahead and give this a test so we'll save that we'll go ahead and add in another user give it a second to load this user in we'll add in a second and then a third okay so we publish the users if i go back to this first one and close this out here we go oh i forgot to call remove okay so give me a second we'll just do remove okay so we're simply removing that element from the dom let's go ahead and save that we'll add another user here and if i click x here there we go and there we go so now we can publish a track and we can also remove that track and if i join with another user it'll obviously do the same thing so the next thing i want to do here is focus on actually presenting these video frames into the big stream here or into the big frame so right now it's hidden but at any point let's say a user is sharing their screen or i just want to focus on let's say user number two here that's talking i want to be able to click on this user add them to that big frame and then switch between which users in that frame along with also removing them so let's go ahead and actually move on to the next section and before we get started with that i want to go ahead and actually remove some of these containers here so we'll just remove container three and four so i just wanted to make sure that whenever we add a new frame into the dom we're not having all of those placeholder elements taking up the space so we can actually see it so that just looks a little bit cleaner so let's go ahead and move on to the next step here so we'll close out room.css and let's just open up room.js so anything that has to do with the dom and click functionality we'll leave that in room.js anything to do with the agora rtc sdk we'll leave in room underscore rtc so let's keep these files separate and in here let's just go ahead and scroll down from all of these events here and let's just set some variables to start with and then we're going to build out a few functions that will actually handle the click events for any time we click on one of those video frames so the first thing i want to do is actually go into room.html and i just want to query all the stream actually let's see i want to query the stream box right here so i can actually add event listeners to that and append elements to this div right here and i want to get all of these video containers so we can add in click events to them so let's go ahead and call this display frame i guess that's the most uh descriptive thing i can think of it's just a display frame so it's the one that's going to display the main element so we'll just do document.git element by id and this will be stream underscore box or double underscore so then we want all the video containers so we'll just do video frames and this will be document.getelementbyclassname get elements by class name so elements by class name and we're just going to get all the video containers double underscore container let's make sure that's the name of the class here so that's all of these containers so that's going to be an array of html elements or a collection and then the next thing i want to do is set a variable for the user id in display frame so anytime we click on an element i want to know what user is actually inside of this display frame because we're going to take this element put it into this box right here and we just want to know what the current id is and there's a few reasons for that but before we have a user in there this this value is going to be null so we're going to leave it as blank but at some point we want to know which item is actually in there so now what we want to do is go ahead and create a function that will be in charge of expanding a video frame and adding it inside of this div right here whenever we click on it so this will be called expand video frame we'll go ahead and create the arrow function pass in the event here so finish up the arrow function and essentially what we're going to do in this function is we're going to first check if we have any item inside of the stream box if there is one we'll remove it and add in the new frame that we just clicked on and then we'll change the actual styling of this stream box so right now in the css you don't see it because we have it as display none so we'll be this function will be in charge of actually changing that styling and then moving an element in or out of that stream box so let's go ahead and actually first go ahead and get that display frame we'll just do display frame dot style dot display and we'll just do block let's see if i can write display here so we'll do display and then block so first we'll make sure that we can actually see that element so if i save this let's actually go ahead and add the event listener to all these video frames so we'll loop through all the video frames so we'll just do four finish up this for loop we'll do let i is equal to zero then we'll get the length of video frames i'm one of those people that writes my for loops like this so i like to keep it old school so while the video frames is less than i let's go ahead and increment i and let's just go ahead and add the event listeners so we'll get each video frame get the index right there to add event listener and we're just going to say on click let's go ahead and expand the video frame okay so this right here is actually going to add the event listener only to the frames that were already there so i'll show you what i mean by this so when we're here if i click on this event it's going to expand that video frame that's all the function does but if i refresh that and i wait for a new video frame to be added if i click on this nothing's going to happen because this item was just added and that event was not added to it yet so we'll actually deal with that in a second so right now only one and two right here can actually expand that frame so let's continue here so we're going to call expand video frame and the first thing i want to do is check if we have an item in here so right now we're just displaying it as block that's actually going to be the next step here so let's go ahead and check for a child element we'll just do child is equal to display frame and we'll just do dot children get the first index so we want the first child which would be that video frame we'll say if there currently is a child so we'll get the child and we're just going to take that child take them out of this stream box right here so we'll remove them from this element and we want to stick them back into the streams container so we'll say if child let's go ahead and do document dot get element element by id we'll grab the streams double underscore container make sure there's an s right here so streams not stream i know we have two ids here so i want to make sure that or two ids that have a very similar name so i wanna make sure we don't confuse that yeah so we have stream container and streams we're adding that to here so we're taking that streams container and we're just gonna do dot append child so this function will take that child element and add them back into the stream so it'll remove it from that mainframe now what i want to do next here is go ahead and take the current item that we clicked on and add them into that main stream box we're basically going to reverse what we just did right here so at this point we remove any item inside of that stream box down to stream containers but the current item needs to be placed into that main stream box now so we'll just do display frame dot append child so we're gonna call this function again we'll go ahead and get e dot current target and i know sometimes e dot target works but for some reason it was clicking on the actual video tag and it was placing the wrong element in here so if you want to follow along exactly make sure you don't get an issue go ahead and just do e dot current target so we're taking the current element and we're adding this into that display frame and what i also want to do is go ahead and take that current user in display or that user's id and we'll just do e dot current target dot id so now we know what item is in this display frame so let's go ahead and actually test this out here just bring this down here remember the actual video frame that gets added this will not work because we have not added the event handler but if i click on one here we go we see one and don't worry about the size here we'll rework all that out so if i click on two it's going to take one bring them down here and it's going to place two in here now if i click on that or if i click on one it's just going to keep changing so those items keep flip-flopping we're removing an item and adding one in so the next thing i want to do here is actually shrink these items right here so whenever we have an item inside of this main frame i want to make sure that all these items in here are set to 100 pixels in height and width just so they have some room there and it looks a little bit better i think that way so we'll just go ahead and create another for loop here so as we expand an item we're just going to loop through all the items here so let's copy and paste this loop right here just so i'm not typing that out again and we're just going to do video frames i and we'll just do dot style dot height not scroll height but height like that and that's going to be equal to 100 pixels and we're going to do the same to the width here so we'll just do with and there we go so the only issue that occurs here is the fact that we don't want the main item in that display frame to have the height and width of 100 pixels we actually want to expand it and make it large so these are shrinking but we want to make sure this item stays big here so let's go ahead and add in a quick question we'll just do if the current item in the display frame so we want to check this condition let's actually copy and paste this into the if statement so we're just going to go ahead and say if the video frames id so the current id value if this is not equal to the user id and display frame the value that we just set right here then go ahead and change the height and width so let me go ahead and just try this again we'll click on one two and there we go so right now the height and width is going to be like that because it actually adjusted it because we clicked on it at some point but if i click on it for the first time one stays the same while these are actually shrunk here so let's go ahead and actually add this to this video frame now so we'll go ahead and add this event so we're going to take this expand video frame event we'll bring this into room underscore rtc here and we're just going to call this as we append this item to the dom here so we're just going to go ahead and grab the currently newly added item here so on handle user published actually let's do this at join stream here let's start here so let's go ahead and grab this we'll just do document dot get element by id we'll just get the user dash container dash uid and we'll just do dot add event listener on click and we'll just do expand video frame so i just pasted that in so once we add the item to the dom go ahead and add the event listener so let's copy this entire method right here and let's just bring this or line of code and let's bring this down into handle user publish so anytime a new user publishes their stream let's go ahead and add this down here too so we want to make sure every single user has that now at this point we're going to have to do user.uid and i think we're good there okay so let's go ahead and just try this out okay so when a new video frame gets added we can click on this there we go click on one two and then that frame and let's just go ahead and add another user now we can just switch between these two you can see that's changing and all of that's working okay so let's move on to the next step so the issue that we're currently having is the fact that that video frame is actually small because we're not adjusting the size of that container so let's go into room.css and what i'm going to do is go ahead and find the stream box here so let's see we'll go into stream box and i just want to make sure that the current video container that has the height and width set right here we're just going to take this value and we're going to say anytime this item is inside a stream box let's go ahead and do this we'll just stream double underscore box let's go ahead and also style that stream boxes video container so at this point let's just go ahead and set the border to two pixels solid and we'll just change the color to black so we'll just do i believe we can do zero zero like that or triple zero and that'll work we're gonna set the border radius to zero so we just want to change that make sure it's not a circle anymore and we're also going to set the height of 100 percent or to 100 and we'll just use the important property there and we'll do the same for width so 100 and we just want to make sure that this happens so it overrides what we currently have there and poor can't i don't know why i can't spell that word okay so we set the height and width and that's it for this box right here but we also want to style the actual video tag here so we'll just do stream underscore box and we'll just do video and the video tag doesn't have a height and width so we're going to make sure that the border radius is set to zero like that so we want to make sure that it's not a circle once it's added so let's go ahead and test this out so we'll just go ahead and click on this actually if i just refresh it there we go so give it a second let that video frame come in then we'll click on two this one right here and that looks a lot better so now we're actually able to display the frame and style these elements so that looks a lot better so we still have a few things that we want to fix up here and one of those issues is the fact that if we've clicked on one of these video frames when that frame expands all these other streams right here actually get shrunk down into these small circles here so they expand and shrink but what happens is if we're currently displaying someone in that mainstream anytime we add in a new user that user stream by default is going to be set to a height and width of 300 pixels because we haven't restyled that new user we've only restyled what we currently have here so i'll show you what i mean by this so let's go ahead and add in a new user and when this user is added automatically their frame is set to 300 pixels in height and width now when i click on it it does adjust it so that's fine everything works out but we want to make sure that any time we handle that user joined event we fix that issue here and we also want to fix the issue of the current user in that frame leaving so let's go ahead and refresh everything and we'll go back in here inside of room underscore rtc.js and we're just going to bring this down into handle user published event so we're going to take that video player we're going to check if it exists we're going to add it to the dom and then we're going to run one more check so the check we want to look for is to see if we currently have any styling to that display frame so we're going to do display frame i want to make sure i'm saying that right so if we currently have a style to that display property then that means we're currently displaying a user there so let's go ahead and just change that so we'll take this player right here and we'll just do player dot style dot height and this is going to be set to 100 pixels so we'll do 100 pixels and we'll just do the same for the width so we'll just copy and paste that and this will be the width here so when they join if we're currently displaying someone set them to 100 pixels of height and width so let's go ahead and do this we'll actually display this user we'll display another user now when this user joins automatically this is set to 100 pixels in height and width so we checked is someone in this frame change that styling now for this other user because there's no one being displayed this is going to stay at 300 pixels now i want to show you another issue that's going to occur here so we're going to have to kind of bounce around here and fix up a few things so right now this is that remote user and this is the user in this tab so if i click on this user and they're displaying their stream if i close this user out so they leave the stream what's going to happen here is automatically that video tag is going to be removed but that frame is still going to be displayed out there so we want to make sure that we read which user left and if they were the ones that were currently displaying their stream we just want to resize all of these back to their original 300 pixels in height and width and just make sure that's fixed up so let's go ahead and just move back in here so this is going to be inside of handle user left right here so whenever a user leaves let's go ahead and see if they were the user that was currently inside of that main frame so we're just going to go ahead and check what that id was so we'll do if the current user id in display frame so we're going to get that id and we're just going to check if that was this current user so we're going to get this entire id so we we're about to remove it or i guess we did just remove it but we also want to know do we need to hide that mainframe so if that's the case if they were the ones in there let's just go ahead and do display frame dot style dot display and we're just going to set this to null so we're going to remove any styling that they have there and the next thing we want to do is make sure that all the video containers that were there all get resized back to 300 pixels so i just want to show you issue by issue okay so this is that remote user when they leave there we go we change the styling but these right here should be set back to 300 pixels so we just want to go ahead and resize those next so we're gonna go ahead and remove that mainframe then we're just gonna go ahead and get the video frame so we already have this value stored right here so we'll just get all the video frames actually i'm going to query it again because i just realized i need to get the new video containers so let's just grab this and we'll bring this back into handle user left we're going to get all those video frames we're gonna make that query again and we're just gonna loop through those video frames and resize them so we'll do let i is equal to zero and then we'll do video frames dot length we'll get all the video frames that are currently in there if this is less than i increment i and let's see we'll just do video frames okay each size dot style dot height and this is going to be set back to 300 pixels so that means the main user left we're just going to reset everything back to normal okay so let's go ahead and try this out so we'll just go ahead and open this up we'll make ourselves the main frame we'll add in a few new users now i'm just going to select this newly added user and if they leave i think it's this user there we go we just reset those video frames and that looks good now if we open one of these up i don't know which users what right now so i think this is this remote user click on that there we go that looks much better so we're actually resizing everything and just kind of moving things around now if someone's in this main frame i want to be able to click on this and actually shrink the frame down and set it back to where we were so i want to have the ability to remove frames without always having somebody in this mainstream so once we finish this little section up we're going to work on actually changing this video quality right here and then we'll just move on to the next step so let's go ahead and go back into room.js here and we're going to create a function to hide a stream here so let's just do hide display frame so not a stream but the main frame here and we're just going to go ahead and make an arrow function and what i want to do here is set user in display frame i want to set this to null so i just want to reset this value then i want to go ahead and get the display frame we'll just do style dot display and this is going to be set to null also so we want to set these values and we also need to grab the current element in there so we want to see if there's a current element in that mainframe which there is going to be if we're hiding it so we're just going to copy and paste all of this we're going to take this child and i guess we don't need to ask this condition because there is a child in there that's why we're hiding it and we're just going to add them to the streams container so we're going to move them down now at that point i just want to go ahead and loop through all those video frames let's just copy this right here inside of handle user left and i just want to resize all those frames back to 300 pixels so we're just going to loop through all of them resize them and that's it for the function so the last thing we need to do here is go ahead and do display frame dot add event listener and we'll just listen for a click event and we'll just do hide display frame okay so let's check this out let's make sure it's all working so we're going to open this up we'll open up a user we'll add in second user and if we're in here okay so it restyle that let's see why is that user not showing actually we're having an issue here so let me try this one more time so i'll click on this user we'll remove them and we'll add them again that might have just been a lag here okay there we go so it's all good so if i click on this i want to hide it and resize it so i can go ahead and toggle all these users i can go between them and i can also hide a user and reset all of this so that looks good i want to go ahead and just remove those placeholders right here we can remove one and two i just wanted to keep that there just to work on that toggle functionality so let's actually remove the second frame there we go so that's all in there now and now let's go ahead and move on to setting the video quality here so right now if you look at this frame it doesn't really look that good and if we actually look at the settings here so let's just go ahead and minimize that it's a little bit blurry here if we actually go to inspect you'll actually be able to see the video size here so right now the video dimensions are 640 by 480 and there's a lot we can do here we can actually set the frame rate set the actual dimensions here so for now i'm just going to set the dimensions of this and actually make this frame a little bit bigger so it'll look nicer so you can actually read about how you can do this in the documentation if you want to really advance from here and actually set a lot of like really advanced settings but for now let's just go ahead and go back into create camera and microphone track so this is where we want to set it and because we're setting microphone and camera tracks we can actually set two encoder configs here so we can do one for the audio we're not going to work on that right now so let's leave that as an empty object and then we're also going to set the encoder config for the video so the audio is the first object the video will be the second so once we have this object let's go ahead and add in encoder config okay so let me actually move a few things out of here so we can see this we'll do encoder config and then this will be an object right here so i'll move that down so remember we have two objects and then an object inside of the encoder config just make sure you set that up correctly and we're going to set up a width here now for the width we're also going to throw in an object here and we're just going to set the minimum frame here so let's see we'll do min and for this value let's just go ahead and set 640 for the minimum width then we'll just go ahead and set our ideal width so this is what we always want it to be we'll just do 9 20 and then the max width will also just be 9 20. okay so once we have the width we're just going to go ahead and move down we'll set a height here and we'll create another object we'll also set the min and this will be 480 and then the ideal will be let's see we want to have this at 1080 so do 1080 and then the max will also be 1080. so this is completely up to you i just want to show you where we can actually adjust this and i just want to make sure our video quality is a little bit better so an object here this wraps it everything looks good i think we're all ready to go and let's take a look at this so now if i open this up look at the video quality it's a lot bigger so if we do a before and after you can see the frame right here you can see the picture frame the outside and then you can see almost the entire bed but if i remove this we're going to see all that shrink and the quality will actually go away so let's go ahead remove it save it and take a look at this again i'll extend it and there we go so it's cut off so you can actually see that adjustment and the quality itself actually changed so i'll just go ahead and undo that save it and there we go so now what i want to do is actually go on and move on to setting up the controls the control buttons for muting our mic turning our camera on and off and that sort of stuff so all that functionality that you see on the bottom right here all these controls so let's go ahead and start adding some functionality here so we're going to start by going into room.html and i'm going to change these buttons here so right now if we have a class of active on a button it's going to be purple here so by default when we first join i want to make sure that our camera and audio is active so let's set that to true and then we're going to add in some custom ids so we'll take this class of active move that to this button and this is going to be our camera button so we'll do camera dash btn and we'll just copy this bring this down here this will be the mic button so we're going to add in some event handlers to these so we want to make sure we have a way to identify them this will be screen and this button right here will be leave so we're not going to add the class of active to these because they're not going to be active right away and this button eventually will be read the leave button so we'll just call that leave button here all right so let's go ahead and move on to the next step here so what i want to do is first toggle the camera actually i was going to toggle the mic first but i actually want to toggle the camera so you can see it because when i toggle the mic you might not hear it i'll have to just put in my headphones see if i hear an echo and we'll test it that way so let's start with the visual part first then we'll adjust it so to do this let's go back into room underscore rtc so we'll close out room.js here and i'm just going to move down to the very bottom here and we'll create that function here so we're going to create a function called toggle camera so in toggle camera we're going to throw in the event here we'll finish up the arrow function here and we're also going to make sure it's an async function here so i always like to do that just in case i need to use a weight somewhere okay we've got the async function and let's just talk about the logic here so the first thing i want to do is go ahead and just get the button so we'll just do e dot current target so that's the button that we clicked on current target so we'll get the button and then i also want to set the class to it so i want to adjust the actual active class so if we are currently active we'll remove the class of active meaning we're turning it off and then we'll just flip it and we'll change it up based on what the current status is so to check if we're currently muting the mic so muting is a word that we can actually use for the camera itself so it makes more sense for the mic but you can actually mute the camera which means it'll turn off the camera stream so for this we're going into local tracks and the camera is index one so we're gonna go ahead and say if index one so the video we're just gonna go ahead and do if it's muted we'll check the status of muted then we'll just go ahead and unmute it so we'll just do await we'll do local tracks one so we're grabbing the video track and we'll do set muted so this is how we can actually change the status and we're just gonna do false so if it's currently muted we're gonna unmute it so false meaning it's gonna turn on the camera with this we also want to change the class of the button so we're going to do button.class list dot add and we're just going to add the class of active so this will make sure the button's purple and then we'll just add in else and in this section all we're going to do is simply invert this right here so we're going to set muted to true that means that if we are currently muted then we're just going to go ahead and or if we're not muted we are going to mute it so we're going to set media to true and we're just going to remove the class of active so that's going to change the color of the button so that's all we need to do there i'll save that and then the next step here would simply be to add in an event handler so let's just go ahead and do document dot get element by id we'll get the camera button camera dash btn and then we'll just do dot add event listener on click so we'll click the button let's go ahead and toggle the camera so let's go ahead and test this out so we'll open this up here so i'm going to bring that into frame we'll click it so right now you see that the purple was removed and you don't see the camera if i toggle that again purple's added and it just switches so perfect that's exactly what we wanted so to do this for the mic it's actually very easy it's the same exact function so we're just going to update this so we're going to take our toggle camera function and i'm just going to paste this above so we have toggle camera and this one's going to be toggle mic so we'll change this to toggle mic now the only functionality that we're going to change here is we're going to get index 0. so index 0 in local tracks is the audio so we're going to go ahead and check the same thing if the audio is currently muted we'll set it to false meaning we're unmuting it and so on and we're just simply updating the class and all we have to do here is simply add in the event listener now so we'll bring this down here and let's just go ahead and update this to listen for the mic button click right here and then we're just gonna do toggle mic for this one okay so one toggles the camera one toggles the mic and i'm gonna need to throw in my headphones here so i can hear the echo it won't actually capture it in a screen recording so let's give this a second and we'll just click actually here i need to add in a second user okay now i hear the echo and if i mute it and then unmute it perfect i can hear the echo it's all working and then i can also toggle the mic now one thing i want to work on here is whenever this mic is toggled see this black frame right here i want to make sure that we actually have some kind of background so it looks a little bit better than just having it black so if we have a bunch of people in the stream let's say they're just meeting their camera i want to make sure something is visible because it looks like something's broken and actually at this point it does seem like something's broken here i don't know what's going on here uh i'll fix this background first and then i'll see what's going on in that section okay so right now let's go ahead and unmute the camera there we go then we mute it again and all right perfect so to do this background right here what i'm going to do is uh go ahead and look into my images folder so we have this logo right here so this is the mumble logo it's actually the same logo that we're seeing up here so this logo that we have in the header bar we're just going to set that to the background color now i can't see it because i'm not sure why i can't see it but i guess that's irrelevant it's there let's just go ahead and actually go into our css and we're going to go into our video container now for this all we're going to do is set a background image to that video container so we'll just do background dash image and the actual image we just need to set the url so we'll do url and we're just going to point this to the images folder so we'll do slash images and we're just getting the logo dot png okay so we'll save that and if i go ahead and check this out there we go so we see a logo there and it's repeating it doesn't look that good so let's go ahead and just finish up the styling so we're just going to go ahead and do background repeat and we'll just do none for this or no repeat so we're going to make sure the image isn't repeating and i also want to make sure it's centered so let's center that in the middle so we can just do background position and this will be center and i also want to set the background size so we'll just do background size to 75 pixels let's do 75 like that and that should be good okay so now when a user mutes their camera we actually see something so if i meet my camera on this side this other user will see it and then if we unmute it there we go so we both see the extreme and then when we mute that's the output so what happens when i click on this so if i happen to click on that user let's say this user mutes it so if you mute it this logo looks a little bit small so let's go ahead and just make sure this is uh filled out to size so we'll just go up to stream box video container and if the video container is in here we'll just set the background size and make that a little bit bigger let's see we'll do background size and let's just set this to 300 pixels i think that should be a good size here all right so there we go so if it's in here it's large and if i move it down it's small down here that's perfect that's exactly what i wanted okay so i just took a few minutes and i tried to solve that issue that was occurring where when we started adding multiple video frames it looks like some of the frames weren't being added and it came to a few conclusions here and i really don't think it was an issue i think the problem was the fact that when the camera quality is so high it's kind of overloading this camera right here so that's kind of the theory because when i went into this section right here and i would highly recommend that if you're having this issue test this out and if you're still having it let me know maybe we'll see it in the comment section if i fix it at some point we can try doing something else and maybe i can add a fix for that but when i remove this right here and i just try to add on a bunch of streams and i think i got up to like 15 streams no issue occurred it was only when i added in this quality right here so when i set the height and width i think what's happening is it's overloading the camera and it's just making it hard for the camera to actually load up in time and it's breaking something so i think that's the issue again try just taking all of that out testing it if you're having it and if that doesn't fix it then i'm not exactly sure what to do we'll come up with a fix later but it seems like it's only because i'm loading it all in one camera so if you're working with remote users it shouldn't really be an issue because they're loading up their own camera so yeah i think that's the only solution we'll see kind of what happens here i'm going to leave it at the high quality if we see that issue we're just going to assume that that's what it is and we'll move on from there now there was one issue that i saw i noticed that whenever remote peers were being added we have this function right here let's see where was that inside of handle user published i think we were calling player.style and i realized until this item is added to the dom we actually can't do this so what we're going to do here is let this item be appended to the dom and then we're just going to go ahead and actually query and then update it so we'll just uh call let's see what i want to do here actually wrote this as a note so we're going to go ahead and get this video frame so we'll do video frame and we'll do document dot get element by id and we're just gonna grab this item right here and we'll throw that in just use the back ticks and there we go so let me just comment this out real quick and i'll show you what was happening so essentially when i expand the video frame and if i add another user we'll look at the console this is the issue that i'm seeing so let's add another user and when it adds this user it looks like it's saying no hi for undefined or that type of property um sorry i hear an echo so it's throwing me off but essentially uh we're not setting a height to a real element yet so we're just going to go ahead and query the video frame then we'll do video frame and we'll just set the height and width to that so let's go ahead and give this a test so we're grabbing that new video frame that was being added and handle user published and let's try that one more time so i'll mute my mic make this full screen add another user actually here let me refresh it again just so i see the latest console and let's try this so when i add this user i think i'm good here so yeah i don't see any issues so now we're actually able to query that item and yeah that just solves that so i just want to fix those two issues i'll try to fix bugs as i go but also we'll try not to interrupt the video as i'm doing right now i just want to make sure we catch everything so the next thing i want to do actually is i want to go ahead and actually learn how to display our screen here so very often you might be in a meeting let's say maybe you're using this as a streaming platform you want to be able to share your screen if you're playing a game or maybe coding we want to be able to toggle this and actually see what's going on here so let's go ahead and work on that so at this point inside of our room.html function we have our screen button right here so what i'm going to do here is i'm going to create a function and we're going to go ahead and toggle that function and that's actually going to request our screen basically request access to see our screen we'll select which screen we want to show and then we're going to unpublish our video track and actually show that screen so let's go ahead and take care of that so we'll go into room rtc and the first thing i want to do is set some parameters here so some default values so right here we have our local tracks remote users let's go ahead and do local screen tracks so we'll set something up for that and at this point it's just going to be undefined we don't have any screen tracks not everyone's going to want to share that so the next thing i want to do is go ahead and set a value called sharing screen and this will just be set to false so by default until we share our screen this value will be false and we just want to make sure that we can check this if we need to now let's go all the way down to the bottom and underneath toggle camera what i'm going to do is create a function called toggle screen so we'll do let toggle screen and we'll make this an async function let's go ahead and add in our event and i want to call the event listener right away so let's copy and paste one of these here and we'll just do screen button we are going to toggle the screen whenever this is clicked so when we click this button what do we want to do so right away i want to make sure i can get a few buttons here so we want to get the screen button so we'll do a screen button that's going to be equal to e dot current target because that's the button that we clicked on and then i also want to make sure that the camera button is hidden so the only way to toggle the screen off is to actually click this again so when we click this i want this camera button to go away we're going to unpublish the stream and then only when we toggle this again do i want this camera button to actually appear so we'll go back here and we'll just do camera button and this will be document dot get element by id we'll just do camera btn so we'll get both buttons and let's go ahead and check a condition here so we'll first check if we're sharing the screen so we'll do if sharing screen and i actually want to use the not operator because right away i want to see are we if we're not currently sharing i want to actually work on that functionality right away which means that we're about to toggle that screen and share so if we're not sharing then let's go ahead and set sharing to true that means that we're about to toggle it so we'll first change that value and then i want to change the button so we'll go into our screen button and we'll add in a class here so we'll just screen button dot add class list here and we're going to set this to active so we're going to go ahead and toggle this and right after this i want to grab our camera button and we're just going to go ahead and add in the class list of where we're going to remove the class of item or active so the reason why we're doing this is because when we toggle our screen off again and we want to turn our camera back on uh by default i'm going to have the camera set as muted so the audio and video will be muted so if you want to turn off your screen you're not surprised if your face all of a sudden just appears on camera so i want to right away make sure that the camera is not active so we'll also take our camera button and we'll do style.display and we're going to display it as block here so the reason why we'll display it as a block element is because if we are currently sharing we're actually going to hide it in fact let me just go ahead and add this right now we'll do else and if we are currently sharing let's go ahead and set this value let's see i'm trying to remember how i wanted to do this so we'll set sharing screen to false so that means that we're actually turning off our screen and one of the things that we'll do is camera button right here we'll take this value and set it to none here okay so that's kind of why i'm changing this up here so the style here is going to be actually this is going to be blocked okay so there we go so we're going to hide it up here but then we'll show it down here so there we go sorry i confused myself up a little bit i just wanted to make sure you understand what's going on with the buttons so let's actually just try this so right now i guess nothing's going to happen so if we want to share our screen we're going to click that our screen will be toggled and then when we click it again we're going to turn this off and then turn the camera back on so we'll work on that in a second okay so the next thing i want to do once we finish the button functionality here is i want to go ahead and actually get our screen sharing track i was about to say screen recording now to do this we earlier used a function called get camera and audio tracks where was that let's see somewhere here so i'm trying to reference this so we got the microphone and camera tracks but at this point we already have our audio track so the only thing we need is a screen track so we'll set this to a weight and we're just going to call agora rtc dot create screen video track okay and what this is going to do is again it's going to toggle us and ask us what screen we want to share and if we approve just like we would with the camera on the first time now once we do that let's go ahead and remove our current video track so we're only going to be able to share our video or camera track and sorry about the outside noise uh let's go ahead and do git element by id and we're just going to grab the user dash container and we'll grab our current uid so throw in the uid and we're just going to remove it so we'll remove the current item our current video track and then after that we also want to automatically display our screen into the big frame so we'll do style or display frame dot style and we'll just do dot display and this will be equal to block so right away if i'm sharing my screen i want to be able to see if it all worked if the users are seeing exactly what i want to show them and so on so i figured it'd be good practice to automatically display that now after this what i need to do is create a player a video player so let's just grab one of these let's take this value we'll bring this down here so we'll create the video player and we'll have to actually initialize the variable here so we'll get the player and at this point we're just getting our current uid we don't need to get the user uid because that's for remote users and let's see the next thing i want to do is go ahead and add this player into the dom so we're going to display that mainframe as a block here and then we're just going to go ahead and do display frame and then we'll just do dot insert adjacent html and we'll throw in the player before end we'll throw in the value like this and we'll just call the player okay so we're going to take this new video player we'll throw it in there and the next thing i want to do is go ahead and actually uh actually let's go ahead and add in the event handler so when we add in this player into the video frame automatically i want that frame to be clickable so a user can actually toggle that so we want to add in that expand video frame function so we'll just do document dot get element by id and we'll just go ahead and grab the current item so get element by id let's see we'll just grab this user container i guess i could have set that into a variable so we'll grab that item and we'll just do dot add event listener and we'll just listen for a click event and we're just going to go ahead and add in expand video frame so every new item that we add in we want to make sure it has that function so in this case we're just adding it to the new element that we just created okay so once we do that let's go ahead and set the user in display frame id so we want to go ahead and take this current id so we are now inside of that display frame and we use this later to check certain values so let's go ahead and again we'll grab this value right here so that uid of this video player will set this value and after that i want to go ahead and actually play that screen track so just like we did earlier with that screen or the video track so we grab all or we create the track and then we call local tracks play and it's going to go ahead and create a video tag and throw it into this frame well we're going to do the same exact thing here so once we set user and display frame we're just going to go ahead and grab the local screen tracks and at this point we only have one track so we don't have to do anything like index 1 or anything like that we can just grab that local screen track and we'll just call play and where do we want to add this well we want to add this to user uid right there so we want to throw that into this value okay so that's going to go ahead and play it so at this point it's only going to play locally but it won't work for remote users but i just want to make sure that you can see what's going on here so let's try this so i talked a lot there let's go ahead and just try this so when i click on this it's going to go ahead and ask me hey what screen do you want to share so let's go ahead and just share this current screen and there we go now i am sharing the screen so if i move all this code to my right screen let's actually restart this i'll share that right screen now so we'll go in here and let's say we're asking somebody for some help well we can just share our screen and the code is very small because that screen is not still zoomed in and there we go that's how we can do it and then we're going to work on the toggle functionality but what happens when another user joins here so if another user joins they're only going to see our video track and not our screen track so we have to do a few things here we are currently still only publishing our video track and not that screen track so we need to publish we need to unpublish our video track and republish the screen track so this is only happening locally so let's go ahead and fix this so i'll drag my code in here let me go ahead and just refresh this again okay give that a second all right so let's go back in here and what i want to do here is go ahead and unpublish the current video track so we're just going to call await we'll call client dot unpublish so just like we have published now we can un publish and we're going to unpublish only the video track so we're just going to call local tracks and we're going to unpublish index 1. so we still want our audio track to be published because if we're sharing our screen we still want people to hear us so we're only unpublishing the video track and the reason why we have to unpublish it is because right now with this client object we can only publish one track at a time now there's ways to do two uh we're not gonna get into that for now we're just going to publish one track at a time so that means we have to unpublish this local track and then we're just going to go ahead and copy and paste this bring that out down here and we're going to call client.publish and now we're going to publish our local screen tracks so you see what we're doing here we're unpublishing our video track because we can only publish one at a time and we publish our local screen tracks our audio is still published all right so let's go ahead and save this now and let's check out what this looks like for a remote user so i'm going to drag this code into the right screen if i toggle this we'll go ahead and publish that and now if i join as a remote user i can see myself as a remote user now if this user is sharing their video track or screen track i can actually see that there we go so i can toggle myself and see this remote user's screen track it's all working perfect so now we're going to work with some untoggle functionalities let's go ahead and continue here so i'll close this out we'll refresh this and we'll drag the code back in i know it's kind of getting a little bit slower here but i just want to make sure we go step by step and i explain everything now at this point if we do this i just want to make sure that when we publish our screen automatically it's going to get displayed in the big frame so i want to make sure that we loop through all of our video frames and set those frames to a heightened width of 100 pixels so let me find where i did that i just i'm just going to copy the code here we go so we'll just grab all the video frames and let's just update them so we'll take this and we'll bring this actually let's see that was setting them to 100 pixels here let's just um we'll actually go back into room.js and we'll do this right here so we'll take this entire for loop and we'll just bring this in this is where i'm getting lazy and i'm copying and pasting code so after we publish it the last thing we're going to do is go ahead and loop through all our video frames and at this point let's just go ahead and set document or actually we're going to set video frames it's going to be equal to document dot get elements by class name and we're just gonna grab video underscore container let me just move this over okay so that looks correct here just want to make sure we're setting that and there we go okay so now we need to work on unpublishing and republishing our video tracks so right away if we're currently streaming we're going to set sharing screen to false now and we're still going to make sure that our camera track is or now we're going to make sure our camera track is visible after we do that what i want to do is remove the current item so i'm just going to go ahead and take this value right here so we're just going to take that container again and we're just going to remove it so we're going to remove that current screen container once we do that let's go ahead and unpublish our local screen track so i'm going to take this unpublish method we'll bring this down here and now we're going to unpublish local screen tracks so once we do that i want to publish my video track so i'm going to put all of that inside of its own function let's see i'm seeing an issue here i just want to make sure everything's working okay it looks like i have this extra bracket right there okay so at this point we're going to call a function called switch to let's see what i want to i'll switch to camera so switch to camera okay so we're about to call this and i'm just going to comment this out because we're going to create it in a second so let's go ahead and make this function right here next to join stream so a lot of the functionality is going to be the same so i'm just going to go ahead and make this underneath join stream and let's just go ahead and create the function so we'll just call this switch to camera we'll make this an async function all right so what do we want to do in switch to camera so the first thing i'm going to do is create another video player so some of this functionality is getting redundant so we're going to take this bring this down here we'll create the video player then i want to make sure that this is added to the dom so let's grab let's see grab one of these stream containers so at this point we'll just grab this right here paste this in here and i just want to make sure it's in the big frame so we can kind of notice that something happened so let's just go ahead and do document.getelementbyid and at this point let's just go ahead and change this to display frame actually so we'll take that display frame and we'll throw in our new player in there so this is going to be that video stream once that's done let's go ahead and mute our video and audio track so just so nobody gets confused let's call await local tracks here and we're just going to take the index of zero so that's going to be our audio track we're going to do set muted and we're going to do true here so we're going to mute ourselves immediately copy and paste that we'll take the video track now that'll also be true and we're also going to take that mic and screen button and change up some of the classes here so automatically we're just going to do document.getelementbyid we'll take that mic button so mike btn and then we'll just do dot classlist dot remove and we're going to remove active so we're going to be muted so we want to make sure that the user knows that they're muted whoops what i do here okay so we're muting or removing the class of active from the mic button and we're also going to remove this from the screen button so we want to adjust that we'll remove the class of active there that looks good and now we just want to go ahead and play our local video track again so we'll take this right here from join stream we're going to do the same thing and we're just going to go ahead and bring that down here we're going to play it and we're also going to publish it we're going to publish our video track here but we're going to do something a little bit different so i'll bring this into that switch to camera function and we've already published our audio track so that's already published it's muted at this point but it is published so let's go ahead and just remove this one we don't want to republish that that's going to cause an issue so we're only publishing our video track okay so kind of a lot of jumping back and forth there but if anything just go through that again it should make sense so now we have the switch to camera function and we just want to go ahead and call that right here so let's see if everything worked i hope i don't have any issues all right so we'll save that and let's go ahead and try this so i'm actually going to take this code move it to the right screen let's go ahead and here let's actually refresh that and i'm going to toggle this right away and see how it actually adjust that frame so we'll toggle this we'll share this screen screen number two now i'm sharing the screen and i want to toggle my camera again so i'm going to click this it's going to go ahead and deactivate my mic my mic my camera and the screen and now here we go i can see myself so you see how that republished the camera so let's see what this looks like for a remote user so right now i'm currently showing my face let me mute the mic here so i don't hear that echo so if i go ahead and share my screen again so i can re-toggle it the remote user now sees this right here the screen and we'll go ahead and untoggle or toggle that again and now we're just seeing that mic muted so the local user just sees black we can fix that later i won't worry about that right now but here we go now we're seeing that and we can just keep going back and forth so it's all working let's just double check that's good awesome awesome and we are good to go there we go all right so that is the screen sharing functionality what are we doing next so let's see let me go back into the code bring this over and i think at this point what i want to do is actually give users the ability to create new rooms so i think i'm going to close out a few of these tabs actually but i want to make sure that users can actually go to create room and specify their name because we are going to get to the point where we're actually going to display user's name in the chat so i want users to be able to create or join a room so let's start dealing with this functionality so right now if i do something here nothing will happen let's go ahead and build this in so let's jump back into we're not back into but let's jump into join form for the first time so in the js file we'll go into lobby not join and in here we'll also pull up lobby.html so let's just uh oops let me open that in live server we'll close out room rtc room html room css and we'll do lobby html we'll open that to the side okay so here we have a form we have a container and we have a lobby form so let's get this form and add in an event listener that actually responds to when someone submits it so we'll set the value of form and this will be document.getelementbyid we're going to go ahead and get lobby double underscore form and we'll add the event listener right away so we'll do form dot add event listener and we're going to listen for the submit event and we'll just add in the function right in here so i'll make that an arrow function and let's see so the first thing i want to do is prevent default so we want to take care of all the functionality that occurs when the form is submitted and i need to get the room name so i want to make sure that this is not required because i want to do something a little bit different here so the name will be required we'll leave that right there but for the room value here this is all inside of the form let's go ahead and get that so let's just go ahead and specify the value of invite code and this could be the room name invite code the reason why i call the invite code is because if i'm in a room and i want to add another user i would just send them the room name or invite code it really doesn't matter at this point so we'll go ahead and get the target value so that's gonna be the form and we're getting room dot value okay so we're going to get that invite code but if an invite code doesn't exist we're going to use the not operator and we're going to go ahead and say hey if we don't have an invite code let's just go ahead and create a new invite code so for this we're just going to go ahead and do string and we're going to generate a random number between 1 and 10 000. so we're going to do math dot floor and in here we can do math.random to generate a random number so we'll do random and at this point we'll just do by ten thousand okay so there's different ways to do this you can maybe force users to create a uid this part's up to you i'm just showing you the functionality so let's go ahead and do window dot location so we're just gonna redirect the user so once the form is submitted we're going to redirect the user to the room.html page now at this point we also want to throw in the room value and this is how we're going to get this from the other page here so we'll do room and then this is going to be the invite code okay so inside of our room underscore rtc.js file we're getting the room id from the url right here so we're getting that room value and then up until now we've just been setting that room to main but we want to actually change this so let's go ahead and just give this a test and see what's happening so if i'm in the room here let's go to create i'm just going to add in a name because that is required if i go to go to room we just see a random room generated now if i go ahead and paste this in as long as we're in the same room we can see another user but if i change this value right here we're in that room page but this user and this user are in two separate rooms here so let's say i can add in a random random amount of characters it doesn't really matter we're in two separate rooms so that's how we're able to do that but if i want to create a room and have a specific name so let's say i name my room test here i can go to the room here i'll see it so if i send this number or this value to another user let's just go ahead and bring this in here give me a second here okay so if another user goes here and they specify their name let's just do dennis and i tell them hey the room name is test so they can go here and now we're in the same room okay so that's the idea here either generates a random room number or a room invite code id whatever you want to call it or create another one or a custom one now the next thing i want to do is go ahead and actually have a display name so at some point we're actually going to have a chat here and i want users to be able to actually see our name here so on the sidebar and then when we're actually messaging so what i'm going to do here is go ahead and try to get the name from local storage so right here let's just go ahead and do let and we'll call this display name and the idea here is to get the name from local storage so we'll call local storage dot get item and we're just gonna go ahead and get display underscore name so if we've actually been on this website before and we've entered a name in we should just go ahead and get that value from local storage so that way we're not having to type this in again now we can always update it but we're just going to go ahead and get that value now if this value does exist we're just going to go ahead and do if display name if it does actually exist here let's go ahead and just add it to the form here so we'll just do form oops okay form dot name dot value and we're just going to set it sorry i don't know what's going on here so form dot name dot value okay and that's going to be equal to display name so let's go ahead and test this actually before we finish this up we're just going to go ahead and actually uh submit the form with this so when we submit the form we need to update this name so we'll just do local storage so local storage dot set item so when we submit it if we don't have a name yet we'll just do display underscore name and we're just going to set the name values which we'll do e dot target dot name and every time i do name for some reason it seems to mess this up so we'll just do that slowly so we're going to set the name value any time we submit it okay so let's try this out so we'll go in here go to create room okay so it looks like i already have the name peter inside of local storage so that's the beauty of local storage it automatically filled this in so if i do dennis we'll just go ahead and set the room name as one now if i go back to create room if i refresh this dennis is right here so if i go ahead and do inspect here i'm not going to go into the difference between local storage and session storage other than the fact that session storage will be cleared once we're off this website it's just for the session whereas local storage will store this value in here for a set amount of time or whatever we actually want to leave that as whether it's permanent or a specific amount of time this value will always be in here so that's how we get the local storage so if i remove that now and i refresh that let's try this again so right now if i refresh it nothing's going to be in there if i added dennis that value was added to our local storage and that's always going to be there so for testing purposes though because i want to open up multiple tabs let's change this to session storage and then we'll do session storage right here so that's all we wanted to do here and what i want to do is make sure before we jump into the next step here is i want to make sure that a user always has a display name if they don't i want to make sure that we simply just redirect them so let's actually take this display name value and let's copy that and let's move this into room underscore rtc so we'll bring this in here so we're going to get that display name now we're going to check we're going to say if there is not a display name so we'll use the not operator not dispatch event so if there's not a display name let's go ahead and simply redirect the user so we'll not allow the user to be in here without a display names we'll do location so window.location and that's going to be equal to the lobby page so lobby.html okay so let's try this so if i go ahead and try to enter the room without a name so just go ahead and add in some kind of room name it actually requires it so let's just go ahead and add in dennis now i'm in the room but if i go in here and i clear that local storage let's go into application let's go into session storage actually not local storage we'll clear that there and in session storage if i remove this and refresh it it's just going to redirect me back to the lobby page so i cannot enter a room without a name okay so let's move on to the next section here and what i want to do now is start adding in some real-time features into this project by listing out participants that are currently in the room so as users join or leave the room we want to be able to update this right here and the participants total along with a chat room on the side so this is all going to be in real time now typically you would need to use something like websockets to add in real-time communication and some kind of signaling server but for this agora actually provides us with another sdk called agora rtm and we're going to be able to use that sdk to build all these features in so there's a lot of cool stuff with that and up until now we've worked with the rtc sdk so real-time communication that has everything to do with audio and video now it's time to add in another sdk and start building this out so if we go back into our agora console go ahead and go into let's see we'll go into documents here or downloads actually let's start with the api reference and let's look up the rtm sdk so go ahead and select web right here for your platform and down here if we scroll we can see real-time messaging so this is the sdk this is the api reference and if you look here we can see a lot of features here so we can send peer-to-peer messages we can send messages to a channel and we can also get information about this channel like who's in it and get updates on when people join or leave a channel so that's how we're able to communicate is by joining channels and sending messages either directly to peers or to these channels so let's just go ahead and get started and you'll see how all of this works so what we're going to do here is go back into our console and i'm actually going to go into the download section and we're just going to go ahead and download the sdk for rtm so we have real-time messaging let's go ahead and click that and we're just going to need to add this into our application and connect a few things so i'll move this over here and i'll minimize this let's just grab the zip file right here let's go ahead and go into the libs folder and let's take this one right here so the js extension and we're going to drag this into our project so we'll close all of this out and back inside of our code here let's just go ahead and connect a few things so the sdk is right here let's drag this into the js folder so let's move everything in one section here and then we'll go back into lobby.html and let's just connect it down here let's see that's actually supposed to be in room.html so we'll minimize that and let's go in here and we'll just bring this down here so let's copy and paste this and i'm just going to go ahead and get the name here so this is agora rtm make sure you grab the right one we're going to copy that name and i'm just simply going to update everything from after js all the way to dot js okay so let's jump back into room underscore rtc.js and what we need to do now is configure the rtm sdk here that we just added so just like we did with the rtc sdk we created a client object then we take our room name and uid that we have generated and we join a room with a specific user so we join a room right here we use the uid token app id and so on well we're going to do a lot of the same stuff with the rtm sdk except for in this case we're going to join a channel which is going to be like a room so it's kind of similar here and with this room we're able to send messages to and get information about all those users so the first thing i want to do is configure a client object and at this point let's just call this rtm client and i'm just going to prefix it so we know that this is for the rtm sdk and we also want a channel and this can also be called room channel that's kind of the same thing i'm just going to go ahead and keep it that way because that's a common convention when we're dealing with something like this so we have our client and our channel and we want to go ahead and initialize this so inside of the join room init when we join this video call we're also going to join the channel using the rtm sdk so let's actually move this down here so we're basically joining two things at once here and we're gonna go ahead and get the rtm client and at this point we can call await agora rtm so before we did rtc now we're doing rtm right here because we have that sdk and i also need to set this value so we're calling agora rtm dot create instance so again i would highly recommend you look at the agora documentation go back to the same place that i showed before look at the api reference and you'll see the details about all these functions if you need to do something else there so at this point we're just going to go ahead and pass in our app id so that's how we create an rtm client instance now once we create that rtm client instance let's go ahead and actually log in with this client so we're going to call that client object we'll call login and to log in we are going to need our uid so we have the uid that we specified earlier along with the token now remember that we set our token or the authentication to app id only so token is okay if we leave it as null so let's just recap this we created the uid and we also have our token so we're using the same exact values to log into rtc and rtm okay so we log in here and the next thing we want to do is go ahead and actually create or join a channel so we're going to take that channel value that we just created and we're going to go ahead and call await and here we're going to do rtm client dot create channel so again if this channel exists we're just going to join it if it doesn't we're going to create it and we're going to take that same room id so the room id that we created for the rtc client here well we're going to join the same thing so we're creating or we're joining a room with that room id from the url and once we actually create the channel we need to join the channel so i actually misspoke there we're going to create it and then join it so we're going to call channel dot join let me just throw in a way here just in case okay so we create the rtm client we log in with that client using our uid and token and then we go ahead and create a channel and we join that channel so we're in that channel now once we're in that channel what i want to do here is i want to see everybody else that's in this channel so at this point let's just go ahead and create another javascript file so any of the functions dealing with the agora rtm sdk we're going to keep that separate so we're not adding to this file and making this longer i think we're at about 210 lines of code right now so we'll still work in this file here but i just want to make sure that any functions that could be separated are separated here so we're joining the channel at this point everything's looking good so let's go into the js file and we'll create a new file and we're going to call this room underscore rtm dot js here and let's go ahead and create this function so when we join a channel there's going to be an event listener just like we had right here where we had client on user publish so anytime a user publishes their stream or leaves right here we have these side effects that get called well with rtm we're going to have the same thing here and this is how we're going to be able to update those participants in the dom and actually know when someone joined so we're going to go ahead and call the channel here and we're going to call the on and then we'll do member join so make sure it's a capital m and capital j so on member joint we're gonna call a function and this is gonna be called handle member joined so we're just gonna go ahead and actually call this function here and we're gonna build this function inside of agora rtm and before we actually start working with this let's go back into room.html and i want to make sure that we actually connect rtm here so let's go ahead and bring this down here and we're going to change this to rtm like that okay so we added that in we want to make sure that we have access to everything and actually just in case let me bring this above rtc so i just want to make sure that we're actually having access to all these functions so i think placing that above would actually be better okay so we'll go back into room rtc so we're calling handle member joined and in room rtm or room underscore rtm dot gs let's go ahead and create this function so we'll do let handle member joined we'll make this an async function and by default when we call this function we want to know the member id so we actually have access to that so we'll do member id and let's go ahead and make this an arrow function and close this off here and for now let's just console this out so we can actually see it so we'll do console.log and we'll just do a new member has joined the room and then let's just console out the member id so let's just get the core functionality in and then we'll actually start working with the side effects here so we have the function we call the function here on member joined so listens for that event and here we go so let's check this out so if we go back into our application here let's open this up let me close out a few of these so let's go ahead and actually go into inspect here check the console i'm going to refresh this so we're in the room right now but if i join as another member we should see this console out we should see a new member has joined the room and then that should actually show up here okay what did i do here okay so we should actually see this consoled out because it's going to fire this off so let's try this and we're also joining the same room so the room is going to be 5 six nine four so we have to make sure it's the same one so let's move this down here and let's join copy and paste that oh i guess i need the room name here and let's just join as tim so let's throw in a name so we'll go to room and write it right away we see here let me mute this so i'm not hearing the echo okay so immediately we're seeing this get consoled out so we'll just say a new member has joined so here we go we can see it so we can see that user's id and we know someone else has joined the room so perfect now let's go ahead and actually add them to the dom so when a new member joins i want to be able to update this so what i'm going to do here is go ahead and clear this out and we're going to start updating that sidebar so let's go into room.html and let's go into the members section so when a new member joins we're just going to go ahead and create this element and actually add them so i'll leave this top one right here and i'll just comment this out because i do need one at least to work with because i want to take the styling and let's just take all these member wrappers and we'll go down to the last one and we'll clear that out let's see this save it okay so we see no members and i'm also going to reset participants to zero because we're going to update that okay so when a new member joins let's take this div so i'm going to copy this and let's go back into room rtm here i'm hearing an echo somebody to close one of these out so we're going into room rtm here and we're going to create another function and this is going to be called add member to dom so we're going to go ahead and add a member we're going to make this an async function and at this point we're just going to pass in the member id so member id and we're just going to call this let's see let member item and i'm just going to paste in what i copied so i'm going to use backticks we'll paste this in and we're going to add this item to the dom so at this point we're going to want to change this value of 1 to the actual user id because when a user leaves we need to be able to query this and remove it and we're also going to update the name later on we'll actually add this as a name value but for now we'll just throw in the member id so let's go ahead and actually add this to the dom we'll just do let members wrapper oops accidentally capitalize something there and then we'll do dot get element by id and this is going to be member underscore list or double underscore so we'll take that members list and that's right here and we just want to add that item to this section so let's go ahead and get that member and we'll just do members wrapper dot insert adjacent ht or adjacent html not element so we'll do html and we'll just go ahead and add this before end and we'll throw in the member item so take that member item we'll add it and then let's go ahead and call this function right here so we'll add the member to the dom pass in the member id and let's try this out so let's open this up and if i go ahead and join with another member let me mute a few things here we should see this pop up here actually here i keep forgetting to join the room so we'll join as tim again and we'll meet her mike and here we go we see the member has joined the room so we don't see the name and we don't see our own id so we're going to fix that so the other member joins we don't see all the participants so we're going to fix all of this right now so let's go ahead and join with another room or with another member we'll just do eric and this is five six nine four yeah five six nine four we'll go back here and as that member joins we can see them enter the room so perfect and then this one's only going to see that newest member and then the current member that just joined won't see that so we're going to update this in a second now what happens when we actually leave the room so when we leave a room the members don't get removed so we want to actually remove these members here so as they leave we have another event that we can listen for and this is going to be called member left so we're simply going to take that element from the dom find out which member left the room and we're going to remove them so there's going to be a little bit more functionality here so let's go ahead and add this so we'll go back into room rtc and we're going to take this event right here let's copy this and we'll do member left here so left and then we'll do handle member left so we'll go back into our room underscore rtm file and create that function so let's go ahead and create that handle remember left make that an async function we do need the member id so we'll pass that in create the function itself and in here what i'm going to do here is actually go ahead and create another function that's going to remove this member from the dom so later on we're actually going to want to call some other stuff inside of this function so that's why it's going to be its own function but we're going to create another function like this where we add the member to the dom here we're also going to remove it so let's go ahead and just create that or call it right here so we'll do remove remember from dom and we're going to pass in the member id in here and then we'll create that function so remove remember from dom and we'll make that an async function pass in the member id and let's go ahead and close this out so all we want to do here is go ahead and grab the specific member wrapper and remove it so we'll just do member wrapper so this specific wrapper we're going to grab it by the id so we're looking for this id we're going to query this so this tells us which member left we're going to go ahead and get that member and then delete them so the document document type but document what the heck is going on here so document.getelementbyid and this is going to be remember double underscore passing the member id and then we'll do double underscore wrapper so we're getting that number and then we're calling remember wrapper dot remove and that's it so we're just going to go ahead and actually remove the member now to actually leave the channel if i just close out my browser uh technically the member will still stay in there after about 30 seconds agora by default if it senses an activity it'll actually just call the leave function and we'll leave the channel but we want this to happen immediately so when a user closes out their room leaves a channel turns off their computer or whatever happens we want this function to be called now we could call this on click of some kind of back button or some exit button but that's all not always practical because some people leave the room by simply shutting their laptop or turning off their computer not everybody always clicks a button so for this just to ensure we actually call this function here we're going to go ahead and call it on the unload or before unload event listener with a dom here so we'll first create a function and this is going to be called leave channel so when a user leaves a channel we're going to go ahead and actually call this so leave channel we're going to go ahead and do await channel dot leave and then we're going to log out with the rtm client so we'll just do await rtm client.log out so we're simply leaving and logging out now to call this function and again this is what's going to trigger this event right here so handle member left this is going to be that response right here so when we call this function so now to ensure that this is always called let's do window dot add event listener and we're just gonna do before unload so before unload so basically once the dom is being torn down once we're starting to tear this down before it actually completes it we're gonna call leave channel okay so now we can join and leave a channel so let's go ahead and try this here let me make this room number a little bit easier to remember we'll just do one so we're gonna be in this room and now when another user joins we'll do 1 and this will be tim so we'll just keep adding these users and this one will be eric or i guess that name is irrelevant but okay so the members are being added but when i close this out this is going to call that before leave before unload function it looks like it's freezing up a little bit so let's go ahead and close this out so when a member leaves we see that be removed from the dom and there we go and then if that member tries to join again we'll join as tim one all right there we go so that functionality is ready so what i want to do now is make sure that when we first load our page if there is a room with members already in here so right now i joined as a new user i want to see myself and i also want to see the user that was in there before me because right now we're only seeing the new members that are joining so if i join as paul and i join this room you'll notice that the first user is going to see the two new users this user will see the most recent user and this user won't see any so let's go ahead and call a function to get all those members and load them in here so we'll save everything and let's go ahead and down below the leave channel function actually we'll do this above right here so just below remove member from dom let's call this get members so we're going to go ahead and make a call for all the members in the channel on the first load so to get the members we're going to create the variable members and the channel has a function i actually want to make sure to add in a weight right here we have a method called get members so it's going to go into this channel and it's going to give us every single member that's currently in this channel it's just going to return back an array of their ids so once we get this we're going to create a for loop and we're just going to loop through each channel member and then add them to the dom so let's go ahead and do let i is equal to zero and then we'll do members dot length and if that's greater than i then we'll go ahead and increment i and then go ahead and add this function in here so we'll do add member to dom and at this point because of that iteration in that loop we're actually getting the member ids so we'll do members and then get that current member's id so we're going to go ahead and call add member to dom and then render them out here so to get the members on the first load let's go ahead and grab this we'll bring this into room underscore rtc and i'm going to bring this just underneath the channel functions right here so let's go ahead and call this so we'll go ahead and call get members so we want to have time for everything to load we'll load all our channel members in there we'll join ourselves and then we'll call the function so let's go ahead and open this up here we go so on the first load i'm already seeing my own id that's perfect now when i join as a new user i want to see my id that's going to be the new user's id and then the users that are currently in there so we'll join the first room there we go we see two ids and two ids so mute that mute that there i guess that didn't mute it and then we'll join again let's call this paul okay and let's go ahead and join perfect so every single member now sees themselves and it's lagging right now it's not an agora issue my network actually has been bad all day for some reason so um yeah that's i wouldn't worry about this part that's not an issue with the app so here we go we see the members everything looks good as we leave all that gets updated and perfect so having an idea is not really practical we want to actually see member names so we want to know who's in the room so let's go ahead and add that next so this is actually going to be very easy and i think it's about two lines of code here so all we need to do is go ahead and as we join a room we're simply going to add in an attribute to our members so when we join we kind of like upload our id into that room so our channel id gets stored there so that user object is stored in the channel room and we just want to basically add in an attribute to that user object and that's going to be a name so we can add in whatever attribute that we want here so let's go ahead and as we log in let's go ahead and create this attribute so we're going to do await rtm client dot add or update it's kind of a long function so add or update local user attributes like that and then in here we want to first specify the attribute that we want to add so this is going to be named so we want to add in a key value here and then we're going to take our current display name remember we have that up here stored somewhere so we have our user's display name and we're just simply going to add that in there so we're adding it as a channel attribute okay so first we add the attribute so now all other users can actually get this channel attribute by finding our user's id so now we'll go back into room underscore rtm and we're going to get this channel attribute so in the add member to dom function we have the member id here so let's go ahead and actually call this right here so we're going to call let and then name so we actually want to destructure the return value here and we're going to call await and then we can do rtm client dot get user attribute by keys and we're just going to go ahead and call this function first we want to know what user attribute we want to get or what users attributes we want to get so we're going to pass in that member id we're saying hey this is the user that we want to receive some more information from and we're going to pass this in as an array and we want a list of the attributes that we want so we added this as name right here so we want to get this value right here so we're saying go ahead and get the name value from this specific user so get user attributes by key so that should be s right there i want to make sure i got that right and let's go ahead and test this so get user attributes just want to make sure all the spelling's correct so once we get that value we can go ahead and change the member name to name let's go ahead and just update that and let's give this a test so there we go i see my own name we'll join in as another user whoops okay copy the wrong value here so let's go ahead and join what the heck is going on won't copy the entire url okay so we'll join as paul let's go ahead and join room one here we go we see paul and dennis and then on this end i see the user's name so perfect we're able to add in those channel attributes and join with those users so we can actually see who's in that specific room so the next thing i want to do is update this total so we want to make sure to actually get the list of channels find out the length of that array that we have in that participants list or members list and we'll just go ahead and update this total so let's go back into room underscore rtm so underneath our add member to dom function let's go ahead and create another function and we're going to call this one update member total so update member total and this is going to be an easy function here so we'll just go ahead and make this an async function by default let's go ahead and complete the function and let's just go ahead and set the value of total or let's go ahead and actually get that dom element so we'll do total and that's going to be document dot get element by id this value was member count so we want to get this element right here and we're simply going to update the value inside of that so total is going to be dot inner text here so we're going to update that text from 0 to members dot length so we're going to go ahead and actually pass in the members array into here so throw that into the parameters and i want to call this anytime a member joins here so when a new user joins let's go ahead and call this we'll call update member total we'll throw in members here and let's actually get the members on the first load so we'll do let members is equal to channel dot get members and also need to throw in a weight right here okay so we're going to go ahead and throw the channels in here update the total let's copy this again we also want to update it when we leave so or when a user leaves so handle member left we'll bring that in here update that value and then we also want to update the total on the first load so inside of get members let's also call the function right here and here we already got the members so let's paste that in and let's check this out okay so i just need to refresh everything so we saved it a bunch of times probably screwed up a few things so let's go ahead and just mute this and here we go okay so here we see two members let's go ahead and bring in another one paste that in we'll call this one eric join the room we see three total and then these were updated to three and this one is updated to three if we leave that should be updated and there we go so we are now updating the participants totals and we have all the information so we took care of most of the core functionality here and now it's time to move on to actually adding in chat messages so just underneath our get members function here let's go ahead and create a new function and we're going to call this one send message so this function will be responsible for actually sending out a message to the entire channel so anybody that's a part of this channel can have an event listener that can listen for this and this is a channel message so we have channel messages and we have peer-to-peer messages we won't work with the peer-to-peer messages right now but i just want to let you know that there's ways to actually direct message people based on their uid so inside of the send message function let's go ahead and prevent the default so we'll do e dot prevent default and then after this what i want to do is go ahead and get the message from the form so we'll go ahead and grab that message is going to be e dot target and then we'll just go ahead and get the value so target dot message dot value so i want to make sure that this is the name that i actually give that form field so let's go back into that form field inside of room let's see where was that so we have our stream or messages container so let's go ahead and go to the bottom here so we have our message form and then we have the name of message okay so we're grabbing the right value so we're just going ahead and grabbing that message here and then once we actually get that message we want to send out a channel message so we can go ahead and grab that channel value and then we have a method called send message so this is going out to everybody in that channel or i guess in that entire channel now in here we want to go ahead and send the actual text value so we could do something like this and then pass in the message let's say the message was hey like that we can go ahead and pass in a string well in our case i want to send a little bit more data i want to send our name the type of message that we're sending because we can actually send different types of messages so what i'm going to do here is send an object but first i'm going to stringify it and then we'll parse it on the other end so we want to make sure it's sent as a string so inside of stringify we'll go ahead and add that object and then let's go ahead and specify the type so type will be a chat message and then the actual message value let me create some space here so you can see everything so we'll go ahead and get the message and this is going to be the message from the form and then after that we'll go ahead and send our display name so do display name and we'll camel case that and then we'll throw in the display name that we currently have okay so let's go ahead and continue to the next step here so we're sending our message and before we actually start adding this to the dom i'm just going to go ahead and console this out so let's go ahead and just reset our form and then we'll actually see what it looks like in the console and then we'll actually start adding it to the dom so let's take this one step at a time so we'll do e.target.reset so the form will reset after we send it let's just make sure that that's working so if i type something out here hit enter it's just going to reset it looks like it actually refreshed the page so i forgot one more thing here let's actually add in the event listener so down here and this is all inside of room underscore rtm let's go ahead and get that message form select and we'll call this message form and this is going to be document dot get element by id and we're just going to go ahead and grab the message double underscore form double check this we have message form perfect and then let's add in the event listener so do message form don't add event listener and the event we're listening for is going to be submit or on submit and then we're just going to call send message so this will send out a message now on the other end i'm sure you're starting to see this pattern now we need a way to actually handle the response to this message so if we go to room underscore rtc right here we have this channel member joined channel member left we also want to listen for a message so let's go ahead and copy this one right here paste this below and then we're going to listen for the event of let's see i actually need to add in the channel here so channel or the c there and we're just going to listen for channel message so again in the agora documentation these are all the events we can listen for so anytime someone calls the send message function that's going to trigger channel message because that's the type and we're just going to handle the channel message so we'll call handle channel message and now we need to create this function to actually respond to that message so what's going to happen when appear gets this message so let's go ahead and build that out so we'll go back into room underscore rtm and just above send message let's go ahead and create the handle channel message function so we'll go ahead and call this handle channel message we're always making them an async function and here we're going to get the message data and we also want to know who this message came from so we're going to get the member id so those are two values that we can access right away and then we'll continue on with the function and the first thing i want to do is console this out so let's just go ahead and do console.log and we'll just say a message let me fix this a new message was received okay so we got a new message and i don't know why i can't spell received let's see whoops okay so a new message was received then we want to get the actual message so we're going to create a variable called data and remember that the message was actually stringified so we need to go ahead and parse it so we're just going to do json.parse we're going to parse messagedata.txt so we're just getting the text value right here so we want to get everything inside of this object so there's more data that comes with this message but we're just wanting to get that value because that's the actual date of the message and at this point let's go ahead and do console.log and we want to see the message so message and then let's pass in the message so this will be an object we're not getting message data but data okay so we send a message to the channel we resell our form we have the event listener that listens for that submission in this section we actually listen for that event here and let's go ahead and check this out see if we did everything okay okay so i'm gonna go ahead and mute this bring in a new member here and let's change the whoops what's going on here okay i need to reset this entire thing so let's join the room name of one and we'll do that again so we'll do peter i don't know where i come up with these names i guess more common names i'm just going to stick to that okay so i'm going to mute this okay so let's go ahead and do inspect here and we're going to have this user right here send this user a message well technically to the entire channel so let's go ahead and make sure we can see the console and let's just do hey like that we'll send it and then on this end here we go we see that chat message so that happened in real time i didn't have to refresh anything this other user automatically got that message let me try this again we'll just do hi this time and i just want to see how that works so as i'm typing this out you can keep seeing the messages right here so we can see the actual chat or the message type the message itself the display name and so on so we're able to actually go ahead and get all of this data so the next thing i want to do is actually add this to the dom so i guess the fact that we're sending out a channel message you should assume that both users can actually send messages back and forth so this user can now respond to this one and so on okay so we'll go back here and we're going to create a function called add message to dom so i'll just go ahead and bring this right here just underneath send message and let's just go ahead and add this so add message to dom and we want to take the parameters of the user's name so whoever sending it and the actual message value so the text of the message and then let's go ahead and actually create an html element so a message and then we'll append it to the dom so before we do that we want to go ahead and get the messages wrapper so we want to know where we're adding this element so we'll do document.getelementbyid and we are looking for the container for all the messages here so all the messages are added into this div right here so we have the id of messages so let's go in here we'll grab that container and then we want to create the message items we'll call this new message and let's go ahead and use backticks here and i want to find a message that's not from a bot here so we have a mumble bot message and that looks a little bit different from an actual user message so we want to get this one and then later on we'll use the mumblebot message so any message that's from the bot that's going to be extended by underscore bot right there so we want to scroll down and grab one of these right here so make sure it doesn't say bought so let's grab that entire message wrapper and we'll bring that down here give me a second oh i keep thinking i need to open up a new tab here so we'll paste that in right there inside of the back ticks let's create some space here and then for the actual name all we need to do is go ahead and take this out and we're just going to pass in the name of the sender and then we'll take in this message right here and we'll just remove the entire text value then we can just go ahead and throw in the message so we'll throw a message right there and there we go so we have the new message we have the message wrapper and we just need to go ahead and append this to that messages wrapper so we'll do messages wrapper dot insert adjacent not element but html we'll add this before end and we'll throw in new message okay so now we want to actually call this here so there's a few things that we want to fix up here but the add message to dom we want to go ahead and call on send message so right now only the sender will actually see that message and we're going to have to actually add in something on the other end for a user to receive that message so in add message to dom we're going to go ahead and throw in the display name and then we'll just do the message itself and let's test this out here so let's go ahead and look at this let's send a message so if i say hey if i scroll down we're going to see that message now if i say hi we have one slight problem here and that is the fact that new messages we want them to appear at the bottom here so as we're typing we want to make sure that they always go into frame if you look at the scroll bar it keeps scrolling up here or it keeps adjusting but we want to make sure that the newest one is always in frame at the bottom so let's go ahead and add something to fix that so down here after this messages wrapper here after we actually add in the element what i want to do is go ahead and get the last message so we'll do last message and this is going to be document dot query selector so we'll use the query selector and we want to go ahead and get the messages div and then we want to go ahead and get the message double underscore wrapper and we want to get the last one so we'll do last child and that'll ensure that we always get the latest message in that wrapper now once we get that last message all we're going to do is call scroll into view so this will just make sure that this element is in view so if it's out of view by default it'll just quickly adjust and we're going to scroll that into frame now for some reason i've actually had issues with this before so let's actually check if we have a last message for some reason when i try to call scroll into view uh it doesn't always read it as an element i feel like if there's no messages then it tries to call it and then that's when it throws an error so anytime we're calling it for the first time that's where i get that issue okay so that should fix it we get the last message then we scroll into view so if i go ahead and just refresh everything let's uh try this again so we'll say hey and there we go so now we see it if i say hi hello the latest message comes in so now i want to make sure that remote users actually get this message so right now we're only calling it when we send it so we want to go ahead and make sure we call add message to dom on the receiving end so at this point we need to do this inside of the handle channel message so anytime we receive a message we want to actually get the message and then append it to the dom so here we go we have handle channel message let's go ahead and clear this out we'll just leave this right here just in case we need to debug later so at this point let's go ahead and check the type of message and if we get a chat message so data dot type if this is a chat message so remember we sent that with the object of chat right there we want to go ahead and actually get this message so we want to append the message to the dom but we first need to get the user's name so the sender so let's go ahead and throw in name and then we'll do data dot message so we're getting the message value and then we're getting the display name so at this point we can just go ahead and do data dot display name so let's go ahead and save that and we'll try this one more time so i'll bring this to the left here and then i'll take another one bring it to the right let's make sure we're in the same room i believe the room id is one so let's just go ahead and rejoin again so we'll do dennis one and then we'll do tim and then we'll also do one so let's go to the room we'll open up the chat here and i'm gonna mute the audio so i don't hear an echo and let's try here we'll say hey so dennis says hey to tim or everybody in the room and then tim says hi to everybody in the room and there we go so we have real-time communication between them awesome it's looking good so let's go ahead and do a little bit of cleanup so i want to go ahead and get rid of these extra messages right here and we're just going to comment this out because we're about to add in this bot right here so i want to comment out that bot message make sure that we have it so we can actually use it in the new function we're about to create so we'll go to room and let's go ahead and go to the bottom of the messages and let's grab every single message but this last one at the top so delete all of them but one we'll go ahead and save this because this is a bought message and let's go ahead and comment this out create some space here so we have a fresh start here so right now i'm gonna go ahead and start all over again there we go that looks good and there's one issue here is that the chat box right here doesn't actually have this background until i add a message so i'm going to go ahead and work on some css so when you get the template this should be updated i'll make sure to fix that but now let's go ahead and move on to adding in this bot message so let's go back into room underscore rtm and what i'm going to do here is take this entire add message to dom function and let's just copy everything and duplicate it and we're just gonna add in another function to deal with bot messages so let's take the one on the bottom so we have add message to dom and then this one's gonna be add bot message to dom now in here we're just going to go ahead and change up a few values here so we're going to call this one bot message so we'll do bot message and we're only going to throw in this one parameter because of how we're going to throw in the text based on the message from the bot and at this point let's go ahead and change this value so let's actually take this entire div right here let's grab that and we'll copy it and i guess i could just remove it right away here so let's just go ahead and take that out save it and let's go back into this function so we'll take this value right here and paste that in let's fix the indentation and we have this bought emoji so there was a hex value to that you could just literally look up a robot emoji and then copy that and that's going to save the hex value for you so that's how it shows up like this so the name will be mumble bot and then for the actual value well that's going to be the message that the bot is saying so this will either be a user left or a user just joined us so we just want to go ahead and throw that value in so that's the actual thing that the bot is going to say now with this we have this underscore bot so the styling is going to be a little bit different so we change this up and now all we need to do is go ahead and call this so the first place i want to call this is going to be when we first join a room so i want users to know that there is an active chat box right there so this way it'll just welcome them to the room and it'll just notify them that something's actually going on here so let's go ahead and throw that in and then the actual message here let's just do welcome to the room and then we'll just go ahead and take in their display name so we'll do display name and this is what i meant by that emoji here so let's just go ahead and actually grab it so i'll just go find one we'll just call this wave emoji looks like i already looked it up and if you just grab this right here this should work just go ahead and copy that and i believe that's going to turn it into a hex value actually i've seen it before and i've never done it before myself until this video so it's kind of cool to try that so we just want to wave to the user and say hello i kind of want it to look a little bit different okay so the bot message is going to say this and we're just going to go ahead and make sure that that is inside of this text area so let's check this out so this is on join room init just after we get all the members so let's try this close this out and there we go so welcome to the room dennis makes me feel special there we go and now i want to make sure that anytime another user joins i also want to see a welcome message i want them to see the welcome message but i also want to be alerted that someone has joined this way we can see them in the participants but we see an active chat move in here so let's see at this point i want to go ahead and call this in the handle member joined function here so grab this go back into room underscore rtm and we have handle channel or handle member joined and let's see where i want to add this so let's just go ahead and put this at the bottom so we'll go ahead and grab the user and we'll say welcome to the room and then the user's name so i don't want it to say display name so what i want to do is go ahead and change this value to name and earlier i showed you how to get a name here so we can actually grab it from this section so we're simply going to go ahead and grab that user's name attribute based on their member id so when they join we have their member id so let's go ahead and grab this value bring this in so we're going to take that name and then we'll say welcome to the room and then whatever their name was so let's go ahead and test this so we're in the room and if another user joins i should see a message in a second oh let's see okay let's do tim and then i'll do one and then i'm going to jump back to this one real quick so as tim joins here we go so tim sees welcome to the room tim and then i see that tim was here now i want to add in one more bot message and that's going to be when a user leaves so i really don't see much value into this but it's kind of a cool feature i guess you can learn how to do it and then it's up to you if you ever want to add this in but essentially i want to know whenever a user leaves so i want to be able to notify everyone uh just i guess a small alert that we can add in it feels like since the app the app isn't that active it feels nice to actually have something filled up in that chat bot or the sidebar there so at this point we want to go ahead and call this function whenever a user leaves so instead of creating an entirely new function for that let's go ahead and just call this on remove member from dom so rather than querying that user's name again we're just going to go ahead and call this right here so let's go ahead and call the message or the function down here so take this value we'll go into remove member from dom and we're going to call this down here and we're just going to change the message up and we're going to say name so we're going to start it with name and then we're just going to go ahead and say has left the room so we're not sending the user the message we're just letting everyone else know because that user left they're not concerned about what's going on in the room anymore so it has left the room okay so we have a message and now we need to get this user's name so how are we going to do this well at this point i'm just going to go ahead and do this very simply and i'm just going to grab it from this member wrapper so right now we're actually getting this member right here and we're simply removing this element from the dom so before we actually remove that i can just go ahead and get the name so we'll just do name and we'll just go ahead and grab the member wrapper and we'll just do get let's see remember wrapper dot get element or elements by class name so we'll go ahead and get elements by class name and we're going to get the member name to be specific so member underscore name and make sure it's one underscore and not two because i've been using double underscores but at this point it's only one so we're getting all the elements that are the child of that member wrapper that have the class of member name and there's only one but it's still going to return an html collection because of this so it's still going to be an array so let's go ahead and get the first element so we return an actual item and then we'll just get the text content of this item so that's going to be the name itself so this is how we get the name we're going to go ahead and throw that in here so as a user leaves before this item is torn down from the dom we're also going to get a message so let's just go ahead and refresh it just in case here we don't have any errors and we'll open this up we'll join as a new user so we'll just do tim and we'll join the room so tim has joined the room we see tim and let me just mute this because i keep hearing the echo okay so i'll mute that and we'll just do paul i just want it to look busy okay so right here we see dennis has joined the room so that's us we see tim and paul so right now if paul leaves the room if i click this we're going to see paul removed from here and removed from here so where we'll see the message so paul has left the room and tim has left the room so perfect now we're getting all the alerts along with us engaging with the chat so that's the participants and the chat bot there we added in the agora rtm everything's looking really good or the rtm sdk everything's looking good and the next thing we want to do is we want to work on how a user actually joins a stream so the idea would be to turn this into something like more like a social platform where people can stream and talk have people join the video call but also be able to come in and just view it so it's almost like a twitch maybe twitter spaces type of clone whatever you want to call it kind of a mix of that so right now when we join it automatically turns on our camera we could mute it but we don't want everybody to join that way so what i want to do here is add in a join button so if i want to join as a viewer i can actually click join and then enter the room and then later on in the live demo of this i'll probably have it to where the host can invite people so not just everybody can join whenever they want i can imagine that can get pretty messy if we just start letting everybody join the room so yeah we'll make this to where these controls won't be here until someone clicks join when they join then they'll pop up on screen so when they join a room they'll still they'll still see their name here so we'll still see that they're in the chat just not in a video stream so to get started with this let's go ahead and actually jump into our room.html page and we're going to create a new button here so we have the stream container so let's actually get rid of some of this stuff that's commented out we might as well get rid of this so we don't have extra space in here so where we don't have a more code taking up space so we have our stream actions and it ends right here so just underneath stream actions let's go ahead and create a button here and this will be the join button so we'll do button and we'll give this an id of join dash btn okay so we'll have our join button and we're just going to say join stream so we want to prompt a user and then let's go ahead and also style whoops i should autofill that let's also style the leave button so i want to make sure that this is red so we want to give users the ability to still be in the room but also leave the stream so if they don't want to be publishing their video stream anymore so we'll just do this in line we'll add in a background color and for this i have a red hex value that i'm going to use so this is going to be pound f f f we'll just do capital f and then we'll do 5 0 5 0 so that should give us a red color so let's see style background color why does that look off oh i have two pound symbols there okay so we have our stream button and what i want to do is just make sure that it's there and then we'll actually style that so there's our stream button now we have a red back button so if anybody wants to leave it's more obvious so let's go ahead and go into our room.css and now we want to style up that stream button so for this i'm just going to go down to the very bottom i'm just going to start a new little section here and we'll do join or join dash btn and the first thing i want to do is set a background color to it so we'll do background so background color and this is going to be pound eight four five six nine five so i'll make that a purple color i guess i could have just copied and pasted it right there and then we're gonna set a font size so we wanna make sure that this is a little bit bigger so we'll do font dash size we'll set this to 18 pixels and then i want to set some padding here so do padding and for this i'm just going to do 25 pixels and then 50 pixels here okay so i also want to make sure there's no border so we are going to style this from scratch so let's just go ahead and set that to none so we're actually not going to use any of the button styling for one of these even though it's going to look the same but i just want to do all this from scratch so let's see for the actual color we want to set a text color and this is going to be white so we'll do pound ff okay and let's see for the position i want to make sure this is at the bottom so we'll do fixed that way we can place this anywhere we want and for that now that it's a fixed position we'll set the bottom value at 1 rem that's how much space we'll have from the bottom we'll set the border radius here of the button itself so border dash radius and this is going to be 8 pixels and then we want to make sure that this is actually completely centered so we'll do left positioning at 50 percent here so 50 and then we just want to do transform and for this we'll do translate x and that's going to be negative 50 so this is how we're going to center this item here and we'll also do cursor pointers i want to make sure that people know it's clickable okay so that should place the button right here so let's see join button i don't know why it's not working maybe i didn't save something we'll go back to room.html join btm i need n okay so let's try that one more time here we go okay perfect so that's where we want it now in the beginning before we actually join a stream i want to make sure that this stream actions container is hidden so we don't see those buttons in the background so let's go ahead and actually hide those so let's see we're looking for stream underscore actions or we'll do double underscore actions and at this point it's display flex so that's how we inline the buttons so let's go ahead and set this to none and then we'll display it as flex when we actually make this appear so we'll go ahead and hide those buttons and there we go so that looks good and i want to make sure that i'm not joining the stream right away so let's go ahead and change that now so we'll go into room underscore rtc and at this point when we join a room we have a join room in it we call this join stream function so i want to go ahead and get rid of this so let's copy this actually and let's just go ahead and go down to the bottom and we're going to create an event an event handler not event handler so we'll do a document.getelementbyid and we're going to create an event listener for the join btn so for the join button so we'll do add event listener let's go ahead and listen for the click event so we'll do on click let's go ahead and call join stream so now we're only going to join a stream when this button is clicked so for this we're going to go back to our join stream function and let's change up a few things here so before we actually get that audio and video track let's go ahead and change up a few values so we'll just go ahead and grab the join stream button so we'll do document.getelementbyid and this is going to be join. btn and immediately what i want to do here is just go ahead and actually hide this button so we'll just do style dot display and we're going to set this to none okay so we're going to hide the join button because we just joined and then we want to go ahead and actually grab the stream actions container and go ahead and actually display that so the document dot get element by class name or elements so let's see in the template it's actually set to class here so we'll go ahead and grab stream actions and we only have this once so it should be an id or it should be an id but at this point we'll just go ahead and just grab the class because that's fine so we'll just grab the stream actions class we need to get the one item so we'll just go ahead and get the first index of zero and then we'll just set this or style dot display and this is going to be to flex so remember the original display when the in the template was flex so instead of saying display block we're going to do display flex so they're all in line okay so we're just going to go ahead and hide the button and then display this one so we still have more work to do but let's test this out so right away when i join i'm no longer toggling my camera so we're just in the room we can chat we can see that we're in it other people join or if they join we can also see them in the chat here so let's just find a different name so let's just do peter and we'll join one okay so we have dennis and peter in the room and they can message each other okay so let's say dennis wants to join the room so dennis can click join let's say we have approval to join the room and there we go so we toggle it and now we are in the room so if i go to peter's side peter is currently not in the room but peter can see dennis streaming now if peter wants to join peter can just go ahead and click join there we go now they are both inside of the stream and it's working exactly how we want it so what i want to do is actually give users the ability to leave now and then also to be able to rejoin the room so we're going to add in an event listener to this button right here that's essentially going to allow users to leave unpublish their stream but still stay in the chat and then if they want to join back again let's say the host gives them permission they can actually jump back into the room so we're just dealing with a little bit of toggling over here making sure that all the functionality actually works so all the clicks are working correctly so let's go ahead and go down to the bottom of the page here and i'm going to create a function called leave stream so it's not going to leave the room but it's simply going to leave the stream so go ahead and do leave stream and we'll make sure that this is an async function so we'll do async and let's go ahead and throw in the event here and let's go ahead and continue the function itself so leave stream is going to be called on the leave button so let's add in that event listener right now so let's copy this and we have a button called leave so we have leave btn let's go ahead and add it to this we can close out room rtm there so we'll do leave dash btn and then we're going to add the event listener of leave stream okay so how is a user going to leave the stream so what exactly is gonna happen so the first thing i wanna do is go ahead and change the styling of the buttons so we'll go ahead and go into join stream and let's just reverse this right here so right here we hide the join button and then display the controls but the first thing i want to do in the leave stream button is go ahead and change this up and i also want to go ahead and prevent default so maybe this was set to a link for some reason so by habit any link i always do prevent default if i feel like that can mess something up here so let's do that and for the join button let's go ahead and do display block so we're inverting this so the join button is now going to be displayed once we leave a stream and then for the stream actions we'll do display none so we simply invert those that looks good and then we want to go ahead and turn off our tracks so i'm going to go ahead and create a loop and we're going to loop through every single track inside of local tracks here and we're going to stop and then close each track here so let's go ahead and create our variable set that to zero then we'll just grab our local tracks so local tracks dot length here so we'll go through that make sure that when that's bigger than i i will just simply increment and then we can go into local tracks and then actually go into each value so let's see local tracks i and then we want to do dot stop so we'll stop the track from playing and just do dots sorry i'm like stuttering a little bit so we want to stop the track and then we'll want to go ahead and close it so you can actually close and or you can actually stop and start a track but once you close it it means the track is closed and you're actually going to create a new one so we're stopping it and then we're closing it so we're looping through all the tracks and stopping that stream now once we do that we want to unpublish our stream so right now our stream is still technically published so we want to go ahead and unpublish it from the channel so we'll call client and then we can do dot unpublish so we've done this before so client.unpublish and we're just gonna unpublish our local tracks audio and video so we're gonna grab the first index which is the audio then we'll do local tracks and then unpublish the video so we'll unpublish those and then if for some reason let's say we were sharing our screen we want to check if we were so we'll just do if local screen tracks so if we ever click share let's make sure we unpublish these too so the local stream screen tracks having a hard time talking here and then we'll just simply unpublish local screen tracks so we're unpublishing all of our tracks then what i want to do is go ahead and remove my item from the dom so i want to make sure that my video frame is removed so we'll do document dot get element by id we'll go ahead and grab our own user container so we'll use backticks user dash container and then we'll throw in our uid so now we're going to remove our video frame from the dom so let's go ahead and call the remove function and that'll get rid of it so the next thing i want to think about is if we happen to join a stream and if our video frame is in focus here so let's say we clicked on our own video frame if we leave i want to make sure that we actually go ahead and hide this display frame and that we resize all the circles that were here so we've dealt with this before we've done this a few times but we haven't dealt with this when we actually leave our stream but we still stay in the room because this functionality is a little bit different before we've just been leaving the room so we don't really have to think about what happens when we actually leave and tearing anything down after that so let's go ahead and run this check so again this should be all really familiar now we're just going to go ahead and check the user in display frame function and we're just going to check if we're currently the id that was in there so if the value is this user container with our id then let's go ahead and take that display frame and we're just going to go ahead and do dot style dot display not display frame but display and then we're going to set that to no so we're just going to go ahead and hide it now i also want to make sure that all the circles are resized so we're going to go ahead and run through this loop again and at this point let's just go into room.js and i think i should have this somewhere here so we're just going to take this loop right here this is inside of the expand video frame function let's grab this and actually i guess we can grab one of these right here so let's grab this one inside of high display because we want to make sure that those frames are actually expanded since we just hit it so we'll bring that in here paste that so we're going to loop through all those video frames and then reset the size so let's try this again let's join then i want to join as a second user so we'll join as let's do steve and then let steve join the stream we'll mute the mics and then let's see i think this is my display frame so if i leave there we go it resized it and then we still see steve's frame here so that's perfect that's exactly what we wanted to deal with so at this point what i want to do is make sure that any time we leave a stream we also remove that video frame for the remote user so i'll show you what i mean by this so right now if we join the room i'll just mute the mic and if we join again with another user let's just do paul this time so if we're in the stream and we happen to leave what happens here is if we leave we tear down our video frame and everything looks good but for the remote users we technically unpublished our stream but we never left the room so the only way we we've actually been tearing down this div right here or that video frame is inside of the handle user unpublish function i believe let's see where did we call that so somewhere down here we called client or user left and that was handle user left so let's go ahead and check out this function so we call this when we actually leave the stream well the issue here is we're actually not leaving the stream we're simply unpublishing but we're still in that video stream our video is not publishing but we're there so we haven't left so the way that i'm going to solve this is right now technically if this user just leaves then it'll work but if that user is still in the video stream i'm just going to handle this in another way so we'll have both functions actually handle that and essentially what i'm going to say is i'm just going to go ahead and send out a message using agora rtm we're just going to send out a message to everybody in that channel saying hey this user has left go ahead and remove their element or their video frame from the dom so it's simply going to be a message that we're going to send out when we actually leave the stream but not the room so in this function right here if we leave a stream let's go ahead and at the bottom let's go ahead and actually send out a chat message so we'll just go ahead and do channel dot send message and with this message type we're going to go ahead and throw in text we'll go ahead and stringify that again so we'll do json.stringify and let's throw in the object and the type here of the actual message is going to be uh let's see what i want to call this i'm just going to go ahead and call this user underscore left so that's what we'll call the type or the the message type so we'll do user underscore left okay so we're sending this message and the other thing that i want to send here is simply the user id or the uid so i want to know what user left so we can send this message to all the other users find that container in the dom and then remove it so we'll throw in our uid so as we leave we tear everything down we unpublish and then we send a message and then we're passing in the type of user left with that uid now if i go into room underscore rtm we have this function to handle these messages so remember we we can send a lot more than just a chat message this can be any kind of message just as long as we're sending data so in that handle message function let's see somewhere here it's a lot of work that we've done here so it's kind of hard to find it handle i think it's towards the bottom okay add message to dom send message handle channel message okay so at this point we're just gonna do if data dot type if this is user underscore left so user underscore left let's go ahead and remove this user from the dom so if this is the type let's go ahead and do document the document dot get element by id and we're just going to go ahead and grab that user container so we'll do user dash container and then we'll throw in that user id so at this point it's data dot uid because we sent that with that channel message and then we're just simply going to remove it so we'll do remove and then i guess all we need to do at this point is see if this user was in the display frame and then simply resize everything so let's go back in here let's take the same exact value and let's just go into this user left function and i think that's all we need we can simply just repeat this make sure everything's resized and then we'll be good to go okay so let's go ahead and check this out we'll join the stream so let's bring in another user and we'll just call this one steve join okay so here's paul's stream so if paul leaves now this stream was removed from here and that's perfect so it's working exactly how we wanted it and paul can join again and everything is working so there's one last thing that i want to work out it's not really an issue but it's gonna remove an error from the console and that is if i happen to leave the stream and if i go into inspect element if i happen to actually close out with this user we're gonna see oh it was actually in this frame right here we're gonna see this at this error right here and essentially when we call handle or user left with agora rtc we're also trying to remove that dom element or we're trying to remove that entire video frame so that's going to cause an issue because that video frame technically doesn't exist because we removed it by sending that message so inside of room underscore rtc in handle user left let's go ahead and just do this let's just make this a quick fix we'll patch it up we'll just do item is equal to this right here so let's go ahead and run a condition so we'll just do if item so if it actually exists then let's remove it so we'll just do item dot remove so it's a quick fix here okay so i just noticed it as i was finishing up this video so let's try this again and let's see so if we join we'll be in frame and if we join as another user just think of another name okay so let's go into inspect here so in console if i leave and then i go in here and then close this out that error doesn't show up anymore everything looks good and it all checks out so that's it for this video i had a lot of fun with this project i hope you enjoy it and be sure to check out the live demo down in the video description so that demo might look a little bit different from what we built out here because i will keep adding features to that so be sure to check that out and make sure to check out my youtube channel dennis ivey that's linked up in the video description so thank you brad for allowing me to collaborate again this is a lot of fun i'm sure it's not going to be the last time i always enjoy doing this so see you all in another video and thank you for everything