Phone to Browser HTML5 Gaming Using Node.js and Socket.io

ss

With the advent of new Chrome Experiments such as Maze and RollIt, we’ve been discovering a new way of HTML5 gaming – using our smartphones as controllers to play browser games.

While this seems quite magical up front, the inner workings are actually quite simple.  In this blog, I’ll be showing how to set up this type of connection using a few different technologies:

  • Node.js – To serve our app
  • Express – Web App Framework for Node.js
  • Socket.io – To create the websocket connection between our browser and phone
  • HexGL – The open-source HTML5/WebGL racing game by Thibaut Despoulain.  We will add the phone-controller feature.

This tutorial assumes an Android/iOS device, and CentOS as our base environment. It will also assume we are running Chrome or Firefox browsers to play the game.

If you’d like to skip ahead, the demo is available here.
The full source code is also available on GitHub

Table of contents:

  1. Set Up Environment
  2. Create the HTTP Server
  3. Create the serverside websocket interface
  4. Create the clientside websocket interface
  5. Implementation and Conclusion

Set Up Environment

We begin by setting up Node.js :

git clone https://github.com/joyent/node.git
cd node
git checkout v0.10.12-release
./configure
make
sudo make install

Notes:

  • We git checkout the latest release branch.  All branches can be found by doing git branch -a
  • If you get an error while trying to execute ./configure, you probably need Python 2.7 installed
  • There are also Windows and Mac installers located here

Once installed, we now have access to npm, the Node.js package manager, and can install Socket.io:

npm install socket.io

We are now ready to set up our project.

Let’s go ahead and download HexGL:

git clone https://github.com/BKcore/HexGL.git

Navigate into HexGL and create a “server” directory.
Create a file named “package.json”. This will serve as the config/metadata for our project so Node.js can serve it.

HexGL/server/package.json

{
    "name": "HexGLPhoneController",
    "version": "0.0.1",
    "description": "Control HexGL with phone tilt",
    "dependencies": {
        "socket.io": "latest",
        "express": "3.x"
    },
    "author": "Robert Devitt, rdevitt@artandlogic.com"
}

The important things here are the dependencies. Socket.io will establish the phone-to-browser connection, and Express is a web app framework that will allow us to serve our app easily.

Now, while in the server directory, have Node.js initialize the project:

npm install

This will create a “node_modules” folder which contains the dependencies.

We are now ready to begin coding!

Create the HTTP Server

First, we’ll set up the HTTP Server. Create a file named “server.js”:
HexGL/server/server.js

// Dependencies
var express = require('express');
var http = require('http');
var io = require('socket.io')
var crypto = require('crypto');

// Set up our app with Express framework
var app = express();

// Create our HTTP server
var server = http.createServer(app);

// Configure the app's document root to be HexGl/
app.configure(function() {
   app.use(
      "/",
      express.static("../")
   );
});

// Tell Socket.io to pay attention
io.listen(server);

// Tell HTTP Server to begin listening for connections on port 3250
server.listen(3250);

Now would be a good time to test if our server is working! Start the server by simply executing:

node server.js

You should see a simple message:
info – socket.io started

Ensure that the HexGL game comes up by opening http://yourserver.com:3250/ in your browser

Create the serverside websocket interface

We’ll now set up the websocket part of our Node.js server. We have several requirements:

  • Identify client types (is this client the browser “game” or the phone “controller”?)
  • Connect the controller client to the game client
  • Send controller data to the game client

To link the browser and the phone, the user will need to first open the game in the browser, which will generate a code that the user will then enter into their phone.  Once authenticated, the game will automatically start.

The Maze Chrome Experiment uses a fancier version of this, which automatically appends the code into the URL, and using Chrome tab-sync the user can open the same tab on their phone without having to enter a code.  For the purposes of this tutorial though, we’ll keep it simple.

The server-side socket conversation looks like this:
Client: Connect
Server: Welcome!
______________________
Client: My device type is 'game'
Server: Ok, here is your game code, show it to the user.
______________________
Client: My device type is 'controller' and my game code is ####
Server: Ok, your game code is valid, begin playing!
Client: Accelerate!
Server: Telling the game client to accelerate!
...

After a client is connected, only the controller client will need to send commands. The game client will only receive.

Working in the same HexGL/server/server.js file:

