Note from the editor: The following is a guest post by Nick Morgan aka Skilldrick. I don’t know much about canvas, so it’s a pleasure to be able to share an article about a current web technology that is outside of my current range of knowledge.
Of all the new elements being added in HTML5, I think canvas
is one of the most exciting. Canvas gives you a fixed-size drawing surface and a selection of basic commands to draw on that surface.
In this tutorial I’m going to show you how to make a simple snake game using canvas. You know, like you used to get on your Nokia phone.
Making the canvas and our first rectangle
Let’s make a <canvas>
element, and draw something on it. In this code snippet, I’ve created the canvas element, set its dimensions, and drawn a pink rectangle on it.
See the Pen 5474d4c9b7f26b9c792b82d1d34ec57d by Chris Coyier (@chriscoyier) on CodePen.
The first thing to note here is that we don’t call drawing methods on the canvas, but on its drawing context. Currently canvas only has one context, which we acquire by calling .getContext('2d')
.
First we set the fill style of the context, which takes the form of a CSS colour, e.g. #f00
or rgba(100, 100, 100, 0.5)
. Then we call fillRect
, which takes the coordinates of the top-left hand corner of the rectangle, its width, and its height. Notice that x/y coordinates in canvas start at the top-left hand corner of the canvas – x increases to the right and y increases downwards.
Making a game loop
Now, canvas wouldn’t be too much fun if you couldn’t make things move. The standard way to make movement in a game is with the game loop. This is a function that gets called at a fixed interval to update the state of the game and display the changes.
See the Pen 4156c10284ec71ff53c0bbeb45437af0 by Chris Coyier (@chriscoyier) on CodePen.
(Note from editor: These used to be embedded examples from JSFiddle, but they broken when we went HTTPS around here. I ported them to CodePen, but they are still the original work of Nick Morgan.)
I’ve started adding in some organisation to the code now. To keep the global scope clear, I’m only using one global variable, called JS_SNAKE
. I’m then making an object called game
which will hold all the game-related functions. game
is an object with one public method, init
, and a number of private variables and functions.
The loop basically does three things. First, it updates the game’s state (in this case adding 2 to the x position and 4 to the y). Then it updates the display, taking into account the updated state. Finally, it calls setTimeout
, passing the gameLoop
function and the time to wait until calling it again.
This results in a function called every 500ms which will draw a rectangle in a different position each time. We need to call clearRect
each time to clear the canvas, otherwise we’d be drawing over the previous frame each time.
Drawing a snake
Now comes the fun bit: drawing the snake. The snake is a collection of square blocks. Each frame we add a new head and remove the tail, thus moving the snake forward.
See the Pen 651dad042be85e7453aebe140a6dfd91 by Chris Coyier (@chriscoyier) on CodePen.
The snake object has two public methods, advance
and draw
. advance
moves the snake one square to the right, and draw
actually draws the snake to the canvas.
advance
makes a copy of the head of the snake, moves it forward by 1, then adds the new head to the front of the position array and removes the tail (using unshift
and pop
, respectively).
draw
calls an internal function, drawSection
, which takes a block position and draws a square. At the beginning of draw
we save the context, and at the end restore it. Saving the context saves its previous settings, so any changes we make can be reverted. In this case, I want to change the fillStyle
to #33a
, but I want it to revert to whatever it was before when draw
has finished.
Moving the snake
We now need to hook up the keyboard so we can control the snake. I’m not going to go into detail on the code, but it’s fairly straightforward.
See the Pen 6f5ebe3df1fffd73a0d74e8035b7474e by Chris Coyier (@chriscoyier) on CodePen.
There’s a new function called in game.init()
which sets the event handlers. So far, the only event we care about is keydown
. If the key pressed is any of the arrow keys (keycodes 37, 38, 39 or 40), then we tell the snake to set a new direction.
setDirection
checks to see if the new direction is valid. This is to stop going back on yourself, e.g. pressing left when you’re travelling right. If the new direction is valid, then it sets nextDirection
to the new direction. This is then used in advance
so we know where the new head position should be.
Adding an apple
Now the snake needs something to eat, so let’s give it an apple.
See the Pen 1cf0319d740962633a5b19014f3ffd4d by Chris Coyier (@chriscoyier) on CodePen.
There’s not much to this, except we’re drawing a circle for the first time. To draw a circle in canvas, you need to start a new path, then draw a circle, and then fill the path. To start the path, call beginPath()
on the context. To draw the circle call arc()
on the context. arc
takes 6 arguments, the x coordinate of the centre, the y coordinate, the starting angle, the finishing angle, and a boolean indicating whether to draw clockwise or not. The angle is measured in radians, and 360° is 2 * pi radians.
Finishing it off
Now that you know the basics you can view the source code of the final snake game and see what’s
going on. It adds the technicalities of collision detection and score-keeping.
See the Pen 91b9fbfb55ca36e99029297a6f452e07 by Chris Coyier (@chriscoyier) on CodePen.
(For those playing at home, remember to click into the canvas area so that it can capture your keyboard events. It’s presented here in an iframe which cannot receive events from the outside page.)
Interesting implementation, I love the subtle speed up. It does seems to be horrendously slow though, is it just me (Chrome/Mac)?
Here’s one I made a few weeks back based on ThinkVitamin’s tutorial http://red-root.com/sandbox/snake/game.html for those interesting. Trying to implement requestAnimationFrame at the moment but can’t get it right yet, everything is too fast.
That’s a cool one :)
Addictive game! good work!
Simple but effective – great implementation though.
Yes, it’s slow – I set the starting speed at 2 fps (500ms frame length). As Charlotte said though, it’s easy to change the starting speed!
really cool man, got 16 score :D
This is so interesting :)
I feel like canvas isn’t explored enough by UI designers
… Got to 15 before I realised I could just edit the js and make it FASTER!
You should use requestAnimationFrame instead of setTimeout. See http://paulirish.com/2011/requestanimationframe-for-smart-animating/
Yes, requestAnimationFrame is the way forward for things like this. I didn’t go with it here because everybody’s familiar with setTimeout. This is a canvas tutorial and I didn’t want to introduce too many different concepts.
Nice ! This is a good little example that can get me started on a few things, thanks.
got to 81
nice game!
I knew an html5 snake example, that’s also great!
Nice article! Thanks for sharing!
jQuery? Really? Is it really important to use this?
No, it’s not important to use jQuery for this. However, I was striving for the simplest-possible code, so there was nothing to distract from the main application logic. I knew that most readers of CSS-Tricks would be familiar with jQuery, so I went with that.
Thank you for this post. I like snakes!
Green apples?! Yikes!
Check out my score!
Check out my score! I actually enjoyed playing! I propose a competition:P
Thanks, Very very useful, effective, well explained tutorial!
Thank you so much for the nice tutorial.
To my understanding, there’s no way for stop the user from changing the javascript (for example, from score++ to say score += 100).
So if I want to keep a scoreboard of the highest scores in a database on a server, is there a way to make sure, or at least use some protocol to test, that the score submitted to the server is legit?
Thanks again.
I’ve heard that it’s practically impossible to stop cheating/spamming. Your high score system will be safer if you make the js code more difficult to read (via compression) or… maybe it can be executed through php. But I’m not sure.
Very nice game! I got 44!
very nice thank you
69, and I am not being perverted. Gotta love really simple addicting games, I almost wanted to play more.
The best thing I took from this tutorial is the iFrame embedding of jsFiddle, Great way to show off some code example. But also great tutorial!
Great tutorial, Nick—thanks!
Got to 21, lol
Hey great tutorial! I am making a javascript canvas game too and I’m new to the creating my own objects in js. What object pattern are you using for this example? I’m wondering because I’m changing my game code to use objects and from what it looks like, I’m pretty sure you are using the Revealing Module Pattern. Am I right?
Hi Jake, yeah, it’s some form of the revealing module pattern. I think it’s a great pattern to use – in some ways very similar to class-based object design, and everything’s private unless you specifically make it public. I’ve written a bit about this on my blog: http://skilldrick.co.uk/2011/02/zen-and-the-art-of-statefulness/