Wes Bos

Designer, Developer & Entrepreneur making the web an awesome place.

sockets

Web sockets and Canvas are two really cool features that are currently being implemented into browsers. This tutorial will give you a short rundown of how they both work as well as create a realtime drawing canvas that is powered by Node.js and web sockets. For simplicity’s sake, I’ll be writing all the code in coffeescript. If you prefer regular ‘ol JavaScript, take a look at the corresponding .js files. I’ve also left out the CSS for the same reason.

Quick Screencast detailing tutorial

Cross Device / Browser compatibility

Server Side

The first thing we need to do is create a web socket server. For this we will be using Node.js and the module Socket.io. Socket.io makes its super easy to get a web socket server up and running. It even provides a flash fallback for browsers that don’t support native web sockets. In this tutorial we will only be working with browsers that support the canvas element.

If you don’t have Socket.io installed yet, make sure you do so by typing npm install socket.io into your terminal.

For now, lets just set up the web socket server. Create your server.coffee file with the following configuration.

io = require('socket.io').listen(4000)
io.sockets.on 'connection', (socket) ->

Compile your coffeescript and hop back into your terminal and type node server.js. You now have a web socket server running on port 4000.

If you go to localhost:4000 you’ll see the following:

Client Side

First, lets quickly get our index.html file up and running. In addition to some bare bones markup, I’m also including jQuery, our Socket.io JS file which is now being served up from our server, a jQuery plugin for drag events, and our own scripts.js file which will hold all the magic.

<!DOCTYPE HTML>
<html>
<head>
	<meta charset="UTF-8">
	<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script>
	<script type="text/javascript" src="js/jquery.event.drag-2.0.js"></script>
	<script src="http://localhost:4000/socket.io/socket.io.js"></script>
	<script type="text/javascript" src="scripts.js"></script>
	<link rel="stylesheet" href="style.css" />

	<title>HTML5 Canvas + Node.JS Socket.io</title>
</head>
<body>
	<article><!-- our canvas will be inserted here--></article>


	<!-- Scripts required -->
	<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script>
	<script type="text/javascript" src="js/jquery.event.drag-2.0.js"></script>
	<script src="http://localhost:4000/socket.io/socket.io.js"></script>
	<script type="text/javascript" src="scripts.js"></script>
</body>

Now that we have our server up and running, we can write some code which will draw to the canvas. Create a new file called scripts.coffee. All of the following code happens within the App.init() method which we will trigger on the jQuery document ready.

Create our Canvas Element

# setup our application with its own namespace 
App = {}

###
	Init 
###
App.init = -> 
	App.canvas = document.createElement 'canvas' #create the canvas element 
	App.canvas.height = 400
	App.canvas.width = 800  #size it up
	document.getElementsByTagName('article')[0].appendChild(App.canvas) #append it into the DOM 

	App.ctx = App.canvas.getContext("2d") # Store the context 

	# set some preferences for our line drawing.
	App.ctx.fillStyle = "solid" 		
	App.ctx.strokeStyle = "#bada55"		
	App.ctx.lineWidth = 5				
	App.ctx.lineCap = "round"

	# Draw Function
	App.draw = (x,y,type) ->
		if type is "dragstart"
			App.ctx.beginPath()
			App.ctx.moveTo(x,y)
		else if type is "drag"
			App.ctx.lineTo(x,y)
			App.ctx.stroke()
		else
			App.ctx.closePath()
	return

Draw to canvas function

Since drawing to canvas involves beginning, moving and closing paths, i’ve create a short little function that hooks into the jQuery dragstart and drag events.

# Draw Function
App.draw = (x,y,type) ->
	if type is "dragstart"
		App.ctx.beginPath()
		App.ctx.moveTo(x,y)
	else if type is "drag"
		App.ctx.lineTo(x,y)
		App.ctx.stroke()
	else
		App.ctx.closePath()
return

Setup our client side web socket

Since we included our file at http://localhost:4000/socket.io/socket.io.js we are able to create an object which we can send our data over. With just a few lines, we create our App.socket object and bind to any incoming web socket events called ‘draw’. We will go over this more soon.

# Sockets!
App.socket = io.connect('http://localhost:4000')

App.socket.on 'draw', (data) ->
	App.draw(data.x,data.y,data.type)

Canvas Drawing Event

This is where things gets exciting. Now we want to bind a few events to our canvas element. The way this works is when someone draws on the canvas, we immediately use our draw() function to draw to the current canvas as well as send the x and y ordinates over the web socket with socket.io’s emit. In just a bit we will take a look at the server side part of this event and see how the server sends out this data to all open windows.

###
	Draw Events
###
$('canvas').live 'drag dragstart dragend', (e) ->
	type = e.handleObj.type
	offset = $(this).offset()
	
	e.offsetX = e.layerX - offset.left
	e.offsetY = e.layerY - offset.top
	x = e.offsetX 
	y = e.offsetY
	App.draw(x,y,type)
	App.socket.emit('drawClick', { x : x, y : y, type : type})
	return

