The Snake game is a classic arcade game where a snake moves around the screen, consuming food and growing in size. The game ends when the snake collides with the walls or itself.
Remember the classic Snake game?
Let’s bring it to life with JavaScript!
This tutorial will guide you in building a simple, interactive Snake game using HTML, CSS, and JavaScript. You’ll have a fun game and a better grasp of core web development concepts by the end.
We’ll break the code into three parts: HTML for the structure, CSS for styling, and JavaScript for game logic. The game will be interactive and will include a few cool features, such as:
Click to start: The game will prompt you to click the canvas to start or restart the game.
Score display: The score will be displayed on the canvas, and the final score will appear when the game ends.
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Snake Game</title><link rel="stylesheet" href="style.css"> <!-- Link to external CSS file --></head><body><canvas id="gameCanvas" width="600" height="400"></canvas><script src="index.js"></script> <!-- Link to external JS file --></body></html>
In the HTML code above:
Lines 7 and 12: We include separate CSS and JavaScript files for a cleaner structure.
Line 10: This canvas is the area where the game is drawn.
The style.css
file will contain the basic styling to center the canvas on the screen and give the game a clean look.
body {text-align: center;margin: 0;padding: 0;background-color: #222;color: white;font-family: Arial, sans-serif;}canvas {margin-top: 50px;border: 2px solid white;background-color: black;display: block;margin-left: auto;margin-right: auto;}
In the CSS file above:
Center the canvas: We center the canvas in the middle of the page.
Background and text styles: We set the background and text colors for better contrast.
In the index.js
file, we define the game logic, including the snake’s movement, food generation, collision detection, and game state management (e.g., starting, ending, and restarting the game). We also add the functionality to show the score and the “Click to Start” message.
There are certain key features that every snake game has, such as:
Snake movement: The snake continuously adds new head segments based on the arrow key input.
Food generation: Food is randomly placed on the canvas, and when the snake eats it, the snake grows.
Collision detection: The game ends if the snake hits the wall or itself.
Score display: The score updates as the snake eats food, and the final score is shown at the end of the game.
Let’s implement these key features of a snake game step-by-step:
The first thing we need to do is access the canvas and set up the 2D drawing context. The canvas will be where we draw the snake and food, and the context (ctx
) will be used to perform the drawing operations.
// Get the canvas and contextconst canvas = document.getElementById('gameCanvas');const ctx = canvas.getContext('2d');
Here, we grab the <canvas>
element and set up the 2D drawing context—where all the game visuals (snake, food, score) will be rendered.
Note: If you’re ready to dive even deeper into HTML5 canvas, consider taking your skills to the next level. The “HTML5 Canvas From Noob to Ninja: An Interactive Deep Dive” course on Educative offers a comprehensive exploration of canvas—from advanced drawing techniques to optimizing animations for game development. This interactive course provides hands-on challenges and real-world projects that will help you unlock the full potential of Canvas.
Kirupa is an MIT grad who works at Microsoft as Senior Program Manager. He has been tinkering with web technologies since 1998. He works on the Edge browser team where he gets to sink his teeth into the cutting-edge web technologies. HTML5 is taking over the browsers and one of the coolest things in HTML5 is the canvas - the immediate mode drawing surface you have in HTML for bringing pixels to life using JavaScript. It is used to create games, graphs, and animations. This is an extensive course with interactive playgrounds (so you don't have to install anything). The course can be divided into four parts. 1. Understand basics of Canvas and compare it with DOM. 2. Learn how to draw shapes. 3. Take a deep dive into canvas transformations and animations. 4. Explore advanced mouse and keyboard handling to create truly interactive applications using canvas. If none of what I just wrote makes sense, then the content here is just what you need 😀 . Let's get started.
Now, let’s define some essential variables:
Grid size: Defines the size of each grid cell (each cell will be a square).
Snake: The snake will be represented as an array of objects, each holding a snake segment’s x
and y
position.
Food: The food will be a single object with x
and y
coordinates.
Direction: A direction object will store the current movement direction of the snake (right, left, up, or down).
Score: The score will start at 0 and increase when the snake eats food.
Game state: We need a flag to indicate if the game is over (isGameOver
) and whether the game has started (gameStarted
).
const gridSize = 10; // Size of the grid cellslet snake = [{ x: 160, y: 160 }, { x: 150, y: 160 }, { x: 140, y: 160 }];let food = { x: 420, y: 220 };let direction = { x: gridSize, y: 0 }; // Initial direction (moving right)let score = 0;let gameInterval;let isGameOver = false;let gameStarted = false;
In the code above:
The gridSize
determines the size of each grid cell (10 pixels by 10 pixels).
The snake
is an array of objects representing the snake’s body. The snake starts with three segments.
The food
is an object that defines the food’s position on the canvas.
The direction
controls the snake’s movement. Initially, the snake moves to the right.
The score
is set to 0 at the beginning.
The gameInterval
will hold the interval ID for the game loop.
The isGameOver
and gameStarted
help manage the game state.
We need to reset the game variables and begin the game loop to start the game. The game loop will continuously update the game state and redraw the game. We must also clear any previous game state, such as the “Click to Start” message.
// Function to start/restart the gamefunction startGame() {// Initialize game variablessnake = [{ x: 160, y: 160 }, { x: 150, y: 160 }, { x: 140, y: 160 }];food = { x: 420, y: 220 };direction = { x: gridSize, y: 0 };score = 0;isGameOver = false;gameStarted = true;clearInterval(gameInterval);gameInterval = setInterval(gameLoop, 100); // Start the game loop// Remove any existing "Click to Start" messagedrawGame();}
The startGame()
function initializes or resets the game variables and starts the game loop using the setInterval()
method. The game loop is called every 100 milliseconds, updating the game state and redrawing the canvas. The clearInterval()
method clears any previous intervals before starting a new game.
We must implement the endGame()
function to handle the game over state. This function stops the game loop and sets the game state to “over.” When a collision is detected, we’ll call this function inside the gameLoop()
.
// Function to end the gamefunction endGame() {clearInterval(gameInterval); // Stop the game loopisGameOver = true; // Mark the game as overdrawGame(); // Draw the final state (including the "Game Over" message)}
In the endGame()
function:
clearInterval(gameInterval);
stops the game loop by clearing the interval.
isGameOver = true;
marks the game over, preventing further game updates.
Next, we need to allow the player to control the snake’s direction using the arrow keys. The direction control will only allow the snake to move perpendicularly (no reverse movement).
// Function to handle keyboard inputfunction changeDirection(event) {if (isGameOver) return;if (event.key === 'ArrowUp' && direction.y === 0) {direction = { x: 0, y: -gridSize };} else if (event.key === 'ArrowDown' && direction.y === 0) {direction = { x: 0, y: gridSize };} else if (event.key === 'ArrowLeft' && direction.x === 0) {direction = { x: -gridSize, y: 0 };} else if (event.key === 'ArrowRight' && direction.x === 0) {direction = { x: gridSize, y: 0 };}}
The changeDirection()
function listens for keydown
events and changes the snake’s direction based on which arrow key the user presses. The condition direction.y === 0
ensures that the snake cannot reverse direction. For example, if the snake is moving to the right (x
is positive), pressing the left arrow (x
becomes negative) will not work because the snake would collide with itself.
To ensure the game behaves correctly, we need to detect when the snake collides with walls or itself. This will stop the game and trigger the game-over state.
// Function to detect collision with walls or selffunction detectCollision() {const head = snake[0];// Wall collisionif (head.x < 0 || head.x >= canvas.width || head.y < 0 || head.y >= canvas.height) {return true;}// Self-collisionfor (let i = 1; i < snake.length; i++) {if (head.x === snake[i].x && head.y === snake[i].y) {return true;}}return false;}
The checkCollision()
function verifies if the snake’s head
has hit the walls or its body. It checks for the following conditions:
If the snake’s head
(the first segment in the snake array) goes beyond the canvas boundaries (left, right, top, or bottom).
Suppose the snake’s head collides with any body segments (excluding the head itself). If either of these conditions is met, the game ends.
When the snake eats food, it should grow, and the score should increase. We must detect if the snake’s head is in the same position as the food.
// Function to handle food consumptionfunction eatFood() {const head = snake[0];if (head.x === food.x && head.y === food.y) {score+=10;snake.push({}); // Add new segment to the snake// Generate new food locationfood = {x: Math.floor(Math.random() * (canvas.width / gridSize)) * gridSize,y: Math.floor(Math.random() * (canvas.height / gridSize)) * gridSize};}}
The eatFood()
function checks if the snake’s head eats the food.
It compares the head’s coordinates (snake[0]
) with the food’s coordinates.
If the snake eats the food, the score increases by a certain amount (e.g., 10 points).
A new segment is added to the snake by pushing a new empty object into the snake
array.
A new piece of food is placed randomly on the canvas by generating new x
and y
coordinates for the food.
The game loop is the heart of the game. It will continuously update the game state, move the snake, check for collisions, and redraw the game.
// Function to update the game state and redraw the canvasfunction gameLoop() {if (detectCollision()) {endGame();return;}// Move the snakeconst head = { x: snake[0].x + direction.x, y: snake[0].y + direction.y };snake.unshift(head); // Add new head to the snakeeatFood(); // Check if food is eatenif (!isGameOver) {snake.pop(); // Remove the last segment of the snake; if not, game over}drawGame();}
The gameLoop()
function is called repeatedly by setInterval()
. Each time it runs, it updates the game state, moves the snake, checks for collisions and food collisions, and redraws the game on the canvas. The game loop runs at regular intervals (every 100 milliseconds), giving the game smooth movement and continuous updates.
Next, we must draw the game state (snake, food, score) on the canvas each time the game loop runs. This involves clearing the previous drawing and then rendering the updated game elements.
// Function to draw the game on the canvasfunction drawGame() {ctx.clearRect(0, 0, canvas.width, canvas.height); // Clear the canvas// Draw the snakesnake.forEach((segment, index) => {if (index === 0) {// Draw the head in redctx.fillStyle = 'red';} else {// Draw the body in greenctx.fillStyle = 'green';}ctx.fillRect(segment.x, segment.y, gridSize, gridSize);});// Draw the food in yellowctx.fillStyle = 'yellow';ctx.beginPath(), ctx.arc(food.x + gridSize / 2, food.y + gridSize / 2, gridSize / 2, 0, Math.PI * 2), ctx.fill();// Draw the scorectx.fillStyle = 'white';ctx.font = '20px Arial';ctx.fillText('Score: ' + score, 10, 30);// If the game is over, show a messageif (isGameOver) {ctx.fillStyle = 'white';ctx.font = '30px Arial';ctx.fillText('Game Over', canvas.width / 2 - 85, canvas.height / 2);ctx.font = '20px Arial';ctx.fillText('Score: ' + score, canvas.width / 2 - 40, canvas.height / 2 + 30); // Show the scorectx.fillText('Click to Restart', canvas.width / 2 - 75, canvas.height / 2 + 60);} else if (!gameStarted) {// If the game has not started, show the "Click to Start" messagectx.fillStyle = 'white';ctx.font = '30px Arial';ctx.fillText('Click to Start', canvas.width / 2 - 85, canvas.height / 2);}}
The drawGame()
function renders the entire game state on the canvas. It is called in every game loop iteration to update the display.
Clear the canvas: Before anything is drawn, the canvas is cleared to remove any previous drawings. This is done with ctx.clearRect(0, 0, canvas.width, canvas.height)
to ensure the screen is refreshed.
Draw the snake: The snake is drawn segment by segment. The head is drawn in red, and the body is drawn in green. The fillStyle
property is used to set the color, and fillRect()
is used to draw a square for each snake segment.
The color for the first segment (head) is set to red: ctx.fillStyle = 'red'
.
The remaining segments (body) are green: ctx.fillStyle = 'green'
.
Draw the food: The food is drawn as a yellow circle using ctx.arc()
at the current coordinates of the food object.
Display the score: The score is displayed in the top left corner of the canvas using ctx.fillText()
. The text color is white, and the font size is 20px.
Game over state: If the game is over (isGameOver
is true
), a “Game Over” message is displayed in the center of the canvas. The current score and a “Click to Restart” message are also shown.
Start game message: If the game hasn’t started yet (gameStarted
is false
), a “Click to Start” message is shown in the center of the canvas.
This is the final part of the game setup. We set up event listeners to handle user input and interactions with the game. We also ensure the game starts by initially displaying the “Click to Start” message.
// Event listener for keyboard inputwindow.addEventListener('keydown', changeDirection);// Event listener for clicking the canvas to start or restart the gamecanvas.addEventListener('click', () => {if (isGameOver) {startGame(); // Restart the game if it's over} else if (!gameStarted) {startGame(); // Start the game if not started}});// Initialize the game by showing the "Click to Start" messagedrawGame();
We did three things in the code above:
We use an event listener to detect key presses. Specifically, the keydown
event is triggered whenever a key is pressed. When this event occurs, the changeDirection()
function is called to handle changing the snake’s direction.
An event listener is added to the canvas to detect when the user clicks it. If the game is over (isGameOver
is true
), clicking the canvas will restart the game by calling the startGame()
function. If the game hasn’t started yet (gameStarted
is false
), the click will start the game.
To initialize the game, we call the drawGame()
function. This will show the “Click to Start” message in the center of the canvas when the game begins. The function is also invoked after setting up the event listeners to ensure the initial game state is displayed.
Now that we’ve completed each step, it’s time to combine everything into a fully functional Snake game. Below is the complete code that includes all the components we’ve discussed. Click the “Run” button to experience the game:
With everything combined, the Snake game is fully functional. You can play by controlling the snake using the arrow keys and clicking the canvas to start or restart the game.
Troubleshooting tip: If nothing is drawing on the canvas, ensure you’ve correctly linked the <script>
and <canvas>
in your HTML and that your JavaScript file is correctly targeting the canvas ID.
Well done! You’ve learned to use JavaScript and HTML to create an interactive game. We hope you enjoyed creating a simple project.
Enjoyed building this game? Keep the momentum going! To help you on this journey, we recommend checking out the Game Development with JavaScript: Creating Tetris. This course will teach you to apply your front-end JavaScript skills to build your first game. By creating Tetris, you’ll learn game development fundamentals and build a project for your portfolio.
What is the Snake game?
What are the basic requirements for creating a Snake game in JavaScript?
How can I implement a pause feature in a Snake game?
How do I store high scores in a Snake game?
Free Resources