SNAKE!

Image generated with ChatGPT

Remember the first time you played Snake on a tiny phone screen? If so, you might enjoy this nostalgia trip! Scroll donw if you haven’t already noticed: there’s a playable Snake game. Go ahead, give it a try, use your arrow keys to navigate the snake and devour those apples (without running into your tail!).

Play Snake!

Yep, a game, embedded right at the top of this blog post. No iframe. No external libraries. Just HTML, CSS, and vanilla JavaScript doing what they do best rendering a dynamic, fully-interactive mini-game straight into your browser.

What you are seeing it's in reality a small snippet of code, all packed inside a <details> tag, it loads with a click and runs seamlessly on an HTML <canvas>. It responds to your arrow keys, wraps around the edges, grows as it eats, and yes, crashes when it bites itself. Just like the one you played on your old Nokia or on your first PC.

But here’s the cool part: this isn’t just for fun. It’s a compact demo of how simple game logic, canvas rendering, and input handling can come together to build something that feels alive directly inside a blog post.

So if you’re into games, browser wizardry, or just love the idea of turning code into something playable, this breakdown is for you.

Let’s dive in.

Understanding the Code

<details>
<summary>Click to view the Snake code with comments</summary>

<!-- SNAKE GAME START -->
<div style="text-align: center; margin: 20px 0;">
  <h3>Play Snake!</h3>
  <!-- The canvas on which the snake game is rendered -->
  <canvas 
    id="snakeCanvas" 
    width="400" 
    height="400" 
    style="border: 1px solid #444;">
  </canvas>
  
  <!-- Display game messages (like "Game Over") -->
  <p id="snakeStatus" style="font-weight: bold; margin-top: 10px;"></p>
</div>

<script>
  /***************************************************
   * 1. Getting references to our HTML elements
   ***************************************************/
  // Grab the <canvas> element by its ID
  const canvas = document.getElementById("snakeCanvas");
  // 2D context is needed to draw shapes on the canvas
  const ctx = canvas.getContext("2d");
  // Grab the paragraph element to show the game status
  const statusText = document.getElementById("snakeStatus");

  /***************************************************
   * 2. Game configuration constants and variables
   ***************************************************/
  // The size of each grid cell (a 20x20 cell for a 400x400 canvas)
  const gridSize = 20;
  // tileCount indicates how many cells fit across (and down) the canvas
  const tileCount = canvas.width / gridSize;

  // Initial snake position (x, y)
  let snakePosX = 10;
  let snakePosY = 10;

  // Initial snake velocity (x, y) = (0, 0) means not moving
  let velocityX = 0;
  let velocityY = 0;

  // This array will store the coordinates of each snake segment
  let snakeTrail = [];

  // Starting length of the snake
  let snakeLength = 5;

  // Apple (food) initial coordinates
  let appleX = 15;
  let appleY = 15;

  /***************************************************
   * 3. Game initialization
   ***************************************************/
  // Listen for keyboard arrow keys to move the snake
  window.addEventListener("keydown", keyPush);

  // Set the main game loop to run every 120 milliseconds
  // (faster or slower to adjust difficulty)
  setInterval(gameLoop, 120);

  /***************************************************
   * 4. Main game loop
   ***************************************************/
  function gameLoop() {
    // Update the snake's head position by its velocity
    snakePosX += velocityX;
    snakePosY += velocityY;

    // Handle "wrapping" around edges
    if (snakePosX < 0) {
      snakePosX = tileCount - 1;
    } else if (snakePosX >= tileCount) {
      snakePosX = 0;
    }
    if (snakePosY < 0) {
      snakePosY = tileCount - 1;
    } else if (snakePosY >= tileCount) {
      snakePosY = 0;
    }

    // Clear the canvas each frame
    // Here we set the background to black ("#000")
    ctx.fillStyle = "#000";
    ctx.fillRect(0, 0, canvas.width, canvas.height);

    // Draw the apple in red
    ctx.fillStyle = "red";
    ctx.fillRect(appleX * gridSize, appleY * gridSize, gridSize - 2, gridSize - 2);

    // Draw the snake in green
    ctx.fillStyle = "green";
    snakeTrail.forEach(segment => {
      ctx.fillRect(segment.x * gridSize, segment.y * gridSize, gridSize - 2, gridSize - 2);
    });

    // Add the new head position to the snake's body (snakeTrail)
    snakeTrail.push({ x: snakePosX, y: snakePosY });

    // Trim the snakeTrail to match the snake's length
    // If it grows too big, remove the oldest segment
    while (snakeTrail.length > snakeLength) {
      snakeTrail.shift();
    }

    // Check if the snake head is on the apple
    if (snakePosX === appleX && snakePosY === appleY) {
      // Grow the snake by one segment
      snakeLength++;
      // Randomly place the next apple
      appleX = Math.floor(Math.random() * tileCount);
      appleY = Math.floor(Math.random() * tileCount);
    }

    // Check for collision with the snake's own body
    for (let i = 0; i < snakeTrail.length - 1; i++) {
      const segment = snakeTrail[i];
      // If the head overlaps any segment, the game is over
      if (segment.x === snakePosX && segment.y === snakePosY) {
        // Reset the snake
        snakeLength = 5;
        velocityX = 0;
        velocityY = 0;
        snakePosX = 10;
        snakePosY = 10;
        // Display a message to the player
        statusText.textContent = "Game Over! Press any arrow key to start again";
        return;
      }
    }

    // If still alive, encourage the player to use arrow keys
    statusText.textContent = "Use arrow keys to play!";
  }

  /***************************************************
   * 5. Keyboard event handling
   ***************************************************/
  function keyPush(evt) {
    switch (evt.key) {
      case "ArrowLeft":
        if (velocityX !== 1) {
          velocityX = -1;
          velocityY = 0;
        }
        break;
      case "ArrowUp":
        if (velocityY !== 1) {
          velocityX = 0;
          velocityY = -1;
        }
        break;
      case "ArrowRight":
        if (velocityX !== -1) {
          velocityX = 1;
          velocityY = 0;
        }
        break;
      case "ArrowDown":
        if (velocityY !== -1) {
          velocityX = 0;
          velocityY = 1;
        }
        break;
    }
  }
