Toby Vervaart

Mar 10, 2015

Second screen navigation with Express & Socket.io

Second screen is the term commonly used for describing a secondary screen, usually a mobile device or tablet which is used in conjunction with another (usually passive) device to provide an enhanced experience.

It’s also possible to enhance web pages with a second screen, in fact the W3C even have a working group1 dedicated to providing a specification to help facilitate this interaction. Even though this specifcation isn’t due to be completed until 2016 it’s possible today to build a second screen experience on the web using websockets.

A demonstration of the final result

Project outline

In this article, we’ll look at how to build a website which uses a second screen to navigate. We’ll be using Nodejs2, Express3 and Socket.io4 to achieve this.

This won’t be anything fancy, just a simple proof of concept which will hopefully spark some wider ideas around the usefulness of second screen and website interaction.

Installing dependencies

To start with we’ll create a simple express app and use the server that comes with it. We’ll also install express-static and socket.io as dependencies. If you’ve downloaded the source code for this project then you can just use the command npm install to download all the dependencies. Alternatively you can create your own package.json file and add these yourself.

Creating the back end

Next we’ll create the backend. I’ve created a file called server.js which will act as the server for the application. In it, I’ve created some paths for static files to be server from and two routes, a route for the index page of the application and a dynamic route to serve the individual pages.

One thing to note about the index route is that it performs a crude mobile detection test. This is important to be able to send the correct version of the site to either a desktop or mobile client. In this instance we only want the desktop to display the website pages and the mobile device to display the navigation.

Finally there is a small piece of socket.io code at the end of the file. This creates a ‘room’ to use to broadcast events and an event listener / emitter to send and receive events through websockets. This will allow the application to respond to events from the mobile device and update the state of the desktop application.

var app     = require('express')();
var server  = require('http').Server(app);
var io      = require('socket.io')(server);
var static  = require('express-static');

// Listen on port 8000
// Uses process.env.PORT for Heroku deployment as Heroku will dynamically assign a port
server.listen(process.env.PORT || 8000);

// Serve static files
app.use("/css", static(__dirname + '/css'));
app.use("/js", static(__dirname + '/js'));

// Index route
app.get('/', function (req, res) {
    // Basic user agent check - tests for mobile
    var ua = req.headers['user-agent'];
    if (/mobile/i.test(ua)) {
        // Send mobile to the navigation controls
        res.sendFile(__dirname + '/pages/mobile.html');
    } else {
        // Send desktop to the main site
        res.sendFile(__dirname + '/pages/index.html');
    }
});

// Dynamic page route
app.get('/page/:id', function( req, res ) {
    res.sendFile(__dirname + '/pages/' + req.params.id + '.html');
});

// Socket IO
io.on('connection', function (socket) {
    // Create a room to broadcast to
    socket.join('main');
    socket.on('statechange', function (data) {
        // Broadcast changes to all clients in room
        socket.to('main').emit('urlchange', { url : data.url });
    });
});
server.js

Hooking up the templates

As mentioned in part one, the index page routes to two separate templates based on which device is being used. In the case of the dektop, it simply shows the page with the text ‘home’. On mobile it shows the navigation for the website.

<!DOCTYPE html>
<!--[if lt IE 7]>      <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
<!--[if IE 7]>         <html class="no-js lt-ie9 lt-ie8"> <![endif]-->
<!--[if IE 8]>         <html class="no-js lt-ie9"> <![endif]-->
<!--[if gt IE 8]><!--> <html class="no-js"> <!--<![endif]-->
    <head>

        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <title>Home &ndash; Second Screen</title>
        <meta name="description" content="">
        <meta name="viewport" content="width=device-width, initial-scale=1">

        <link rel="stylesheet" type="text/css" href="/css/common.css" />

    </head>

    <body class="home">

        <h1>Home</h1>

        <script src="/socket.io/socket.io.js"></script>
        <script>
          var socket = io.connect('http://localhost:8000');
          socket.on('urlchange', function (data) {
                //console.log(d.data);
                window.location.href = data.url;
          });
        </script>

    </body>

</html>
index.html

Notice the javascript at the bottom of index.html. This is waiting for an event called ‘urlchange’ and when it’s recieved it will change the window’s location to whatever data has been sent. This code is on all the desktop page templates.

<!DOCTYPE html>
<!--[if lt IE 7]>      <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
<!--[if IE 7]>         <html class="no-js lt-ie9 lt-ie8"> <![endif]-->
<!--[if IE 8]>         <html class="no-js lt-ie9"> <![endif]-->
<!--[if gt IE 8]><!--> <html class="no-js"> <!--<![endif]-->
    <head>

        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <title>Navigation &ndash; Second Screen</title>
        <meta name="description" content="">
        <meta name="viewport" content="width=device-width, initial-scale=1">

        <link rel="stylesheet" type="text/css" href="/css/common.css" />

    </head>

    <body>

        <ul>
            <li><a href="/" class="home">Home</a></li>
            <li><a href="/page/one" class="one">One</a></li>
            <li><a href="/page/two" class="two">Two</a></li>
            <li><a href="/page/three" class="three">Three</a></li>
            <li><a href="/page/four" class="four">Four</a></li>
        </ul>

        <script src="/socket.io/socket.io.js"></script>
        <script>
          // For local dev
          var socket = io.connect();
          [].forEach.call( document.querySelectorAll( 'a' ), function ( a ) {
            a.addEventListener( 'click', function (e) {
                socket.emit('statechange', { url : e.target.href });
                e.preventDefault();
            }, false );
          });          
        </script>

    </body>

</html>
mobile.html

The socket.io javascript on mobile.html behaves differently. It emits events whenever one of the navigation items is clicked. This sends the event to the desktop page which reacts accordingly.

Viewing the site

Viewing the site on your desktop machine is easy, simply run node server.js then browse to http://localhost:8000/ and you’ll be served the website by the node server.

To view the site on your mobile device, you’ll need to find the ip address of your computer on the local network. On Mac OS X go to System Preferences > Network.

On a windows PC open Network Connections by clicking the Start button , and then clicking Control Panel. Then select an active network connection, and then, in the toolbar, click View status of this connection and click Details.

Conclusion

As you can see, it’s quite trivial to set up a website which can be controlled by another device. Feel free to grab the full source code from github and create your own.

You can download the source code here: https://github.com/hellotoby/second-screen-nav