Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / HTML5

DooScrib - Collaboration with an HTML5 Canvas

5.00/5 (8 votes)
24 Jul 2014CPOL8 min read 28.3K   294  
Collaborating via an HTML5 Canvas using DooScrib, Node.js, Express and Socket.io

Introduction

So I have been promising others for sometime now to write this latest series of the article.

In this article I am going to explain how to use the DooScrib jQuery plugin discussed in the previous article with socket.ionode.js and express to create a collaborative whiteboard.

Background

This article is a continuation in a series of articles that I have planned around the idea of demonstrating how to develop an HTML5 digital canvas where users can collaborate in realtime. With the progression of each article I will continue to update the DooScrib web site and publish the code with each article as well as keep an updated source on my github account. 

Just a quick note but there is a general assumption that you already have a basic understanding of Node.js, Express and socket.io. If not then there are a lot of great articles on this site to help get you started.

I will try cover and explain a few things but you might feel comfortable having some basic knowledge first. 

Getting Setup

You will need to of course make sure that you have Node.js installed on your computer.

Whether you have downloaded and expanded the zip file or cloned everything from my github repository there is still the remaining step of getting the packages (express, jade and socket.io) installed. 

From the command prompt or terminal window you will need to make sure you are in the directory where the file package.json is located. Once you are there you will want to execute the following:

npm install
This will create the directory node_modules and install the packages called out in package.json as part of the dependencies which looks like:
JavaScript
{
  "dependencies": {
    "express": "3.3.4",
    "jade": "1.1.5",
    "socket.io": "^1.0.6"
  }
}

If you want you can actually run it now with the following command:

node index.js

With everything installed we can now begin to review through the code that was added to enable collaboration. 

Using the code

I am going to cover the code from the client side as well as from the server side. Before I do that let me give a basic flow of how everything will work.

  • Client submit data on different event queues to the server (ex. move, paint, clear, etc.).
  • Server receives event data and broadcasts to all clients a similar event (ex. painting, moving, etc.).
  • Clients receive event data from server and act on each them accordingly.

Sounds simple and actually once we start looking at the code you will be amazed how simple it actually all comes together. 

Server

Setting up the Server

The following code demonstrates how to setup a server:

JavaScript
var io = require('socket.io')(server);

The server parameter that is being passed in is created in a previous call as we are in the setting up express and the http server.

JavaScript
var http = require('http'),
    express = require('express'),
    path = require('path');

var app = express();
var server = http.createServer(app);

With the server configured and setup the next step is to add code for the processing of a new client connection event. The following code demonstrates how to handle a new connection:

JavaScript
io.on('connection', function (socket) {
    // add code here which handle the different events for when a client sends
    // in data up to the server.
});

Handle Incoming Messages

The server will receive data from the clients on the following messages:

  • start - event from the client indicating that it has started to draw a line.
  • mousemove - event from the client indicating that the mouse is moved (not drawing).
  • painting - event from the client indicating that the mouse is moved and a line is drawn.
  • release - event from the client indicating that the drawing has stopped.
  • clear - event from the client indicating the user has cleared the canvas.

To handle messages from a client we use the socket that is passed to us when the connection event is being handled. The following is an example of how to process a message from a client:

JavaScript
socket.on('message_name', function(data){
    // process data that was sent with message
});

Once the message is received by the server the data can then get transmitted back out to all the other clients. With socket.io there are actually two different ways to broadcast events to all of a servers clients.

  • io.sockets.emit - sends a message to all of the servers clients.
  • socket.broadcast.emit - sends a message to all of the servers clients but omits the current connection.

In this situation the client has already handled what needs to be done. The need for the server to send it a message back can be eliminated so the socket.broadcast.emit function will be used. 

The following code demonstrates the server handling the different incoming messages from a client connection:

JavaScript
io.on('connection', function (socket) {
    socket.on('start', function(data){
        console.log('starting - ', data);
        socket.broadcast.emit('starting', data);
    });	
    socket.on('mousemove', function(data){
        console.log('moving - ', data);
        socket.broadcast.emit('moving', data);
    });
    socket.on('painting', function(data){
        console.log('paint - ', data);
        socket.broadcast.emit('paint', data);
    });
    socket.on('release', function(data){
        console.log('released - ', data);
        socket.broadcast.emit('released', data);
    });
    socket.on('clear', function(data){
        console.log('clearing - ', data);
        socket.broadcast.emit('clear', data);
    });
});

The last part of course is to setup your server so that it will start listening for connections. If you recall earlier we tied our socket.io server into the http server so the benefit is that everything is tied into the http server. The following code is an example of how you would set that up to listen for connections.

JavaScript
server.listen(app.get('port'), function(){
    console.log('Express server listening on port ' + app.get('port'));
});

Client

Creat a Client Socket

Setting up your page to use socket.io is much simpler than the steps we went through to configure the server.