Jump back to server side

Now that we know we are sending the x, y and type of event over the web socket, we need to do something with that on our server. What we want to do, is take that data and send it back out to everyone else that has a browser open.

Our updated server.coffee file now looks like this. We first wait for a connection event, then wait for a ‘drawClick’ event to be sent by a browser. When that happens we take the data and send it out to everyone else with a browser open. THe server side script we wrote earlier will then paint the canvas.

io = require('socket.io').listen(4000)

io.sockets.on 'connection', (socket) ->
	socket.on 'drawClick', (data) ->
		socket.broadcast.emit 'draw',{ x : data.x, y : data.y, type: data.type}
		return
	return

You’ll now need to restart your web socket server as we have made changes to it. Hit control-c to kill it, and node type node server.js to restart it.

Get Drawing!

One you fully understand how this all works, open your index.html file in any web browser that supports web sockets and canvas (at the time of writing Chrome, Firefox, Safari, Opera and IE9). Check http://caniuse.com/#search=canvas for more support info.

Limitations

As this is a very basic demo, there are a few limitations which can easily be solved with a little more code. Currently the canvas only supports one person drawing at a time, if two or more are drawing, the canvas will be painted sporadically. Also, there is definitely a lot of room to add tools such as brushes, colours, erasers and PNG export. If there is interest, I’ll expand this tutorial series to cover them.

If you’re interested in getting this up and running in the real world and off of your localhost, I was able to get mine running on Amazons free micro instance of EC2 although this involves installing Node and NPM all over again. Also note you should run your server on port 80 rather than 4000.

Please feel free to download, hack, complain, fork or contribute to the project on my github account.

This entry was posted in browsers, CoffeeScript, HTML5, jQuery, NodeJS. Bookmark the permalink.

