Simple Chat App using WebRTC (PeerJS)

Simple Chat App using WebRTC (PeerJS)

What does it mean when we say peer to peer?

P2P is direct connection between two devices to transfer data to-and-fro without the need of a central server. The traffic does not go to or from any central server, but directly from one device to the other.

What is WebRTC?

WebRTC stands for web real-time communications. WebRTC was not supported by all the browsers until recently and is also available in most mobile browsers.

WebRTC can be used for multiple tasks, but real-time peer-to-peer audio, video or data communications is the primary benefit.

PeerJS

PeerJS wraps the browser's WebRTC implementation to provide a complete, configurable, and easy-to-use peer-to-peer connection API. Equipped with nothing but an ID, a peer can create a P2P data or media stream connection to a remote peer.

Let's Start

We will make use of peerjs to communicate p2p between two different browser sessions.

To begin with make a simple html file index.html and put the basic html scaffold with a scirpt tag. Before the scipt tag, import the peerjs js file.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>P2P</title>
</head>
<body>


    <script src="https://unpkg.com/peerjs@1.2.0/dist/peerjs.min.js"></script>
    <script>

    </script>
</body>
</html>

Next lets make a basic layout like the following - Preview

Add the following html code in the body -

<input id="peerid" placeholder="My ID"> 
<button onclick="join()">Join</button><br>

<input type="text" id="fpeerid" placeholder="Peer ID"> 
<button id="connect" onclick="connect()">Connect</button><br>

<input type="text" id="msg" placeholder="Message..">
<button id="send" onclick="sendMessage()">Send</button> <br>

<ul id="messages">

</ul>

Now starting with the js part -

When we click on the join button, we call a join function, where we need to connect to the PeerJS Singaling server.

Connect to signalling server


var conn
var peer

function join() {
    console.log("Connecting..")
    peer = new Peer();

    // when connection is created, handle the event - 
    peer.on('open', function (id) {
        console.log('Connected to Signaling Server ID : ' + id);
        // set the input value
        var peerIDField = document.querySelector("#peerid")
        peerIDField.value = id
    });
}

We should be able to see our peer id when we click the join button. Next step is to try and connect to a peer id of another device. This can only be done after we are connected to the signaling server, so we can use the global var peer to try and connect.

Initiate connection to a peer using their ID

function connect() {
    var fpeerIDField = document.querySelector("#fpeerid")
    console.log("connecting to " + fpeerIDField.value)
    conn = peer.connect(fpeerIDField.value);

    // open event called when connection gets created
    conn.on('open', function () {
        console.log("connected")
    });
}

When we create a new connection, the recieving peer triggers an event connection, this event will come only if we have a active peer. To ensure that we can add it inside our join function and modify it as following -

Accept a connection coming from a Peer

function join() {
    console.log("Connecting..")
    peer = new Peer();

    // when connection is created, handle the event - 
    peer.on('open', function (id) {
        console.log('Connected to Signaling Server ID : ' + id);
        // set the input value
        var peerIDField = document.querySelector("#peerid")
        peerIDField.value = id
    });

    peer.on('connection', function (c) {
        conn = c
        console.log("New connection : ")
        console.log(conn)

        // set the friend peer id we just got
        var fpeerIDField = document.querySelector("#fpeerid")
        fpeerIDField.value = c.peer
    });
}

Now we are able to create and accept connections. Next we need to be able to send data and recieve data.

Send data to an active connection -

function sendMessage() {
    var msg = document.querySelector("#msg")
    console.log("sending message")
    // send message at sender or receiver side
    if (conn && conn.open) {
        printMsg("Me : " + msg.value)
        conn.send(msg.value);
    }
}

function printMsg(msg) {
    var messages = document.querySelector
    messages.innerHTML = messages.innerHTML + "<li>" + msg + "</li>"
}

