Grow your CSS skills. Land your dream job.

Learning Canvas: Making a Snake Game

Published by Chris Coyier

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.

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.

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.

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.

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.

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.

(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.)

Comments

  1. Permalink to comment#

    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.

  2. 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!

  3. 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.

  4. Nice ! This is a good little example that can get me started on a few things, thanks.

  5. got to 81

    nice game!

  6. I knew an html5 snake example, that’s also great!

  7. Nice article! Thanks for sharing!

  8. 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.

  9. Thank you for this post. I like snakes!

  10. Green apples?! Yikes!

  11. Kyle
    Permalink to comment#

    Check out my score! I actually enjoyed playing! I propose a competition:P

  12. Thanks, Very very useful, effective, well explained tutorial!

  13. nice plug

  14. Hao Luo
    Permalink to comment#

    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.

  15. Spreng
    Permalink to comment#

    Very nice game! I got 44!

  16. m
    Permalink to comment#

    m

  17. chris
    Permalink to comment#

    very nice thank you

  18. 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!

  19. Kriss Watt
    Permalink to comment#

    Great tutorial, Nick—thanks!

  20. Very nice game.

  21. andy

    Got to 21, lol

  22. Permalink to comment#

    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?

  23. I would be more than happy to see the seo aspects of this. So that no further need be there for extra work to put in seo….

This comment thread is closed. If you have important information to share, you can always contact me.

*May or may not contain any actual "CSS" or "Tricks".