WebRTC Demystified — Text Chatting

Implementing data channels for text chatting

Furkan Can Baytemur
Orion Innovation techClub
6 min readOct 17, 2022

--

In the previous article, we implemented the first of three functionalities of the Magny application; video chatting. We have learned the following:

  • Creating Offer and Answering
  • Signaling with the help of Firestore
  • Tracking Connection State
  • Closing the Connection

In this article, we will learn how to open a data channel and how we can use this data channel to transfer text messages to remote peers. Firstly, we will create a text channel window inside our application and we will implement the data channel to make this window a functional chatting window.

UI for Text Chatting

This is a simple code to add to our chat window. Since .container div has a flex display, this chat box will show in the right section of the page. You might ask why there is a blank #chat-box div in the code. It is our boilerplate to add our messages to show. We will be adding them from JavaScript by DOM manipulation.

Here is the stylesheet for our text chatting elements. The most important features here are that we disabled the display for the scrollbar and gave overflow-y a scroll feature. These tweaks made the chat window embedded in our application. You will see that it is a window that can be scrollable in itself. .talk-bubble will show itself when we implement the DOM manipulation. You check our progress and take a look at the UI from the branch below:

Setting up a Data Channel

Once we create the connection, we can open a data channel. This data channel can be used to transfer various data such as strings, integers, objects (in JSON format) or even files (in buffer format). In this case, we will be sending and receiving simple strings. Since this is a new feature, let’s create a brand new TypeScript file named text.ts . First of all, import peerConnection from connect.ts

We will be opening the data channel on both peers as we did for opening the connection. However, most of the code is similar for both peers. Therefore, we shall create a common function for them.

  • This function takes a RTCDataChannel as an argument.
  • Then, it enables the message input.
  • Message input has an event listener for every key pressed. It is used to check if the input field is empty or not. If it is not empty, we enable the send button. This is done to prevent blank messages from being sent.
  • Lastly, it adds an event listener to the form’s submit event which takes the value from message input and sends it to the remote peer. It executes the createBubble() function and clears the input. Since sending a message is not included in keyup event of the input field, we disable the button.

Let’s see what createBubble function does:

  • It takes the message and a boolean value as parameters. The boolean value determines if the sender is us or not.
  • As you can remember, we have written some style sheets for bubbles. We create a div with talk-bubble class and add another class according to the parameter boolean value. If it is a message sent from us, we put it to right; if it is a message incoming from a remote peer, we put it to the left of the chat window.
  • The last thing we need to do is to add this new bubble to the chat window. We also scroll the window to the bottom so that users would see the new message without the need to scroll.

Common functions are ready. Let’s create functions needed to establish data channels.

  • The one creating the data channel will be the initiator of the call. We give it a name since we will be using another channel for file sharing.
  • We track the openness of the data channel. When it is open, we execute openTextChannel function to prepare a chat window.
  • Lastly, it waits for messages. When a message comes through textChannel , we create a bubble from it.

Let’s take a look at what the answerer of the call will do to complete text messaging.

  • The answerer doesn’t create a data channel. If they do so, there will be two data channels and the communication cannot be established.
  • So it listens for any data channel to be created. If created data channel has the label of textChannel , it sets the message window like the initiator of the call.
  • Likewise, it takes messages coming from the data channel and shows them in the chat window.

All is set to establish text messaging. However, these are just functions, we don’t execute them. Let’s execute them in their respective places in main.ts .

We create data channels before making the offer and answer because ICE candidates and SDP objects are created according to the data we will be transferring. That is why we put video and audio streams to peerConnection before even establishing it.

Let’s take a look at the result:

As you can see, we not only established a video chat but we also established text communication. You can check out the progress so far from the branch below:

Do note that this is real-time P2P communication. We don’t store any value on the server. Refreshing the page will reset messages. One can argue that these messages could be kept on users’ computers but there is no guarantee that a user won’t change the content of the messages. For now, think of this app as one time messaging application in which data resets when we exit the application.

Speaking of reset, we need to add more functionality to our hang-up button since we added a new feature.

Reset the Connection

In the previous article, we added functionality to close the connection. However, the app doesn’t allow us to reconnect to a new peer. We need to refresh the page and this is not a good way to provide a user experience. So we will be reconfiguring the hang-up function.

Add this function to the end of connect.ts file. To reset the connection, it simply closes the connection and creates a new one. The reason why we created a new function in a separate file is that we cannot assign a value to an imported variable. Do note that we changed peerConnection to be a let variable instead of const since we assign a new RTCPeerConnection . You may notice that there is a function named trackConnection . Let’s explore that now.

Since we need to reopen peerConnection , we need to listen for connection events again. Let’s wrap it under a function like this:

We added this function to resetConnection function. In addition, add it right after we declare peerConnection at the beginning of connect.ts file.

One thing left to do is reset everything. Let’s reconfigure our hang-up function.

Firstly, we execute the resetConnection function to re-declare peerConnection . After that, we reset and clear the following parts:

  • Chat input and send button
  • Chat itself
  • Offer input
  • Call and Answer buttons
  • Camera button
  • Video elements
  • Hang-up button

By resetting them, we are turning the app to its initial state as if we refreshed the page. You can reach what we have done so far from the branch below:

That is it for text chatting. You can further improve it by adding timestamps or giving it the functionality to download chat history.

In the next article, we will be adding the file-sharing feature. Stay tuned for more!

Until next time…

--

--