// Sockets object to save game code -> socked associations
var socketCodes = {};

// When a client connects...
io.sockets.on('connection', function(socket)
{
   // Confirm the connection
   socket.emit("welcome", {});

   // Receive the client device type
   socket.on("device", function(device)
   {
      // if client is a browser game
      if(device.type == "game")
      {
         // Generate a code
         var gameCode = crypto.randomBytes(3).toString('hex');

         // Ensure uniqueness
         while(gameCode in socketCodes)
         {
            gameCode = crypto.randomBytes(3).toString('hex');
         }

         // Store game code -> socket association
         socketCodes[gameCode] = io.sockets.sockets[socket.id];
         socket.gameCode = gameCode

         // Tell game client to initialize
         //  and show the game code to the user
         socket.emit("initialize", gameCode);
      }
      // if client is a phone controller
      else if(device.type == "controller")
      {
         // if game code is valid...
         if(device.gameCode in socketCodes)
         {
            // save the game code for controller commands
            socket.gameCode = device.gameCode

            // initialize the controller
            socket.emit("connected", {});

            // start the game
            socketCodes[device.gameCode].emit("connected", {});
         }
         // else game code is invalid,
         //  send fail message and disconnect
         else
         {
            socket.emit("fail", {});
            socket.disconnect();
         }
      }
   });

   // send accelerate command to game client
   socket.on("accelerate", function(data)
   {
      var bAccelerate = data.accelerate;
      if(socket.gameCode && socket.gameCode in socketCodes)
      {
         socketCodes[socket.gameCode].emit("accelerate",
          bAccelerate);
      }
   });

   // send turn command to game client
   socket.on("turn", function(degrees)
   {
      if(socket.gameCode && socket.gameCode in socketCodes)
      {
         socketCodes[socket.gameCode].emit("turn", degrees);
      }
   });
});

// When a client disconnects...
io.sockets.on('disconnect', function(socket)
{
   // remove game code -> socket association on disconnect
   if(socket.gameCode && socket.gameCode in socketCodes)
   {
      delete socketCodes[socket.gameCode];
   }
});

Now that we’ve set up the server-side socket event handling, we’ll need to set up the client-side.

Create the clientside websocket interface

To keep this tutorial concise, I’ll explain the Socket.io part of the code. If you’d like to view other code that updates the phone interface and emulates controls, you may view that code in the full source.

Create a phoneController.js file to hold our client-side logic –
HexGL/js/phoneController.js

var server = 'http://yourserver.com:3250';

var initPhoneController = function()
{
   var controller = $("#controller");
   var gameConnect = $("#gameConnect");
   var wheel = $("#wheel");
   var status = $("#status");

   // If client is a mobile device
   if(  /Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent) )
   {
      // Show the controller ui with gamecode input
      controller.show();

      // When connect is pushed, establish socket connection
      $("#connect").click(function()
      {
         var gameCode = $("#socket input").val();
         var socket = io.connect(server);

         // When server replies with initial welcome...
         socket.on('welcome', function(data)
         {
            // Send 'controller' device type with our entered game code
            socket.emit("device", {"type":"controller", "gameCode":gameCode});
         });

         // When game code is validated, we can begin playing...
         socket.on("connected", function(data)
         {
            // Hide game code input, and show the vehicle wheel UI
            $("#socket").hide();
            wheel.show();

            // If user touches the screen, accelerate
            document.addEventListener("touchstart", function(event){
               socket.emit("accelerate", {'accelerate':true});
            }, false);

            // Stop accelerating if user stops touching screen
            document.addEventListener("touchend", function(event){
               socket.emit("accelerate", {'accelerate':false});
            }, false);

            // Prevent touchmove event from cancelling the 'touchend' event above
            document.addEventListener("touchmove", function(event){
               event.preventDefault();
            }, false);

            // Steer the vehicle based on the phone orientation
            window.addEventListener('deviceorientation', function(event) {
               var a = event.alpha; // "direction"
               var b = event.beta; // left/right 'tilt'
               var g = event.gamma; // forward/back 'tilt'

               // Tell game to turn the vehicle
               socket.emit("turn", b);
            }, false);
         });

         socket.on("fail", function()
         {
            status.html("Failed to connect");
         });
      });

   }
   // continued below ...