The above code will send the message and update our list with that message using printMsg function. We are able to send messages, but how do we recieve them? We have two points - An initiator of the connection and a receiver of the connection. The respective connection objects -

  • When we initiate i.e. call the connect method we get conn
  • When we receive the connection in join using connection event, we get the connection in c as an arg and save it to conn global variable.

We need to handle receive of message in both these cases, so we add a data event listener in both connection event and connect function like following -

Receive data

function join() {
    console.log("Connecting..")
    peer = new Peer();

    // when connection is created, handle the event - 
    peer.on('open', function (id) {
        console.log('Connected to Signaling Server ID : ' + id);
        // set the input value
        var peerIDField = document.querySelector("#peerid")
        peerIDField.value = id
    });

    peer.on('connection', function (c) {
        conn = c
        console.log("New connection : ")
        console.log(conn)

        // set the friend peer id we just got
        var fpeerIDField = document.querySelector("#fpeerid")
        fpeerIDField.value = c.peer

        // handle message receive
        conn.on('open', function () {
            // Receive messages - receiver side
            conn.on('data', function (data) {
                console.log('Received', data);
                printMsg("Friend : " + data)
            });
        });
    });
}

function connect() {
    var fpeerIDField = document.querySelector("#fpeerid")
    console.log("connecting to " + fpeerIDField.value)
    conn = peer.connect(fpeerIDField.value);

    // open event called when connection gets created
    conn.on('open', function () {
        console.log("connected")
        // Receive messages - sender side
        conn.on('data', function (data) {
            console.log('Received', data);
            printMsg("Friend : " + data)
        });
    });
}

If you did everything properly, we should now be able to

  • register to signaling server and get our peer id
  • connect to a peer id
  • receive connection request from a peer
  • send message to an active peer
  • receive messages in both sender and receiver sides

The final code should look like the following -

Final Code

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>P2P</title>
</head>
<body>
    <input id="peerid" placeholder="My ID"> 
    <button onclick="join()">Join</button><br>

    <input type="text" id="fpeerid" placeholder="Peer ID"> 
    <button id="connect" onclick="connect()">Connect</button><br>

    <input type="text" id="msg" placeholder="Message..">
    <button id="send" onclick="sendMessage()">Send</button> <br>

    <ul id="messages">

    </ul>

    <script src="https://unpkg.com/peerjs@1.2.0/dist/peerjs.min.js"></script>
    <script>
        function join() {
            console.log("Connecting..")
            peer = new Peer();

            // when connection is created, handle the event - 
            peer.on('open', function (id) {
                console.log('Connected to Signaling Server ID : ' + id);
                // set the input value
                var peerIDField = document.querySelector("#peerid")
                peerIDField.value = id
            });

            peer.on('connection', function (c) {
                conn = c
                console.log("New connection : ")
                console.log(conn)

                // set the friend peer id we just got
                var fpeerIDField = document.querySelector("#fpeerid")
                fpeerIDField.value = c.peer

                // handle message receive
                conn.on('open', function () {
                    // Receive messages - receiver side
                    conn.on('data', function (data) {
                        console.log('Received', data);
                        printMsg("Friend : " + data)
                    });
                });
            });
        }

        function connect() {
            var fpeerIDField = document.querySelector("#fpeerid")
            console.log("connecting to " + fpeerIDField.value)
            conn = peer.connect(fpeerIDField.value);

            // open event called when connection gets created
            conn.on('open', function () {
                console.log("connected")
                // Receive messages - sender side
                conn.on('data', function (data) {
                    console.log('Received', data);
                    printMsg("Friend : " + data)
                });
            });
        }

        function sendMessage() {
            var msg = document.querySelector("#msg")
            console.log("sending message")
            // send message at sender or receiver side
            if (conn && conn.open) {
                printMsg("Me : " + msg.value)
                conn.send(msg.value);
            }
        }

        function printMsg(msg) {
            var messages = document.querySelector("#messages")
            messages.innerHTML = messages.innerHTML + "<li>" + msg + "</li>"
        }
    </script>
</body>
</html>

A very similar working code is present at -