45 Responses to Realtime HTML5 Canvas Drawing with WebSockets, Node.JS & Socket.io

  1. Sasha says:

    Node.js is sexy. Lately I’ve being playing and trying to find some use for it, but for the projects I’m working on good ol’ PHP does the work.

  2. yulrizka says:

    Nice tutorial … keep on going

  3. Marc says:

    Nice tutorial! You’ve inspired me to resurrect a project I built like this over a year ago, thinking of upgrading from python to node on the server side though.

  4. Can Gencer says:

    This was great! A small comment, the App = {} declaration should be this.App = {} instead so that it gets exported into the global namespace.

    • wesbos says:

      Coffeescript actually wraps everything in a self invoking function so that you dont even touch the global namespace. For this, there is no need to expose any of the code globally..

  5. Can says:

    Ok I see that you put the document.ready into the script file as well. I haven’t looked at the original source code. That was not in your tutorial. I called it from inside the index.html file, so that’s why :)

  6. Pingback: Useful Node.js Tools, Tutorials and Resources - Smashing UX Design

  7. Pingback: Useful Node.js Tools, Tutorials And Resources | Wordpress Training Course Brisbane: Next Course Thur 29th Sep 2011

  8. Pingback: Useful Node.js Tools, Tutorials And Resources | Blogs – NG Outsourcing

  9. Pingback: Useful Node.js Tools, Tutorials And Resources | IdentityNepal.com

  10. Pingback: Useful Node.js Tools, Tutorials And Resources | CS5 Design

  11. Pingback: Useful Node.js Tools, Tutorials And Resources | Ruturaj Pradeep Kohok | Your Web Advisor

  12. Pingback: Useful Node.js Tools, Tutorials And Resources - Smashing Coding

  13. Pingback: Useful Node.js Tools, Tutorials And Resources

  14. sergio says:

    Hi all.

    I’ve take a beagleboard (arm processor module), have put ubuntu + node.js (0.4.9) + sockets-io. Also added “server.coffe” file as explained.

    I’m running it inside Firefox and Chrome from another machine (PC) and the most I’m able to do is draw but both images are not “syncronized”. Seems that Chrome session is independent from Firefox session… (My node.js level is 0.000001)

    Note that I’ve change localhost by the arm IP address.

    Someone audacious can bring me a hand to make it work as in the first video???

    BR,
    Sergio

    • sergio says:

      Hi all. Finally I can make it work locally. Great job wesbos. Now it is my time to learn…

      In the meanwhile I try to run it using remote host instead of localhost, changing all localhost ocurrences by remote linux IP address, but it doesn’t work. Opening different sessions of index.html let me draw but all browsers are not syncronized (I can draw different images in each browser).

      Someone can help me?

      BR,
      Sergio

  15. Dan says:

    Hi Wesbos,

    Apologies in advance for this very naive question but hopefully you can help.

    I have downloaded your source code so I could have a play around with it, (I have experience in programming but nothing web based). But I have no idea how to run it locally on my machine. Do I need to download anything else? I have seen a few tutorials that recommend using something call xampp but not entirely sure what it is or if it is relevant.

    Sorry again for this basic question.

    hope you can point me in the right direction.

    • wesbos says:

      Hey Dan, this runs on something called NodeJS, so XAMPP (which is for running PHP) wont help you out here. Its probably a good idea to get an idea of what NodeJS is first and then take a gander at how this works :)

  16. Sahil says:

    I downloaded the source code from github.com and started the nodejs server using node server.js.
    I can see message when I hit “localhost:4000″ but its not displaying or doing anything when I do localhost:4000/index.html

    It would be great if u can tell what wrong I am doing.

    Thank you for your help.

  17. Ron says:

    Hi Wesbos.

    I was trying to get this to work on my ubuntu vps server (that currently has nginx running on port 80) and I’m a little confused on how to serve the html page with the canvas element via nginx and use websockets to communicate on a different port (4000, as in this example) and update the canvas on port 80. I was thinking of implementing this into a kind of canvas chat sort of thing where there are PHP server sessions adding clients connecting to the page.

    Thanks!

  18. PHP-Sadness says:

    Hi there, what Editor are you using in the tut?
    Greetings, Chris

  19. Bharat Patil says:

    Very helpful article. Thank youwebos. BTW, Happy New Year.

  20. Soroush says:

    Great tutorial.
    I’m really interested to see how you’d go about implementing support for simultaneous drawings from multiple users. What kind of architechture would you use?

  21. Ganesh Chaudhari says:

    Very good demo.

    Appreciated for your work.

    Thanks a lot.

  22. JohnMiller says:

    Exciting and inspirational!

    What’s the trick to getting the style.css and local .js files loaded by the socket.io server? (I’m using your JavaScript files under Win7 Pro.)

    Thanks!

  23. Vivek Lohia says:

    Hi, Suppose i am running 2 Web Pages as 2 clients and the drawing done on the 1st is automatically reflected on the second.

    Now, if i want to clear both canvases from the 1st page itself just by the click of a button, how do i do that ?

  24. sarah says:

    hello, how can i run this project with NetBean.
    help me please

  25. Pingback: Links for 2012-04-05 [del.icio.us] | Suchmaschinenoptimierung

  26. Jeroen says:

    Hey man, great tutorial!
    I was wondering, any idea of how you would implement the “more people drawing at once”, can you push me in a direction or something?

    greetings !

  27. Pingback: Useful Node.js Tools, Tutorials And Resources « Dao Hoang Tu

  28. Josh says:

    I’m trying to implement your code on my local machine. The message saying “info – socket.io started” shows up on my console but I’m not able to actually open the index.html via the web server. How do I do that? When I go to “localhost:4000/index.html” or “localhost:4000″ it gives me “Welcome to socket.io.” Please help.

    • Ben says:

      Very late reply, but maybe I can help anyone else using this tutorial.
      Wesbos’ tutorial only shows you how to send the script. I used the module “connect” to serve the index.html file.
      – make sure connect is installed in node directory
      > npm install connect
      – open up server.js (unless you want to figure out how to do it in coffee)
      – add the following lines above the io=require(…) line

      var connect = require(‘connect’);
      connect.createServer(
      connect.static(__dirname)
      ).listen(8080);

      NOTE: change the port to something you want to use to serve the html file. Making it work through a remote host is a whole ‘nother story. Also note: this method will serve ANY file in the same directory accessed like “localhost:8080/.” so be careful trying to port this for anything serious.

  29. Louise says:

    Hi,

    I absolutely love your tutorial and wanted to know if there is anyway of finding out some information on how to add some buttons into it to change the colour, or size? Any advice you can give me on this would really be appreciated.

    Thanks

  30. JoRo says:

    How exacely would you support multiple people drawing at the same time? It doesn;t seem immediately apparent how to keep the various commands separated by user. Help please?

    Thanks!!!

  31. Pingback: Useful Node.js Tools, Tutorials And Resources - Goodfav Howto

  32. vrobbi says:

    Real time whiteboard collaborative with chat with html5 node.js and websocket
    http://vrobbi-nodedrawing.herokuapp.com

  33. PRADEEP says:

    heya,very nice tutorial, i implemented your code its working fine.but i cannot draw in touchpads like ipad , may be there is no events for touch… can you guide me on this how to create touch events so that i can draw in ipad and same will be reflected in desktop webbrowsers…thnx

    • wesbos says:

      Yep, I’ve since done this with iPads. Instead of just binding to the click event, bind to the touchstart event as well.

  34. Pingback: Useful Node.js Tools, Tutorials And Resources | Smashing Magazine | Hugo Mineiro Portfolio

  35. Pingback: 有用Node.js的工具,教程和资源 | 前端开发

Leave a Reply

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=""> <strike> <strong>