Building a Classic Snake Game with Vanilla JavaScript and Supabase

Building a Classic Snake Game with Vanilla JavaScript and Supabase

March 31, 2025

Hey everyone! Ever feel nostalgic for those simple, classic arcade games? I recently decided to revisit one of the greats, Snake! But instead of reaching for a hefty game engine or a complex frontend framework, I challenged myself to build it using plain, vanilla JavaScript. Why? For the fun of it, to brush up on core JS concepts, and to see how far we can get without extra tooling.

I also integrated Supabase to handle a persistent high score list. It's surprisingly easy to connect a backend-as-a-service like Supabase to a simple frontend project like this.

Repository: https://github.com/vianch/snake-js

Demo: Link

Why Go Vanilla?

In a world full of React, Vue, Angular, Svelte, and countless other frameworks, why choose vanilla JavaScript?

  1. Recapturing the Foundations: Frameworks enhance productivity but often obscure essential browser APIs and JavaScript mechanics. Rebuilding something like Snake from scratch reconnects you with the fundamentals—directly manipulating the DOM, handling events, managing state manually, and revisiting the JavaScript event loop. It’s a fun recap refreshened!
  2. Simplicity for Simple Projects: Snake, at its core, isn't overly complex. While a framework could be used, it might be overkill. Vanilla JS keeps the dependency list empty and the build process (if any) minimal.
  3. Fun Factor: Sometimes, it's just plain fun to build something with the basic tools available in every browser.

Project File Tour

Before diving into the code, let's look at how the project is organized:

The main index.html file, CSS for styling, and several JavaScript files, each with a specific responsibility. This separation makes the code easier to manage and understand.

The HTML Skeleton (src/index.html)

The HTML is straightforward. It sets up:

Styling (Brief Mention)

The snake.css file (which we won't dive deep into here) uses basic CSS properties along with CSS Grid to lay out the game board and position the snake and food elements. The "VT323" font gives it that classic, pixelated look.

Core JavaScript Setup (src/assets/js/index.js, constants.js, utils.js)

Managing the Game Board (src/assets/js/board.js)

The Board class acts as an interface between the game logic and the HTML document (the DOM). It doesn't know how the snake moves, only how to display things on the screen.

The Heart of the Game: Logic (src/assets/js/snake.js)

This is where the magic happens! The SnakeGame class extends the Board class, inheriting all its DOM manipulation capabilities, and adds the specific rules and state for Snake.

Adding Persistence with Supabase (src/assets/js/database.js)

This is where Supabase comes in. The Database class encapsulates all interaction with the backend.

By encapsulating the Supabase logic here, the rest of the game code (snake.js) doesn't need to know the specifics of the Supabase API – it just calls methods like this.#database.fetchData(...).

How It All Connects

  1. index.html loads.
  2. index.js runs, creates a SnakeGame instance, and calls listener().
  3. The SnakeGame constructor initializes state, creates the Database instance, and fetches initial scores/high score from Supabase via the Database instance.
  4. The game waits for input (spacebar press or start button click via listener and #handleKeyPress).
  5. startGame() is called: the board is shown, gameStarted becomes true, and #initializeGameInterval() starts the loop.
  6. Game Loop:
    • #moveSnake() calculates the new head position and adds it.
    • #eatFood() checks for food; if not eaten, it removes the tail segment. If eaten, it updates score/speed and generates new food.
    • #checkCollision() checks for game-ending conditions.
    • draw() clears the board (Board.clearBoard) and redraws the snake and food in their new positions (Board.createGameElement, Board.setPosition).
  7. Game Over:
    • #checkCollision() detects a hit and calls endGame().
    • endGame() stops the loop, updates the display, calls #updateHighScore().
    • #updateHighScore() saves the score to the scores table and potentially updates the highScores table in Supabase via the Database instance methods.
    • The game resets, waiting for the player to start again.
  8. Viewing Scores: Clicking "See scores" uses Board.showScoreModal and SnakeGame.#loadScores (which calls Database.fetchData) to display the score list.

Final Thoughts & Next Steps

Building this Snake game was a fun exercise in using vanilla JavaScript for game development. It reinforces how much you can achieve with just the browser's built-in tools. Integrating Supabase was surprisingly straightforward and added a valuable feature – persistent high scores – without needing to build or manage a custom backend.

If you want to extend this, you could:

Result

A classic Snake game implemented in JavaScript, providing a nostalgic trip with a modern twist.

Screenshot 2024-01-07 at 16 56 02 Screenshot 2024-01-07 at 16 56 05

Game Instructions

I hope this walkthrough was helpful! It shows that you don't always need complex frameworks for fun projects. Sometimes, getting back to basics is the best way to learn and create. Happy coding!

Building a Classic Snake Game with Vanilla JavaScript and Supabase | VIANCH | VIANCH