Real-time Games Using HTML5, WebSockets, Nodejs, & Socket.io

Ever imagined playing a game on the web by simply logging in – no local installation required, no licenses and you can continue from where you left off! Imagine multi-player games from your browser or phone – basically ‘gaming in the cloud’. Ok – that sounded pretty cliche 🙂 But its already here folks and faster and better then we can imagine!

What started off as a proof of concept for us has quickly gathered shape to build something of real potential. What we wanted to do was

  • Launch a game on a common display in a web browser.
  • Control the game from different browsers (desktop or mobile).
  • Manage notifications between controllers and the game.
  • Ensure that no installation required what so ever, even as browser plugins.
  • Measure the latency involved.

The Initial Investigation and options

Nodejs boasts of high concurrency, is a Javascript framework and hence functional or event based programming. Sounds awesome but has a higher learning curve. socket.io works very well with WebSockets, so it would be easy to manage

EventMachine also manages high concurrency using evented I/O. Should be an ideal choice for high concurrency but how will websockets be managed easily?

Simply based on a few facts about nodejs and the new stuff we could learn and investigate , we chose nodejs – (and I am happy to say that it may have been a very prudent choice! )

These are the node modules we used:

  • socket.io – the obvious choice for WebSockets.
  • express – our choice of the Web Interface. We could have used a Rails application but seems an overkill.
  • jade – for HTML templates
  • redis – for PubSub.

Getting our act together

We wanted to build something that would give us the data we need as well as be fun! So, we decided to build a game called ‘Tapit’.

Tapit is a dance floor – a place where you can join in by clicking on a unique URL and entering your nickname. You would see your cartoon on the dancefloor and will have the controls on your mobile browser – 4 actions which will change the images on the dance-floor effectively making your cartoon dance! Everyone can have their own cartoon on the dance floor and can dance together. This creates a ‘engaged interaction game’ – where the person has to be in front of the dance floor to see his cartoon dance to his tune! A simple application with huge potential.

So, we started by setting up the node server and the node modules. We also installed Redis on our server to make the most of Redis PubSub!

To install node, npm and the node modules read these links: nodejs and  npm.

Configuring socket.io, express and redis

We can setup our node server and configure it like this:

HOST = "localhost",
PORT = "3001"

var express = require('express')
, app = express.createServer()
, redis = require('redis')
, io = require('socket.io').listen(app);

const DB = redis.createClient();
io.set('log level', 1); // reduce logging

app.use(express.bodyParser());
app.use(express.static(__dirname + '/public'));
app.set('view engine', 'jade');

app.listen(PORT);

We also need to keen the Redis PubSub messaging channels open. Here is how we do this:

io.sockets.on('connection', function(socket) {
const subscribe = redis.createClient();
const publish = redis.createClient();

socket.on('publish', function(channel, data) {
publish.publish(channel, data);
});

socket.on('psubscribe', function(channel) {
subscribe.psubscribe(channel);
});

subscribe.on("pmessage", function(pattern, channel, message) {
socket.emit('message', { channel: channel, data: message });
});
});

Create a new dance floor

To create a new dance floor, we need to call URL /games/new from the display machine. This creates the dance floor and gets the client side code ready to listen to new events like ‘new dancers’ or ‘change of action’.

Starting the game

To get your cartoon on the dance floor, users need to type the URL generated for that unique dancefloor on their mobile browser. This shows a screen to get your nickname, so you can identify yourself on the dance floor.

Playing the game

Once you have joined in, you should see your own avatar (cartoon) on the dance floor.

At this same time, you should see 4 controls on your mobile browser. You can tap anyone to make the cartoon on the dance floor ‘make your move’.

This is how the client side JS controls actions are controlled

$("#subscribe").submit(function() {
socket.emit('psubscribe', $('#subscribe #channel').val());
return false;
});

