Building a Classic Snake Game with JavaScript

Introduction to the Snake Game

The Snake game is a classic arcade game that has entertained countless players since its inception in the late 1970s. The concept is simple yet addictive: control a snake on a bordered plane and consume food to grow longer while avoiding collision with the walls or itself. In this tutorial, we will dive into building a fully functional Snake game using JavaScript, utilizing HTML5 canvas for our game graphics, and CSS for styling.

This project will provide an ideal environment for beginners to strengthen their JavaScript skills and familiarize themselves with game development concepts. You’ll be creating a hands-on project that reinforces core programming principles while also allowing for creativity in design and execution. Furthermore, as we walk through the development process, we’ll explore important coding practices, variable management, and event handling.

By the end of this tutorial, you’ll have a fully playable Snake game that you can customize and build upon. Let’s get started!

Setting Up the Project

To kick off our game development journey, create a new directory for your project and add three essential files: index.html, style.css, and script.js. This structure is standard practice for web projects and will help keep your code organized.

Here’s a simple structure:

project-folder/
  ├── index.html
  ├── style.css
  └── script.js

Now, let’s fill in the index.html file with the basic HTML boilerplate. We will create a canvas where our game will take place and include links to our CSS and JavaScript files. Start with the following code:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="style.css">
    <title>Snake Game</title>
</head>
<body>
    <canvas id="gameCanvas" width="400" height="400"></canvas>
    <script src="script.js"></script>
</body>
</html>

This code sets up a canvas element where our game will be rendered, and it links to our CSS for styling and JavaScript for the game logic.

Styling the Game Area

Next, let’s focus on styling the game canvas. Open your style.css file and add the following styles to center the game on the page and give it a clean look:

body {
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100vh;
    margin: 0;
    background-color: #f0f0f0;
}

canvas {
    border: 2px solid #333;
    background-color: #fff;
}

Here, we’re centering the canvas using Flexbox and adding a border to make it stand out. The canvas background will be white, while the body has a light gray color to enhance visibility.

Your game area should now look neat and organized. Ensure that your HTML and CSS files are properly linked so that styles are applied as expected. Before diving into the JavaScript, it’s worthwhile to take a moment to contemplate the game mechanics.

Understanding Game Mechanics

The core mechanics of our Snake game revolve around the following components: the snake itself, food, the game area, and the rules governing snake movement and collision detection. Let’s break down each element to understand how they will function.

The snake will be represented as an array of segments, each segment being a part of the snake’s body. As the snake consumes food, the array grows. The food will randomly appear in the game area, and when the snake’s head collides with the food, the score increases, and the food respawns at a new location.

Additionally, we must implement movement controls. The snake needs to move continuously in the direction last specified by the player through the keyboard. We’ll use arrow keys to control the snake’s movement, and the snake will wrap around the canvas edges. Thus, moving right from the right edge will bring the snake to the left edge.

Implementing the Game Logic in JavaScript

With the project set up and a solid understanding of our game’s mechanics, it’s time to jump into the code. Open script.js and start by defining some key variables needed for our game:

const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const box = 20;
let snake = [{ x: 9 * box, y: 9 * box }];
let direction;
let food = { x: Math.floor(Math.random() * 20) * box, y: Math.floor(Math.random() * 20) * box };

In the code above, we set up our game canvas and its context, define the size of each box in our grid, create the initial position of the snake, and randomly place food in the game area.

Now we need to handle key events for controlling the snake. Add the following event listener at the top of the script:

document.addEventListener('keydown', directionControl);

function directionControl(event) {
    if (event.keyCode == 37 && direction !== 'RIGHT') direction = 'LEFT';
    else if (event.keyCode == 38 && direction !== 'DOWN') direction = 'UP';
    else if (event.keyCode == 39 && direction !== 'LEFT') direction = 'RIGHT';
    else if (event.keyCode == 40 && direction !== 'UP') direction = 'DOWN';
}