Earlier we installed the socket.io package and with that the delivery of the client libraries is handled for us. The following demonstrates what needs to be done to setup your page to use socket.io as well as to create a connection to the server.

Include the socket.io library.

HTML
<script src="/socket.io/socket.io.js"></script>

Create the connection to the server.

JavaScript
// change the server name to wherever you have your server deployed
var socket = io.connect('http://localhost/');

Setting up the DooScrib plugin

Let's create an instance of the DooScrib plugin. Just a reminder but when setting it up you give the element on your page that you want to have the canvas element created within. In my example below I am going to create everything once the page has loaded and is ready.

JavaScript
$(document).ready(function(){
    var h = $('#surface').height();
    var w = $('#surface').width();

    surface = new $('#surface').dooScribPlugin({
        width:w,
        height:400,
        cssClass:'pad',
        penSize:4,
        onMove:function(e) {
            socket.emit('mousemove', {clientID:id, X:e.X, Y:e.Y});
        },
        onClick:function(e) {
            socket.emit('start', {clientID:id, X:e.X, Y:e.Y});
        }, 
        onPaint:function(e) {
            socket.emit('painting', {clientID:id, X:e.X, Y:e.Y, color:surface.lineColor(), pen:surface.penSize(), cap:surface.lineCap()});
        },
        onRelease:function(e) {
            socket.emit('release', {clientID:id, X:e.X, Y:e.Y});
        }
    });
});

The difference in this example of the plugin and previous is that we are now taking advantage of callback events exposed by the plugin to notify us of different events happening while the user works on the canvas.

  • onMove - an event indicating that movement across the canvas has occured (not drawing)
  • onClick - an event indicating that a line is starting because the user has selected inside the canvas.
  • onPaint - an event indicating that movement across the canvas has occured while drawing.
  • onRelease - an event indicating that drawing has stopped because the user is not selecting the canvas.

We have already covered the details for creating a socket on the client side; this code now demonstrates it being used with the plugin. Let me cover quickly the json object we are creating and sending as data with each message.

The id for the clientID is a random number generated by each client and we will see later the important role it plays so that we can have multiple clients all drawing at the same time.

JavaScript
// generate a random ID to be used as the clientID
var id = Math.round($.now()*Math.random());

Next of course is the X and Y coordinates that are included as event data in our callbacks. Finally in the painting message I have added properties from the plugin that describe how the line is being drawn.

  • color - the color of the line that is being drawn.
  • pen - the thickness of the line that is being drawn.
  • cap - the shape at the end of the line being drawn. 

The remaining piece now of course is giving the user the ability to clear the canvas as well as communicating that via a message to the server. For the following example I have a button to the page and set id=clear so that I can receive click events on it:

JavaScript
$('#clear').click(function() {
    surface.clearSurface();
    socket.emit('clear');
});

Handling Server Messages

The only piece that remains now is to handle the server messages so that the user can see what everyone else is drawing at the same time.

  • starting - a message indicating that drawing has started.
  • paint - a message indicating that the cursor has moved while drawing.
  • moving - a message indicating movement across the surface (not drawing).
  • released - a message indicating that drawing has stopped.
  • clear - a message indicating a request to clear the canvas.

Before I go over the code to handle these messages let me go over a concern I mentioned earlier about adding the clientId to make sure we could differentiate where the messages originated. While handling drawing messages we need to be able to store X and Y coordinates and also not get that information confused with other clients.

To achieve this I created an Array which I will index with the clientID received with each message.

JavaScript
var prevPoint = new Array();

With that piece of information covered we can now go over the following code which completes everything needed to handle the server messages:

JavaScript
socket.on('starting', function(data){
    console.log('starting - ', data);
    prevPoint[data.clientID] = data;
});
socket.on('moving', function(data){
    console.log('moving - ', data);
});
socket.on('paint', function(data){
    console.log('paint - ', data);
    surface.drawLine(prevPoint[data.clientID].X, prevPoint[data.clientID].Y, 
        data.X, data.Y, data.color, data.pen, data.cap);
    prevPoint[data.clientID] = data;
});
socket.on('released', function(data){
    console.log('released - ', data);
});
socket.on('clear', function(data){
    console.log('clear surface request - ', data);
    surface.clearSurface();
});

Points of Interest

Great, you probably have this running on your local machine but publishing it to the internet would be even better. For my testing and development I have had lots of success with Nodejitsu

What's next??

In the next article(s) I plan to break this up into conference rooms as well integrated the following gists for saving and loading your drawings into a mongodb.

  • This link is an example for converting a Base64 string into an image.
  • This link is an example for converting an image into a Base64 string. 

History

  • 22-July-2014 - Initial Version
  • 23-July-2014 - Moved all of the developed code to DooScrib web site.
  • 24-July-2014 - Added link for downloading source code.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)