$(".action").click(function() {
socket.emit('publish', 'game.#{gameid}.action.' + $(this).data('action'),
JSON.stringify({ nick: "#{nick}", ts: Date.now() })
);

The websocket publishes events for a particular game. When nodejs receives this event, it publishes this on Redis PubSub. Since there are listeners connected, they receive this notification. Since listeners can be web-sockets themselves, they get a push notification on the webpage.

Performance – Latency and Concurrency

We tested out various networks, over WiFi, 3G and even on Edge network. The worse case scenario with Edge we found a latency of 200ms – probably acceptable.

When we tested for concurrency, we were easily able to scale easily to 100 ‘dancers on the dance floor’ dancing at the same time. The big issue currently is that for every subscriber I have to open a redis client. So,  I need to figure that one out. The other issue (still in debugging) was “Too many files open” exception on the node server. Again, this is not node related but a setup related issue.

Github repos

I have this game updated on github at http://github.com/joshsoftware/tapit  Feel free to fork it and play around. The live demo is hosted on http://tapit.nodejitsu.com

Where do we go from here?

This is just the beginning! Our next aim to build a multi-player game on the web. There are constraints of scale and latency – but then let us go where few men have gone before! 🙂

Update: This page has been translated into Spanish by Maria Ramos  from Webhostinghub.com

31 thoughts on “Real-time Games Using HTML5, WebSockets, Nodejs, & Socket.io

  1. You should be able to move the redis client initialize out of the connection event. Raising the unlimited on the server will likely fix the too many files open exception.

    1. @jim Since I am subscribing to events too, I need a new redis connection for each client, since we cannot publish events on a connection that is subscribing to events. Right?
      I can keep only 1 redis connection for publishing events for all connections but not sure if it would scale. I do need to test that out!

  2. Pingback: HTML5 | Pearltrees
  3. Hi all, I am new to this INTERESTING NODE.js…..but i cannot install it. How do i put it up on my server and test it? FYI the url’s don’t load.

    1. Look at nodejs.org and npmjs.org for instructions on installation.
      The URLs demo urls of my local machine – they are not intended to be public available!

  4. Hi Gautam,
    Your post is nice.I need a help regarding this topic .
    I am trying out multi player racing game using Node and Socket IO ,express . So I have tried simple example to see the latency between node server and the clients.
    I have a draggable image in client . when I move the image in one client ,it has to move in all clients.
    so basically when I am moving the image I am sending the image position to the node server in a json format , then from there I am broadcasting to all clients. there is a ~approx 300ms latency from the time. following are the results.

    Client 1 sending data to server at : 286136 (timestamp)
    Server received at : 286271

    Client2 received data at : 286470
    Client3 received data at : 286479
    Client4 received data at : 286487
    Client5 received data at : 286520

    the latency between move from client1 to client5 is 384ms. its too hight for a racing game ..
    here is my server code.

    var app = require(‘express’).createServer()
    var io = require(‘socket.io’);
    var http = require(‘http’);
    var http_server = http.createServer();
    var server = http.createServer(app);
    server.listen(3000);

    var socket = io.listen(server,{ log: false });

    socket.sockets.on(‘connection’, function (client) {
    client.on(‘message’, function (data){
    console.log(“data arrived to server”,new Date().getTime());
    // Below both statements are giving same latency between the client 1 and client 5
    client.broadcast.emit(‘message’,data);
    //socket.sockets.emit(‘message’,data);
    });
    });

    1) Is there any way to optimize the server code to reduce the latency?
    2) is this expected latency using node and websockets ?
    3) is socket io can’t broadcast the data asynchronously (I mean at a same time) ?

    Thanks
    Kishorevarma

    1. @Kishorevarma Latency has nothing to do with nodejs – its entirely related to your network. What is the network latency between your client and server anyway? If you “ping” the serve from a client machine, how long is the round trip. If its in hundreds of milliseconds, there is nothing you can do about it. If that is in a few tens of milliseconds and your websocket latency is 300+ ms, I would need to peek into some more code and logic.

      Btw, IMO, broadcast is not a good solution in this case, since its a racing game, there will too much network traffic.

      Also beware that as the number of participants in your game increases, the network traffic will increase, thereby increasing processing time on server and client: 2 cars: 6 message (1 from each client to server and 2 messages from server to 2 clients both times) 3 cars: 12 messages 4 cars: 20 messages. 5 cars: 30 messages. … 10 cars: 110 message. 100 cars: 10100 messages

      Basically its (n^2 + n). Your solution probably resides with using Redis PubSub and subscribing and unsubscribing to messages. Redis also guarantees that the message delivery is fast and does not slow down on large volumes.

      1. Thanks for your reply. there is 350ms network lag , but any how I am going to try redis pub sub.

      2. Using Redis pubsub would be a lot faster as the data transfer is over a connected socket. Do let me know how it works out and if you need some help.

  5. Hi Gautham,

    Nice Post,

    Do you think Socket.io will give better performance over ejabbered XMPP server. i am planning to build chat application which is actually cross platform. I expect 7,00,000 active users per day and 50 million messages transferred in single day, by using Node.js + redis + Socket.io +AWS SQS + SNS + Load balancing is right combination to meet my requirements ?

    Thanks,
    Srinivas

    1. The answer probably lies in the testing. However, since Facebook Chat uses ejabberd – it’s probably a good choice too but may make things more complex. Sorry – but I cannot give a definite answer on this because I have not tried out ejabberd — but please update the comments if you find some useful information.

      1. Gautam,

        Thank you for the comments. i will let you know on the developments.

        Regards,
        Srinivas

  6. Hi Gautham.

    Does your Socket.io game app will communicate with browser less cross platform devices like Android,IOS and Balackberry , if not is it possible to communicate by developing client app for it to communicate with Socket.io + Redis + Node.js.

    I am doing some experiments using it, so just want to check with you.

    Your feedback is highly appreciated.

    Thanks,
    Srinivas

  7. Hello there Gautan,

    First of all, this article is amazing! Thanks for putting it up on github!
    I’ll be using a similar approach as part of my graduation project with a friend, and even though we’re graduating in design, we’d like to implement something like this in the application we’re coming up with.
    I’m not really very familiar with nodejs nor socket io but I’ve played around with the code and I’m almost getting what we want, so maybe you could help me
    How can I control the elements that are already being displayed on the webpage when the user start a new “game”? He’s supposed to type in his name and on the page displayed there should be a few elements (divs most likely) that upon a click, should be hidden, moved aside, etc. Basically he should be able to control elements of the webpage solely via mobile. Is it possible or am I stretching here?

    Thanks in advance!

    1. From what I can see, most of the what you want is just some CSS play. Moving, hiding HTML elements is all CSS and thats how you should do it. If you can share your code base, and tell me exactly what you are looking for, I can take a look.

      1. Thanks for the reply, Gautam, and sorry I couldn’t get back at you earlier.

        Our code pretty much follows the logic of what you have on github. We changed very little here and there.
        What we’re exactly looking for is for the mobile to show, let’s say, three, four buttons and on the desktop there would be divs or something. Upon clicking button A, the div A would be displayed. Button B would display div B and so on.
        What we couldn’t find out is exactly how to connect one another. How can I call div A? And from where? From the join.jade page? new? That’s where we’re stuck.

        Thank you once again and if necessary, we can discuss this further by email if you want.
        Cheers!

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.