JavaScript Snake tutorial
last modified October 18, 2023
In this article we show how to create a Snake game in JavaScript. The images and sources are available at the author's Github JavaScript-Snake-Game repository.
Snake game
Snake is an older classic video game which was first created in late 70s. Later it was brought to PCs. In this game the player controls a snake. The objective is to eat as many apples as possible. Each time the snake eats an apple, its body grows. The snake must avoid the walls and its own body. This game is sometimes called Nibbles.
HTML5 Canvas
HTML5 canvas element provides a resolution-dependent bitmap area, which can be used for rendering graphs, game graphics, art, or other visual images on the fly. In simple terms, canvas is a new element in HTML5, which allows you to draw graphics using JavaScript. Canvas brings animations to web pages without the need of plugins like Flash, Silverlight, or Java.
JavaScript Snake code example
The size of each of the joints of a snake is 10 px. The snake is controlled with the cursor keys. Initially, the snake has three joints. If the game is finished, the "Game Over" message is displayed in the middle of the canvas.
<!DOCTYPE html> <html> <head> <title>JavaScript Snake game</title> <style> canvas {background: black} </style> <script src="snake.js"></script> </head> <body onload="init();"> <canvas id="myCanvas" width="300" height="300"> </canvas> </body> </html>
This is the HTML source. We put the JavaScript source in the
snake.js
file.
<canvas id="myCanvas" width="300" height="300"> </canvas>
We create a canvas object. It is a rendering area for our game.
// JavaScript Snake example // Author Jan Bodnar // http://zetcode.com/javascript/snake/ var canvas; var ctx; var head; var apple; var ball; var dots; var apple_x; var apple_y; var leftDirection = false; var rightDirection = true; var upDirection = false; var downDirection = false; var inGame = true; const DOT_SIZE = 10; const ALL_DOTS = 900; const MAX_RAND = 29; const DELAY = 140; const C_HEIGHT = 300; const C_WIDTH = 300; const LEFT_KEY = 37; const RIGHT_KEY = 39; const UP_KEY = 38; const DOWN_KEY = 40; var x = new Array(ALL_DOTS); var y = new Array(ALL_DOTS); function init() { canvas = document.getElementById('myCanvas'); ctx = canvas.getContext('2d'); loadImages(); createSnake(); locateApple(); setTimeout("gameCycle()", DELAY); } function loadImages() { head = new Image(); head.src = 'head.png'; ball = new Image(); ball.src = 'dot.png'; apple = new Image(); apple.src = 'apple.png'; } function createSnake() { dots = 3; for (var z = 0; z < dots; z++) { x[z] = 50 - z * 10; y[z] = 50; } } function checkApple() { if ((x[0] == apple_x) && (y[0] == apple_y)) { dots++; locateApple(); } } function doDrawing() { ctx.clearRect(0, 0, C_WIDTH, C_HEIGHT); if (inGame) { ctx.drawImage(apple, apple_x, apple_y); for (var z = 0; z < dots; z++) { if (z == 0) { ctx.drawImage(head, x[z], y[z]); } else { ctx.drawImage(ball, x[z], y[z]); } } } else { gameOver(); } } function gameOver() { ctx.fillStyle = 'white'; ctx.textBaseline = 'middle'; ctx.textAlign = 'center'; ctx.font = 'normal bold 18px serif'; ctx.fillText('Game over', C_WIDTH/2, C_HEIGHT/2); } function checkApple() { if ((x[0] == apple_x) && (y[0] == apple_y)) { dots++; locateApple(); } } function move() { for (var z = dots; z > 0; z--) { x[z] = x[(z - 1)]; y[z] = y[(z - 1)]; } if (leftDirection) { x[0] -= DOT_SIZE; } if (rightDirection) { x[0] += DOT_SIZE; } if (upDirection) { y[0] -= DOT_SIZE; } if (downDirection) { y[0] += DOT_SIZE; } } function checkCollision() { for (var z = dots; z > 0; z--) { if ((z > 4) && (x[0] == x[z]) && (y[0] == y[z])) { inGame = false; } } if (y[0] >= C_HEIGHT) { inGame = false; } if (y[0] < 0) { inGame = false; } if (x[0] >= C_WIDTH) { inGame = false; } if (x[0] < 0) { inGame = false; } } function locateApple() { var r = Math.floor(Math.random() * MAX_RAND); apple_x = r * DOT_SIZE; r = Math.floor(Math.random() * MAX_RAND); apple_y = r * DOT_SIZE; } function gameCycle() { if (inGame) { checkApple(); checkCollision(); move(); doDrawing(); setTimeout("gameCycle()", DELAY); } } onkeydown = function(e) { var key = e.keyCode; if ((key == LEFT_KEY) && (!rightDirection)) { leftDirection = true; upDirection = false; downDirection = false; } if ((key == RIGHT_KEY) && (!leftDirection)) { rightDirection = true; upDirection = false; downDirection = false; } if ((key == UP_KEY) && (!downDirection)) { upDirection = true; rightDirection = false; leftDirection = false; } if ((key == DOWN_KEY) && (!upDirection)) { downDirection = true; rightDirection = false; leftDirection = false; } };
This is the JavaScript Snake source.
const DOT_SIZE = 10; const ALL_DOTS = 900; const MAX_RAND = 29; const DELAY = 140; const C_HEIGHT = 300; const C_WIDTH = 300;
DOT_SIZE
is the size of the apple and the dot of the snake.
ALL_DOTS
constant defines the maximum number of possible dots on
the canvas (900 = 300*300/10*10). MAX_RAND
constant is used to calculate a random position for an apple.
DELAY
constant determines the speed of the game. C_HEIGHT
and C_WIDTH
constants store the size of the canvas.
const LEFT_KEY = 37; const RIGHT_KEY = 39; const UP_KEY = 38; const DOWN_KEY = 40;
These constants store the values of arrow keys. They are used for better readability.
var x = new Array(ALL_DOTS); var y = new Array(ALL_DOTS);
These two arrays store the x and y coordinates of all joints of a snake.
function init() { canvas = document.getElementById('myCanvas'); ctx = canvas.getContext('2d'); loadImages(); createSnake(); locateApple(); setTimeout("gameCycle()", DELAY); }
The init
function gets the reference to the canvas object and its
context. The loadImages
, createSnake
, and
locateApple
functions are called to perform specific tasks. The
setTimeout
starts the animation.
function loadImages() { head = new Image(); head.src = 'head.png'; ball = new Image(); ball.src = 'dot.png'; apple = new Image(); apple.src = 'apple.png'; }
In the loadImages
function we load three images for the game.
function createSnake() { dots = 3; for (var z = 0; z < dots; z++) { x[z] = 50 - z * 10; y[z] = 50; } }
In the createSnake
function we create the snake object. At the
start, it has three joints.
function checkApple() { if ((x[0] == apple_x) && (y[0] == apple_y)) { dots++; locateApple(); } }
If the head collides with the apple, we increase the number of joints of the
snake. We call the locateApple
method which randomly positions a
new apple object.
function move() { ...
In the move
method we have the key algorithm of the game. In order
to understand it, look at how the snake is moving. We control the head of the
snake. We can change its direction with the cursor keys. The rest of the joints
move one position up the chain. The second joint moves where the first was, the
third joint where the second was etc.
for (var z = dots; z > 0; z--) { x[z] = x[(z - 1)]; y[z] = y[(z - 1)]; }
The for loop moves the joints of a snake up the chain.
if (leftDirection) { x[0] -= DOT_SIZE; }
This line moves the head to the left.
function checkCollision() { ...
In the checkCollision
method, we determine if the snake has hit
itself or one of the borders.
for (var z = dots; z > 0; z--) { if ((z > 4) && (x[0] == x[z]) && (y[0] == y[z])) { inGame = false; } }
If the snake hits one of its joints with its head, the game is over.
if (y[0] >= C_HEIGHT) { inGame = false; }
The game is finished if the snake hits the bottom of the canvas.
function locateApple() { var r = Math.floor(Math.random() * MAX_RAND); apple_x = r * DOT_SIZE; r = Math.floor(Math.random() * MAX_RAND); apple_y = r * DOT_SIZE; }
The locateApple
randomly selects x and y coordinates for the apple
object. The apple_x
and apple_y
are the coordinates of
the uppler-left point of the apple image.
function gameCycle() { if (inGame) { checkApple(); checkCollision(); move(); doDrawing(); setTimeout("gameCycle()", DELAY); } }
The gameCycle
function forms a game cycle. Provided that the game
has not finished, we perform collision detection, do movement and drawing. The
setTimeout
function calls recursively the
gameCycle
function.
if ((key == LEFT_KEY) && (!rightDirection)) { leftDirection = true; upDirection = false; downDirection = false; }
If we hit the left cursor key, we set the leftDirection
variable to
true. This variable is used in the move
function to change the
coordinates of the snake object. Notice also that when the snake is heading to
the right, we cannot immediately turn to the left.
Source
This was JavaScript Snake game.