Ring! Sometimes you need an alert to get your attention. Traditional phone systems make this easy – if someone calls you your phone rings. The traditional telephony model assumes the called device is always on an available to ring and this is how it generally works across analog phones, mobiles, VoIP phones, and even desktop calling replacements like Skype. The challenge in the web model is that you can no longer assume the remote device is available to run your program’s ring command. Even if the called party has a browser open, it does not mean they have a tab running your app. This means you need to find some other means of telling the called party to go to your URL. That can be limiting for a lot of apps. Fortunately there are solutions for this.
I regularly review WebRTC-based apps and services. I came across Videolink2.me – a video conferencing, screen sharing, and chat app that includes the ability to receive calls even if you do not have a videolink2.me tab open. Konstantin Goncharuk (@kkooler), founder of Videolink2.me, agreed to share the details on how he used WebRTC’s DataChannel and a Chrome Extension to do this.
Konstantin is a web developer focused on high-load, scalable. Konstantin created Videolink2.me in his spare time with the goal of decreasing the technical and commercial obstacles required for interpersonal communications. We’re happy Konstantin agreed to share his experience and some code on how to do this.
{“intro-by”:”chad“}
Each year bunch of software that used to be a desktop application migrates to Web world. Google started that movement with the Gmail web app and other vendors actively followed that trend so now we have a lot of web-based applications including chats, mail clients, photo edit services and so on.
Web apps are easy to use since they usually don’t require any software installation. However, they do have some of disadvantages – the most important is a lack of integration with the operating system. This means it the app cannot be always on. It will not start automatically after a reboot. WebRTC is creating a new generation of web applications with the same problem. WebRTC web chat users can receive messages only when the chat app is opened. To start a conversation two persons need to open the same page and have agreement at what time they both will be there.
How about spontaneous communication like traditional phone apps allow? How can we notify user that he received new message?
I have been working on the Videolink2.me service that provides WebRTC based online video calls. We always asked this question to ourselves:
How can we let our users receive calls when they don’t have Videolink2.me opened?
Finally we found a way! Below I will share our experience and describe how to use Chrome extension and WebRTC DataChannel to receive messages directly into browser without needing to have web page opened.
Simple WebRTC chat
Let’s start from making simple WebRTC based chat- when one opens the page they should be able to enter their chat name and the name of the user they would like to chat with. And as soon as both users are online they can start chatting.
So our page will contain fields for user and contact name, chat message and log of sent and received chat messages:
I’ve used Twitter Bootstrap to make the design more beautiful than my developer hands can produce, PeerJS library to simplify DataChannel usage and of course JQuery. You can get a PeerJS API key here or setup your own PeerJS signaling server with this code.
Here is the html of the page above:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
<div class="container"> <h4>Enter your chat name and name of the user you want to connect</h4> <div class="row"> <div class="col-md-6"> <div class="form-group"> <label>You</label> <input type="text" class="form-control" id="myName" placeholder="Enter your chat name"> </div> </div> <div class="col-md-5"> <div class="form-group"> <label>Contact</label> <input type="text" class="form-control" id="userName" placeholder="Enter user chat name"> </div> </div> <div class="col-md-1" style="padding-top:24px"> <button type="button" id="saveName" class="btn btn-default">Enter</button> </div> </div> <div class="waiting" style="display:none;text-align:center">Waiting for connection...</div> <div class="form-group onready"> <textarea class="form-control" style="width:100%;height:300px" id="log"></textarea> </div> <div class="onready"> <div class="row"> <div class="col-md-10"> <input class="form-control" type="text" id="input" style="width:100%"> </div> <div class="col-md-2"> <button id="send" class="btn btn-primary">Send</button> </div> </div> </div> </div> |
We also need simple javascript that provides interaction with UI and establishment of p2p data connection:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
var peer, conn, connected = false; /** * When "Enter" button clicked we need to: * - disable user/contact fields and enable "waiting" block * - create p2p connection * - add listener to get notified when second user is connected */ $('#saveName').click(function() { $('.waiting').show(); $('#saveName').prop('disabled', true); peer = new Peer($('#myName').val(), {key: '{{YOUR_PEERJS_KEY}}'}); conn = peer.connect($('#userName').val()); conn.on('open', onConnect); conn.on('data', function(data){ addMessage($('#userName').val(), data); }); peer.on('connection', function(externalConnection) { if (!connected) { conn = peer.connect($('#userName').val()); onConnect(); } externalConnection.on('data', function(data){ addMessage($('#userName').val(), data); }); }); }) /** * When second user is connected we can show chat controls and hide waiting block */ function onConnect() { connected = true; $('.waiting').hide(); $('#myName, #userName').prop('disabled', true); $('.onready').slideDown(); } /** * This functions adds message to local chat log */ function addMessage(author, message) { $('#log').text($('#log').text() + author + ": " + message + "\n"); } /** * Add new chat message locally and send via DataChannel */ $('#send').click(function() { conn.send($('#input').val()); addMessage('You', $('#input').val()); $('#input').val(''); }) |
You can check it all together here: http://dl.dropboxusercontent.com/u/178301/wchat/index.html
Since the DataChannel supports only communication in one direction we need to have 2 channels for each chat: one for incoming and one for outgoing messages.
When we click to “Enter” button, outgoing peer connection is established and we wait until second user does the same. After everyone is connected we receive data over the incoming DataChannel.
So our incoming DataChannel is outgoing for other user and his outgoing is incoming for us.
At this point everything looks easy – we just need to establish a connection and send/receive messages via the created channels.
What happens when one user wants to chat but second is not at the page or he’s not connected? They need to have agreement at what time they both will synchronously login to have a chat. That doesn’t sound user-friendly, right?
How great would it be if the user could get a notification when somebody is waiting for him. It would be even more amazing if that user could get the notification without need to have chat page opened at all.
That’s when Chrome extension appears on the scene.
How can we use WebRTC in Chrome extension?
Chrome extensions provide an easy to use javascript API to build in extra features into browser. In our case we want to make extension that will let us know when somebody is waiting for us in chat. To make it simple we will check only connections from one user (let’s say from Bob).
So extension should establish a WebRTC connection (set our name and name of the person we’re waiting for) and notify us when second user is connected.
It will contain background script that listens to the WebRTC connection and as soon as a connection is established it will show the notification.
Let’s start from creating connection and catching an event when second user connects. To do this we can do some slight modifications to javascript we’ve made for chat webpage above:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
var myName = 'Konstantin'; var contactName = 'Bob'; //This function will show notification, let's keep it empty for now function onConnect() { } //This part taken from webpage. We create connection and wait until second part will join. var peer = new Peer(myName, {key: '{{YOUR_PEERJS_KEY}}'}); var conn = peer.connect(contactName); peer.on('connection', function(externalConnection) { onConnect(); }); |
The only part we’re missing now is creating notification. To do that we can use Chrome Notification API:
1 2 3 4 5 6 7 8 |
function onConnect() { chrome.notifications.create(null, { type: "basic", title: "Your contact is online", message: "He's waiting you in webchat.", iconUrl: 'icon.png' }); } |
At this point we have everything we need to be notified when Bob enters webchat. Let’s try it in action.
How to build our code as Chrome extension
To make it up as Chrome extension we need to put javascript we’ve made together with Manifest file into zip and upload to Chrome store or just install locally. Manifest file is a json file that describes extension: what files it use, which permissions requires etc. All possible properties are listed here: http://developer.chrome.com/extensions/manifest
Our manifest.json should contain only extension name, version, background page file and requires permission to show notifications:
1 2 3 4 5 6 7 8 9 10 11 |
{ "manifest_version": 2, "name": "WebRTC test", "version": "0.1", "background": { "scripts": ["background.js"] }, "permissions": [ "notifications" ] } |
Before testing we need to add couple of adjustments to make it work in Chrome environment. Since we use Peer.js, we need to download their javascript, pack it in with extension and it will load dynamically when background page starts:
1 2 3 4 |
var file = document.createElement('script') file.setAttribute("type","text/javascript") file.setAttribute("src", "peer.js"); document.getElementsByTagName("head")[0].appendChild(file); |
And to check if it’s loaded before creating Peer connection we’ll add wrapper function around initialization code:
1 2 3 4 5 6 7 8 9 |
function init() { if (typeof Peer === 'function') { //Init connection } else { //Wait 1 second and try again setTimeout(init, 1000); } } init(); |
So now we have everything ready to launch, here’s new background.js with all adjustments:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
var file = document.createElement('script') file.setAttribute("type","text/javascript") file.setAttribute("src", "peer.js"); document.getElementsByTagName("head")[0].appendChild(file); //Let's start from hardcoded user names and later will fetch it from configuration var myName = 'Konstantin'; var contactName = 'Bob'; //This function will show notification, let's keep it empty for now function onConnect() { console.log('Connected'); chrome.notifications.create("", { type: "basic", title: "Your contact is online", message: "He's waiting for you in webchat.", iconUrl: 'icon.png' }, function() {}); } //This part taken from webpage. We create connection and than just wait until second part will join. function init() { if (typeof Peer === 'function') { console.log('Init'); var peer = new Peer(myName, {key: '{{YOUR_PEERJS_KEY}}'}); var conn = peer.connect(contactName); peer.on('connection', function(externalConnection) { onConnect(); }); } else { console.log('waiting...'); setTimeout(init, 1000); } } init(); |
Our extension has two files now: background.js and manifest.json. To upload it to Chrome, we need to put them into zip archive, open Menu -> Tools -> Extensions, click “Developer mode” checkbox, then “Load unpacked extension…” button and select our archive.
After you upload it to Chrome, background page will establish WebRTC data connection and show notification when second user is connected.
How it works behind the scene
Each Chrome extension is just a set of javascript files that run in defined context. In our case background.js starts when browser is opened and keeps running until it’s closed (if you open Chrome task manager Menu -> Tools -> Task manager, you will find it in a list of tasks).
So it doesn’t matter what framework or signaling you use, as long as its javascript it can be wrapped as a Chrome extension.
We also use Chrome extension in Videolink2.me, to let our users receive video calls, even when they don’t have their page opened. So people can get connected easy, without making appointments or planing exact time.
You can find all code written above in github: https://github.com/kooler/webrtc-in-chrome-extension/
Note that PeerJS only allows one login per ID. The way our simple app is written you cannot chat back using the same browser that received the notification – the ID is already being used by the Chrome extension. In Videolink2.me we use self-made p2p library that can deal with the same user logged in multiple times. So instead of sending message once, we “broadcast” it to all user connectors. That’s how our users can receive notifications in both the webpage and extension (and theoretically in as many connectors as can exist 🙂 ).
I hope this article was useful for you, if you can any questions, I’m waiting in comments 🙂
{“author”:”Konstantin Goncharuk“}
Victor Pascual says
This specification defines a “Push API” that provides webapps with scripted access to server-sent notifications, for simplicity referred to here as push notifications, as delivered by push services. Push services are a way for application servers to send messages to webapps, whether or not the webapp is active in a browser window.
https://dvcs.w3.org/hg/push/raw-file/default/index.html
SimplePush is a way for application developers to send messages to their web applications. Rather than have applications continuously poll your server for the latest information, you can take advantage of the SimplePush API. This is a very low cost service that lets servers tell clients “Hey, there’s something interesting over here! You ought to do something about that.”
https://wiki.mozilla.org/WebAPI/SimplePush
Google Cloud Messaging for Chrome (GCM) is a service for signed-in Chrome users that helps developers send message data from servers to their Chrome apps and extensions. The service is intended to wake up an app or extension, and/or alert a user.
https://developer.chrome.com/apps/cloudMessaging
y.iwase says
Thanks for great article.
After reading this article, I understood there are 2 cases :
1. When chrome browser opens and websites such as Videolink2.me isn’t open
=> use chrome extension
2. When chrome browser doesn’t open
=> need another mechanism to notify user (e.g. use DM)
Is there a nice approach for 2nd situation?
Konstantin Goncharuk says
To notify user in 2nd situation we need to have some software running separately from Chrome, that listens for updates from our service. Unfortunately there is no universal answer to this question, sometimes you can write an app that will show notifications when Chrome is closed, or maybe send message to Jabber or just drop email.
Martin says
Is the Chrome notifications API (and GCM API) supported on Android?
Leandro says
Hello,
These code is working now a day?
Because I gave a try and nothing happens when chrome loads up.
Thanks,
Leandro.