A note about the HTML5 “deviceorientation” event above. For more information on this event and its properties, see here

   // ... continued from above
   // If client is browser game
   else
   {
      var socket = io.connect(server);

      // When initial welcome message, reply with 'game' device type
      socket.on('welcome', function(data)
      {
         socket.emit("device", {"type":"game"});
      });

      // When we receive our game code, show the user
      socket.on("initialize", function(gameCode)
      {
         $("#gameConnect").show();
         $("#gameCode").html(gameCode);
      });

      // When the user inputs the code into the phone client,
      //  we become 'connected'.  Start the game.
      socket.on("connected", function(data)
      {
         $("#gameConnect").hide();
         $("#status").hide();

         // Start HexGL
         init();
      });

      // When the phone is turned, turn the vehicle
      socket.on('turn', function(turn)
      {
         // Emulate turn controls
      });

      // When the phone is touched, accelerate the vehicle
      socket.on("accelerate", function(accelerate)
      {
         // Emulate accelerate controls
      });
   }
};

Implementation and Conclusion

Finally we can implement our code into HexGL. For details on how this is done, see the following files:

Also make sure you include socket.io.js to have access to Socket.io clientside:

<script type="text/javascript" src="/socket.io/socket.io.js"></script>

In conclusion, the magic is actually websocket work in combination with new HTML5 phone event support like DeviceOrientation.

Node.js will generate a unique id that is associated with a socket connection to the browser game.  Once entered into your phone, a “route” is made through the server and all commands from your phone are sent to the browser game.

Node.js and Socket.io make this very easy to do.

I hope this tutorial was clear and opened your mind to more ideas and possibilities. Post questions or comments below!

The following two tabs change content below.

Tags: , , , , , , , , ,

8 Comments

  1. Hi Robert,
    Thanks for your excellent post. I actually wanted to see the demo – however vigrond.com link doesn’t work, I waited for long time for it. But it never came up in safari.

    Reply
    • Hi Rana. If you are running OSX, you will have to enable WebGL in Safari. If not, you’ll have to use Chrome or Firefox, which have WebGL enabled by default.

      Reply
  2. Hi Robert,

    In my chrome browser on mac I tried to open http://vigrond.com:3250/ – and I got ‘This webpage is not available’. I’ve already paired my android with my Mac (Chrome browser) and successfully played the Roll It WebGL demo.

    Thanks for your help….

    Reply
  3. Hi Robert,

    It worked!!! Thanks…

    Reply
  4. Hello, First of all I want to thanks you for this interesting tutorial, I’ve tried to follow the steps in this tutorial, before using the mobile phone controller for the game worked perfectly, When I’ve completed all steps I get the following error when attempting to start the server:
    /root/HexGL/server/server.js:30
    io.sockets.on(‘connection’, function(socket)
    ^
    TypeError: Cannot call method ‘on’ of undefined
    at Object. (/root/HexGL/server/server.js:30:12)

    I searched the internet and suggested somewhere this little change in the code:
    io.listen(server); <== io = io.listen(server);
    When applied I could start the server, when I load the page in the browser remains gray, but when i the load it on the phone it request for the sync code.
    Any idea how to fix it?

    Reply
  5. I had some errors in the code, but in the end it has worked, it’s amazing what you can do with nodejs and the other modules …
    Thanks you very much!

    Reply
    • I’m glad you got your errors fixed. You can always refer to the git repository linked above for the complete working code. And also keep node.js up to date! I had an issue with the server crashing every couple days, but updating Node.js fixed that.

      Reply
  6. Hi Robert ,

    Thanks for lovely article, but i have one issue. Actually its working properly in computer but not in phone device. Can you advice me what i am doing wrong so that i can resolve this.

    Thanks
    Amit

    Reply

Trackbacks/Pingbacks

  1. 30 Tutorials for Developing HTML5 Web Browser Games | WarWebDev - […] Gamedev using Node.js and Socket.io […]
  2. 30 Useful Tutorials for Developing HTML5 Web Browser Games - […] Gamedev using Node.js and Socket.io […]
  3. 30 Tutorials for Developing HTML5 Web Browser Games - Bibliotecnologia.com | Bibliotecnologia.com - […] Gamedev using Node.js and Socket.io […]

Submit a Comment

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>