Collaborative drawing application using socket.io, node.js and canvas

In my day job i do all kinds of different projects. Recently, I was tasked to do a collaborative drawing application using HTML5 canvas. Now, I’m sure there are any number of ways to do this, but I decided the fastest and easiest way was to use node.js and socket.io. After looking around I didn’t see a lot of articles on how do this, so I figured I would write my own.
DISCLAIMER: This is not a comprehensive tutorial on socket.io, or node.js. This is only for demonstration purposes of how to communicate between two browsers using canvas. Just because I did my code this way doesn’t mean you have to. There are a lot of different ways to write javascript.

Assumptions:

  • Basic understanding of HTML5, CSS and jQuery
  • Know a tad about bootstrap.
  • Working with the Canvas element would be helpful.
  • Working knowledge of node.js and socket.io
  • Browsers that support the canvas element.
  • Basic command line and terminal knowledge (i.e. cd, ls)

Ok so lets get started. I’m not going to go into all the details on how to install node.js its really quite easy and the documentation on the site is very good. I will touch on how to set up the socket.io piece however.

Once you have node.js install you will need to install the socket.io module
Open a terminal and type npm install socket.io in it. This should get the socket.io modules onto your system.
Mine installed here /Users/darren/node_modules/socket.io (note I’m on a mac)
That’s pretty much it for the install. Next you will need to write the code to launch the server and get this drawing application running.

Lets write a basic socket server file. Create a new file called server.js and put the following:

io = require('socket.io').listen(4000)
    console.log("It worked");
io.sockets.on('connection', function (socket) {}); 

Depending on your set up you can put this file in a number of places. I found just putting it in the same folder as my socket.io folder /Users/darren/node_modules/ worked just fine.

Once you have done this save the file and go back to the termial change directory cd [Folder path with server.js file in it]. Once you are in the directory type node server.js you sould see something like this:

darren$ node server.js
info  - socket.io started
It worked

Now open a new browser window and type http://localhost:4000/

Moving on…
Now your server elements are set up lets move on to the fun part, drawing!

So lets create our index.html file.
The main thing to get out of this code is just a basic set up of the file. You will see some include files at the top. All these files will be included at the bottom of this post.
This is the most important part to take away from this.
<script src="http://localhost:4000/socket.io/socket.io.js"></script> this what hooks us into the socket.io server.

<!doctype html>
<html lang="en">
<title>Shared DrawPad</title>
<link type="text/css" href="css/bootstrap.min.css" rel="stylesheet"/>
<link type="text/css" href="css/drawpad.css" rel="stylesheet"/>
<script src="js/jquery-1.10.2.min.js"></script>
<script src="js/bootstrap.min.js"></script>
<script src="js/drawing.js"></script>
<script src="http://localhost:4000/socket.io/socket.io.js"></script>

<body>
<div id="drawingPad">
<!-- Code will be place dynamically -->
</div>
</body>
<script type="text/javascript">
$(document).bind("touchmove", function(e) {
e.preventDefault();
});
// overriding default options
var options = {
width:800,
height:500
},
//init the drawpad
dp = new DrawingPad(options);
dp.init("#drawingPad");
</script>
</html>

Drawing Javascript Code
I’m not going to go into every line of code and explain what it does. You can look over the full code and see read the comments if you want. What I do want to do is point out some of the special things that make this drawing application capable of being collaborative.

The first thing you need to do is establish a connection to the socket. Here I just create an object that the application can use to set and call socket events.

DP.thisObj.socket = io.connect("http://localhost:4000");

...
$(selector).append(_buildToolBar); //add tool bar to DOM

//register socket listeners
DP.thisObj.socket = io.connect("http://localhost:4000");
	
DP.thisObj.socket.on('setUserList', function(data) {
   return setUserList(data); //show pop up list
});
...

The next thing would be to establish listeners for socket events. So that when socket calls emit I can listen for that and do something. In this example I am setting a listener that listens for the draw emit event from the server.

DP.thisObj.socket.on('draw', function(data) {
      return draw(data);
});

The other thing you will be using a lot of is the emit event. The server can emit events and so can you. When you emit an event the server needs to listen for that event in order to react to it. In this example I will emit events then the server will listen for that event and emit back a response. So first I will talk about the client side emit events then I’ll look at how the server handles that.

This code is the event binding of the canvas element. I’m using jQuery to bind a set of event types (“touchstart” , “mousedown”, “touchmove” , “mousemove”, “touchend” , “mouseup”). Once one of those events is triggered I want the canvas’s that are sharing to listen for those events too and draw. I do this by calling DP.thisObj.socket.emit('drawRequest', data)

DP.thisObj.find(".myCanvas").bind(events, function(e){
  e.preventDefault();
  if(DP.isDrawing || DP.isLineDrawing) {
    var coords = _getCoords(DP.isTouchDevice?e.originalEvent:e),
    data = {
	 x: coords.x,
	 y: coords.y,
	 type: e.type,
	 isTouchDevice : DP.isTouchDevice,
	 color: DP.thisObj[DP.thisObj.id].ctx.strokeStyle,
	 stroke : DP.thisObj[DP.thisObj.id].ctx.lineWidth,
	 isLineDrawing : DP.isLineDrawing,
	 isErase : DP.isErase,
	 id : DP.thisObj.id
   };

   draw(data, true);

   if(DP.okToDraw || e.type === eventType.up) {
	 DP.isSharing ? DP.thisObj.socket.emit('drawRequest', data) : "";
   }
  }
});

Back to the server…
In order for the drawRequest to be picked up and broadcast to everyone else I need to have a lister on the socket server to listen for the drawRequest call. So on the socket server (server.js) I added this code

//drawing data
socket.on('drawRequest', function (data) {
  socket.broadcast.emit('draw', {
	x: data.x,
	y: data.y,
	type: data.type,
	isTouchDevice : data.isTouchDevice,
	color: data.color,
	stroke: data.stroke,
	isLineDrawing: data.isLineDrawing,
	isErase: data.isErase,
	id: data.id
  });
});

Notice on the server the syntax is a little different. I have an on event listening for the drawRequest message then it broadcasts an emit to everyone else except the person calling it. This is sort of important to know because if you were doing something that you needed to listen for too, you would need to leave out the broadcast piece. So in the case of getUserList where I’m getting a list of all connected users I need to get back that list. If I called socket.broadcast.emit(“setUserList”, connectedClients); everyone else would be sent that userList and not me. So what i need to do is this.

//returns back a list of clients to the requester
socket.on("getUserList", function (data) {
   socket.emit("setUserList", connectedClients); //send to sender
});

I’m just leaving out the broadcast part and it echoes back to me too.

That’s about it. The rest of the file is about the drawing piece of the code. If you want to learn how to draw on the canvas you can go through and look at the code and see how I did it. But this is really more about just communicating across the network.

Links:
Get the full code

Loading Facebook Comments ...