Introducing SnakeJs, a small side-project
Side project, pet project call it as you wish… I wanted to have some time with front end and game development. I stumbled on an old (started in 2015) project of mine, called SnakeJs. I decided to give it some more love and time.
What is it?
Current state of the project
js/controls.js- handles user interactions via keyboard.
js/graphics.js- renders the game.
js/physics.js- game logic.
js/snake.js- ties all above modules together; implements game loop.
index.html- app template; initializes game.
Currently, there is no assets pre-processing required. All files are loaded as are into
and the logic is initialized in
window.onload. The CSS is very minimalistic, so much that I decided to inline it into
I pretty much like this setup for its simplicity. Publishing the app doesn't require a build process. Even when loading the files separately Lighthouse in Chrome Devtools doesn't even complain about that.
How the game works
This section describes the main parts of the game, how are they implemented and how do they fit together.
Displaying the game state -
Graphics uses the canvas HTML element. It grabs its 2d context and uses the draw rect.
You configure some drawing settings like the fill and stroke styles. It's useful to
think of the context as a stack. You can call
save() on it and a checkpoint is made so the settings can be restored
when you call
restore() on the context.
Graphics object you can find some configuration values like:
The methods of interest are:
It's important to call them in the right order because their effects are drawn on top of each other. The recommended order would be to draw the map first, then the fruit and the snake last.
Interacting with user input -
We have to control the snake somehow. For that we're using the keyboard (not very mobile friendly)
by listening to keyboard events. We're interested in keyup events more as the keydown events are fired simultaneously
when the key is pressed. All we need to do is call
There are three properties on the
actions- you can think of it as an enum of the allowed movement directions.
actionQueue- a fifo queue of actions that the user triggered.
bindings- a map of key codes to actions to trigger.
You can have a look at the
createDefaults method to see how the codes are mapped to actions.
Rules of the game -
Rules for ending the game, checking collisions and moving the snake are placed in the
The main entry point to the logic is the
step method. It should be called from the game loop to update the game state.
In case a fruit was collected or the snake collided with the boarders or own body events are dispatched.
We register handlers for those events in the
Concept of time; the game loop -
snake.js holds some configuration values, interacts with the DOM and ties the logic together.
It also implements the game loop, a concept that is present in all interactive games.
It's responsible for controlling the game speed by calling the physics step method in given intervals.
requestAnimationFrame instead of
setInterval as it's more suited for game development.
This piece of the application initializes all DOM nodes and registers event handlers for when fruits are collected or collisions have occurred, so it can increment the score or end the game.
What are my plans?
As you already heard the code base is pretty old, so I'd like to improve it. My wishlist of improvements currently contains:
- Creating some deployment pipeline so the code gets published to some static site host (probably GitHub Pages, or Cloudflare Pages).
- Modernize the code base: use modern JS syntax or even TypeScript.
- Setup a bundler / minimizer / compiler (Webpack, Parcel or Rollup).
- Add unit tests.
I also have some more far-fetched ideas (more on the bottom of the TODO list) like:
- improving the game on mobile; adding some on-screen controls and better support for different screen sizes.
- porting the game to Elm. I'm learning Elm currently, and I thought it could be a good exercise.
If you have any suggestions I also encourage you to start a discussion on GitHub.