skip to content

JavaScript: Amazing Maze Game

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.

Control keys

⇦ ⇨ ⇧ ⇩

Click to start

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.

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) which flips the element horizontally.

Gameplay

Our JavaScript class (see below) 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

var Position = function(x, y) {   this.x = x;   this.y = y; } Position.prototype.toString = function() {   return this.x + ":" + this.y; }; var Mazing = function(id) {   // Original JavaScript code by Chirp Internet: www.chirpinternet.eu   // Please acknowledge use of this code by including this header.   /* 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 on entrance square */         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) {   /* display message on screen */   this.mazeMessage.innerHTML = text;   this.mazeScore.innerHTML = this.heroScore;   if(this.utter && text.match(/^\w/)) {     /* speak message aloud */     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/)) {     /* ran into a moster - lose points */     this.heroScore = Math.max(this.heroScore - 5, 0);     if(!this.childMode && (this.heroScore <= 0)) {       /* game over */       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;   /* check what was stepped on */   if(nextStep.match(/nubbin/)) {     this.heroTakeTreasure();     return;   }   if(nextStep.match(/key/)) {     this.heroTakeKey();     return;   }   if(nextStep.match(/exit/)) {     return;   }   if((this.heroScore >= 1) && !this.childMode) {     this.heroScore--;     if(this.heroScore <= 0) {       /* game over */       this.gameOver("sorry, you didn't make it");       return;     }   }   this.setMessage("..."); }; Mazing.prototype.mazeKeyPressHandler = function(e) {   var tryPos = new Position(this.heroPos.x, this.heroPos.y);   switch(e.key)   {     case "ArrowLeft":       this.mazeContainer.classList.remove("face-right");       tryPos.y--;       break;     case "ArrowUp":       tryPos.x--;       break;     case "ArrowRight":       this.mazeContainer.classList.add("face-right");       tryPos.y++;       break;     case "ArrowDown":       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" 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

User Comments

Post your comment or question

30 August, 2023

how to change the scale of the maze instead of the size

Everything to do with the appearance is handled by CSS

17 April, 2022

How do I place the treasures and monsters ? I thought it was part of the javascript. But it does not seem to be there. Please help. THanks

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