</script>
<!-- SNAKE GAME END -->

</details>

HTML Structure

At the core of the Snake game, we have a straightforward HTML setup:

  • Canvas Element: This is the visual playground where the snake moves and interacts with apples.
  • Status Display: A simple paragraph element to communicate messages like "Game Over" or "Use arrow keys to play!"

JavaScript Initialization

JavaScript is essential for adding interactivity:

  • Canvas Context (ctx): Provides tools to draw elements dynamically.
  • Event Listeners: Captures keyboard inputs to control snake movement.
  • Game Loop (setInterval): Repeatedly updates the game's state approximately eight times per second, refreshing visuals and logic.

Variables and Their Roles

Let's briefly explore key variables:

  • snakePosXsnakePosY: Track the snake’s head position on the grid.
  • velocityXvelocityY: Determine the snake's direction and speed.
  • snakeTrail: Holds the coordinates of the snake's segments.
  • snakeLength: Manages how long the snake grows when eating apples.
  • appleXappleY: Define apple positions that randomly change after each bite.

Game Loop Breakdown

Within the main loop, the following happens continuously:

  • Position Update: Snake's position updates based on velocity, with boundary wrapping to seamlessly re-enter from the opposite side.
  • Canvas Clearing and Redrawing: Each frame refreshes by clearing the canvas, then redrawing the snake and apple.
  • Collision Detection: Checks if the snake has collided with an apple (to grow) or itself (ending the game).
  • Growth Mechanics: Each apple eaten increases the snake's length, adding complexity to the game.

Keyboard Controls

The game listens for arrow key inputs to adjust the snake’s direction:

  • Prevents direct reversal, adding strategic depth.
  • Responsive controls ensure smooth gameplay.

Wrap Up

That’s all there is to it! The entire game is less than a hundred lines of JavaScript, making it an excellent learning project if you want to dabble in canvas-based animation or event handling.

Thanks for playing and reading! If you have questions or want to share your own Snake modifications (perhaps a rainbow snake or multiple apples?), drop a comment below.

Happy coding and happy gaming!

Share this article: