skip to content

JavaScript: Amazing Maze Game

 Tweet0 Tweets

As an experiment in coding we've created a maze generator in PHP, and now a JavaScript add-on to convert it into an interactive game for your enjoyment. Merry Xmas.

Explore the maze

Make your way through the maze by pressing the arrow keys. Take the shortest route and avoid monsters or your score will zero out and the game will end.

Gather treasure along the way to earn bonus points, and don't forget you will need the key 🔑 in order to unlock the exit door and escape.

Your starting score is the minimum number of steps it will take to fetch the key and complete the maze. If you find it too easy, try at a larger size.

Maze generation

We have created a PHP class to make mazes based on a recursive division algorithm, with a bit of embellishment.

The mazes can be any size and either square or rectangular. The key is automatically placed at the furthest point from both the entrance and exit.

If you view the HTML source code you will see that the markup is nothing more than an array of DIV elements with class attributes.

<div id="maze"> <div><div class="wall"></div><div class="wall"></div><div class="wall"></div>...</div> <div><div class="wall"></div><div></div><div class="wall"></div>...</div> ... </div>

There are separate classes for monsters, treasure, the hero, door and key.

Update: we've now released a pure JavaScript maze generator which you can download and run locally.

CSS elements

No graphics were used in creating the maze. The green background is made up of overlapping radial gradients, while the walls instead use linear gradients to create a rough texture.

The colourful icons are all Unicode characters so will vary between platforms:

Monsters🐊 🐍
Treasures🌼 🍄 🌻 💎 🎁
Door & Key🚪 🔑
Our Hero🚶 🚶

Allowing the hero to face in the other direction is achieved using a CSS Transform of scale(-1, 1).

Gameplay

Our JavaScript class (see below) simply accepts the maze grid as presented in the HTML and allows movement by checking the class attribute of the grid element in the selected direction whenever an arrow key is pressed.

We move our hero through the maze by switching the class of the relevant tiles. Similarly when the key or a treasure is taken, we just remove the relevant class attribute.

Source code for mazing.js

// Original JavaScript code by Chirp Internet: chirpinternet.eu // Please acknowledge use of this code by including this header. function Position(x, y) {   this.x = x;   this.y = y; } Position.prototype.toString = function() {   return this.x + ":" + this.y; }; function Mazing(id) {   // bind to HTML element   this.mazeContainer = document.getElementById(id);   this.mazeScore = document.createElement("div");   this.mazeScore.id = "maze_score";   this.mazeMessage = document.createElement("div");   this.mazeMessage.id = "maze_message";   this.heroScore = this.mazeContainer.getAttribute("data-steps") - 2;   this.maze = [];   this.heroPos = {};   this.heroHasKey = false;   this.childMode = false;   this.utter = null;   for(i=0; i < this.mazeContainer.children.length; i++) {     for(j=0; j < this.mazeContainer.children[i].children.length; j++) {       var el =  this.mazeContainer.children[i].children[j];       this.maze[new Position(i, j)] = el;       if(el.classList.contains("entrance")) {         // place hero at entrance         this.heroPos = new Position(i, j);         this.maze[this.heroPos].classList.add("hero");       }     }   }   var mazeOutputDiv = document.createElement("div");   mazeOutputDiv.id = "maze_output";   mazeOutputDiv.appendChild(this.mazeScore);   mazeOutputDiv.appendChild(this.mazeMessage);   mazeOutputDiv.style.width = this.mazeContainer.scrollWidth + "px";   this.setMessage("first find the key");   this.mazeContainer.insertAdjacentElement("afterend", mazeOutputDiv);   // activate control keys   this.keyPressHandler = this.mazeKeyPressHandler.bind(this);   document.addEventListener("keydown", this.keyPressHandler, false); }; Mazing.prototype.enableSpeech = function() {   this.utter = new SpeechSynthesisUtterance()   this.setMessage(this.mazeMessage.innerText); }; Mazing.prototype.setMessage = function(text) {   this.mazeMessage.innerHTML = text;   this.mazeScore.innerHTML = this.heroScore;   if(this.utter) {     this.utter.text = text;     window.speechSynthesis.cancel();     window.speechSynthesis.speak(this.utter);   } }; Mazing.prototype.heroTakeTreasure = function() {   this.maze[this.heroPos].classList.remove("nubbin");   this.heroScore += 10;   this.setMessage("yay, treasure!"); }; Mazing.prototype.heroTakeKey = function() {   this.maze[this.heroPos].classList.remove("key");   this.heroHasKey = true;   this.heroScore += 20;   this.mazeScore.classList.add("has-key");   this.setMessage("you now have the key!"); }; Mazing.prototype.gameOver = function(text) {   // de-activate control keys   document.removeEventListener("keydown", this.keyPressHandler, false);   this.setMessage(text);   this.mazeContainer.classList.add("finished"); }; Mazing.prototype.heroWins = function() {   this.mazeScore.classList.remove("has-key");   this.maze[this.heroPos].classList.remove("door");   this.heroScore += 50;   this.gameOver("you finished !!!"); }; Mazing.prototype.tryMoveHero = function(pos) {   if("object" !== typeof this.maze[pos]) {     return;   }   var nextStep = this.maze[pos].className;   // before moving   if(nextStep.match(/sentinel/)) {     this.heroScore = Math.max(this.heroScore - 5, 0);     if(!this.childMode && this.heroScore <= 0) {       this.gameOver("sorry, you didn't make it");     } else {       this.setMessage("ow, that hurt!");     }     return;   }   if(nextStep.match(/wall/)) {     return;   }   if(nextStep.match(/exit/)) {     if(this.heroHasKey) {       this.heroWins();     } else {       this.setMessage("you need a key to unlock the door");       return;     }   }   // move hero one step   this.maze[this.heroPos].classList.remove("hero");   this.maze[pos].classList.add("hero");   this.heroPos = pos;   // after moving   if(nextStep.match(/nubbin/)) {     this.heroTakeTreasure();     return;   }   if(nextStep.match(/key/)) {     this.heroTakeKey();     return;   }   if(nextStep.match(/exit/)) {     return;   }   if(this.heroScore >= 1) {     if(!this.childMode) {       this.heroScore--;     }     if(!this.childMode && (this.heroScore <= 0)) {       this.gameOver("sorry, you didn't make it");     } else {       this.setMessage("...");     }   } }; Mazing.prototype.mazeKeyPressHandler = function(e) {   var tryPos = new Position(this.heroPos.x, this.heroPos.y);   switch(e.keyCode)   {     case 37: // left       this.mazeContainer.classList.remove("face-right");       tryPos.y--;       break;     case 38: // up       tryPos.x--;       break;     case 39: // right       this.mazeContainer.classList.add("face-right");       tryPos.y++;       break;     case 40: // down       tryPos.x++;       break;     default:       return;   }   this.tryMoveHero(tryPos);   e.preventDefault(); }; Mazing.prototype.setChildMode = function() {   this.childMode = true;   this.heroScore = 0;   this.setMessage("collect all the treasure"); };

expand code box

Usage:

On a page which includes the maze HTML code (which can be copied and pasted from the page source):

<link rel="stylesheet" type="text/css" href="/mazing.css"> <div id="maze_container"> <div id="maze" data-steps="???"> <!-- insert the maze HTML code here --> </div> </div> <script src="/mazing.js"></script> <script> window.addEventListener("DOMContentLoaded", function(e) { var maze = new Mazing("maze"); /* maze.setChildMode(); */ }, false); </script>

A reminder that you will need to make a local copy of the CSS and JavaScript files rather than hot-linking directly from this website.

< JavaScript

Send a message to The Art of Web:


used only for us to reply, and to display your gravatar.

<- copy the digits from the image into this box

press <Esc> or click outside this box to close

User Comments

Post your comment or question

17 November, 2021

Hey, I couldn't find the maze HTML code. Could you please tell me where exactly is it or could you post it? Thank you!

2 November, 2021

Hey, thank you so much for this script.

Do you know how to place the monsters using your JS script? At the moment, I can only get the door an key along with the player.

The monsters are not actually placed - they're just wall sections dressed up using a CSS class

13 December, 2020

Hey I'd also love to see the random generation of the maze if you get it working!

Also...Am I allowed to use this code for my own projects if I credit you?

25 February, 2020

I have been following your Java script Maze Game tutorial and it has been amazing.

I cannot, however get it to randomly generate, do you know why that would be?

Thanks,

Henry

We have a PHP script running in the background to generate the maze, and the JavaScript just brings it to life. One day I will get around to converting the PHP script to JavaScript, which makes more sense

top