With this code, we can capture arrow key presses and update the snake’s direction while preventing it from directly reversing.

Creating the Game Loop

A game loop is essential for updating the game’s state and rendering frames. To create this functionality, implement the draw and update functions:

function draw() {
    ctx.clearRect(0, 0, canvas.width, canvas.height);

    // Draw food
    ctx.fillStyle = 'red';
    ctx.fillRect(food.x, food.y, box, box);

    // Draw snake
    for (let i = 0; i < snake.length; i++) {
        ctx.fillStyle = (i === 0) ? 'green' : 'lightgreen';
        ctx.fillRect(snake[i].x, snake[i].y, box, box);
        ctx.strokeStyle = 'darkgreen';
        ctx.strokeRect(snake[i].x, snake[i].y, box, box);
    }
}

In the draw function, we clear the canvas for each frame, draw the food in red, and render the snake segments in green. Pay attention to how we differentiate between the head and body segments.

Next, implement movement and collision detection logic inside the update function:

function update() {
    let snakeX = snake[0].x;
    let snakeY = snake[0].y;

    // Move snake in the direction
    if (direction === 'LEFT') snakeX -= box;
    if (direction === 'UP') snakeY -= box;
    if (direction === 'RIGHT') snakeX += box;
    if (direction === 'DOWN') snakeY += box;

    // Collision with food
    if (snakeX === food.x && snakeY === food.y) {
        food = {
            x: Math.floor(Math.random() * 20) * box,
            y: Math.floor(Math.random() * 20) * box
        };
    } else {
        snake.pop();
    }

    // Add new head
    let newHead = { x: snakeX, y: snakeY };
    snake.unshift(newHead);
}

This code checks for snake movement and manages food consumption. If the snake eats food, a new food item is generated; otherwise, the tail (last segment) of the snake is removed to simulate movement.

Finalizing the Game Logic

Now, let’s implement collision detection to manage game over conditions. You can add this logic within the update function:

// Collision detection
if (snakeX < 0 || snakeY < 0 || snakeX >= canvas.width || snakeY >= canvas.height || collision(newHead, snake)) {
    clearInterval(game);
    alert('Game Over!');
}

function collision(head, array) {
    for (let i = 1; i < array.length; i++) {
        if (head.x === array[i].x && head.y === array[i].y) {
            return true;
        }
    }
    return false;
}

This code checks if the snake collides with the walls or itself. If it does, the game will stop, and an alert will notify the player that the game is over.

Finally, we can tie everything together by starting our game loop. At the bottom of the script, initiate the game loop with:

let game = setInterval(() => {
    draw();
    update();
}, 100);

This code sets up an interval to call our draw and update functions every 100 milliseconds, creating the gameplay experience.

Customizing Your Snake Game

Congratulations! You have successfully built a basic Snake game! Now you can explore various enhancements to make the game more engaging. Here are a few ideas to consider:

  • Scoring System: Implement a scoring mechanism that tracks the number of food items consumed.
  • Levels: Introduce different levels of difficulty where the speed of the snake increases over time.
  • Responsive Design: Modify the game canvas to resize based on the viewport to make it more playable on different devices.
  • Sound Effects: Add sound effects when the snake eats food or when the game is over for a more immersive experience.

Each of these suggestions can give you an opportunity to sharpen your JavaScript skills further while allowing for creativity in how you structure your code.

Conclusion

In this tutorial, we’ve walked through the complete process of building a classic Snake game with JavaScript. From understanding the basic mechanics to implementing the game loop and collision detection, we’ve covered various essential programming concepts. Whether you're a beginner looking for a project to consolidate your skills or an experienced developer wanting to refine your coding techniques, this Snake game project is a perfect opportunity.

Remember that the key to mastering JavaScript and web development is practice and experimentation. The more you build, the more you’ll understand how to solve problems effectively. I encourage you to modify this game, explore new features, and challenge yourself to create your own unique version.

Keep coding and stay curious!

Scroll to Top