The word maze covers three completely different things depending on who's typing it into Google. It might mean a printable puzzle for a kid, a wall of hedges in a garden, or a playable browser game where a token moves through corridors to reach a goal. This post is about the third one — the browser game, procedurally generated from an algorithm, playable in about 300 lines of JavaScript. If you're searching for how to make a maze in 2026 because you want to ship a small playable thing this weekend, you're in the right place.
What "how to make a maze" actually means in 2026
The literal search phrase how to make a maze pulls three different reader groups. The first is the printable-puzzle crowd — parents and teachers looking for a PDF generator to hand a kid. Those readers usually add "printable" or "coloring page" and bounce off any post that starts talking about arrays and cells. The second is the paper-and-pencil crowd, drawing garden mazes on graph paper. They're looking for design tips, not code. The third — and the one this post targets — is the game-dev crowd. They want a playable maze running in a browser tab, generated at load time, walked through with the arrow keys, with a win state when the player reaches the goal.
The game-dev version breaks down into four honest concerns that every serious maze tutorial has to answer. First, the grid model: how you store the maze in memory. Second, the generator algorithm: the rule that decides which internal walls to knock down so the corridors form a connected but non-trivial layout. Third, the renderer: how you draw walls to the screen every frame. Fourth, the player and win condition: how a moving token bumps against walls and detects the goal. Skip any one of the four and you don't have a game, you have a wallpaper.
The good news: all four concerns are small. The maze grid is a 2D array. The generator is 30 lines. The renderer is a nested loop calling ctx.moveTo() and ctx.lineTo(). The player is four keydown handlers plus a bounds check. Every choice you make in this post is a swap-out — pick a different generator algorithm, pick a different engine, pick a different wall texture — and the rest of the pipeline stays intact.
The maze game loop in one minute (generate render navigate solve)
Every maze game runs the same four-beat loop, and if you internalise it before you touch code, you'll skip the "why isn't my player moving" bug that consumes an evening for most first-timers.
- Generate. On page load, create a 2D array of cells (rows and columns you pick, typically 20 by 20 for a comfortable first build), initialise every cell with all four walls intact and an unvisited flag, then run a maze generator algorithm to knock walls out until every cell is reachable from every other cell.
- Render. On every frame, clear the canvas and draw every remaining wall as a line segment. That's it — the maze is just a bunch of line segments. Draw the player token as a circle at its current cell centre. Draw the goal tile as a coloured square at the destination cell.
- Navigate. Listen for arrow keys or WASD. On each key press, check whether the wall in that direction (top, right, bottom, left) between the current cell and its neighbour has been knocked down. If yes, move the player token to the neighbour cell. If no, the player bumps and stays put (optionally play a bump sound).
- Solve. After every move, compare the player's cell coordinates to the goal cell coordinates. If they match, the player has solved the maze — fire a win overlay, play a chime, offer a regenerate button.
That's the entire game. Every additional feature (fog of war, timer, three-star scoring, ghost enemies, multiplayer) is an overlay on this loop. Ship the four steps first, extend once the base is solid.
Pick your engine for how to make a maze: Phaser 4, vanilla Canvas, or Three.js
Three engines cover 95% of how a browser maze ships in 2026. The right pick depends on how much polish you want and whether you're planning a 2D or 3D presentation.
Vanilla HTML5 Canvas plus requestAnimationFrame is the right default for a maze game. The mechanics have no per-frame physics, no scrolling world beyond the viewport, no sprite animation beyond a single player token, and drawing the maze is a single pass of line() calls per frame that Canvas handles trivially. A complete maze game ships in about 300 lines of JavaScript and under 12 KB total, jam-entry small. Read the MDN Canvas API reference if you haven't shipped a Canvas game before — the two functions you need are strokeRect for the outer border and beginPath / moveTo / lineTo / stroke for the interior walls.
Phaser v4.2.1 "Giedi" (released 9 July 2026, verified against phaser.io/download/stable on 2026-08-08) is worth the extra ~900 KB minified download if you want polish that Canvas alone would take real effort to build: a Scene manager for a menu-to-game-to-win-screen flow, a tween chain for a wall-fade regenerate animation, or particle effects when the player reaches the goal. Phaser's TileMap system is overkill for a maze (mazes aren't tile-based in the RPG-tileset sense — the walls are between cells, not on cells) but a Group of Sprite line segments works fine.
Three.js r185 (verified against threejs.org on 2026-08-08) opens up the 3D first-person maze walkthrough — think a mini Wolfenstein where the player wanders through corridors from an in-world camera. That's a delightful genre remix but it's a bigger project: you need a proper camera controller, collision-detection against wall meshes, and enough lighting to make the corridors atmospheric. For a first browser maze, ship 2D first and Three.js second. WizardGenie will scaffold the entire project in any of the three based on the prompt.
Step 1 — choose a maze generator algorithm (recursive backtracker, Prim's, Kruskal's, recursive division, Wilson's)
The generator algorithm is the single decision that gives your maze its personality. Five families cover the landscape — all verified against the taxonomy on en.wikipedia.org/wiki/Maze_generation_algorithm on 2026-08-08 — and each produces a distinctly-shaped maze even though the underlying rule (build a spanning tree over the grid graph) is the same.
Recursive backtracker (aka randomised depth-first search) is the default choice. Long winding corridors, few short dead ends. It's the maze a searcher pictures when they hear the word maze. The Wikipedia entry describes it exactly: "starting from a random cell, the computer then selects a random neighbouring cell that has not yet been visited. The computer removes the wall between the two cells and marks the new cell as visited, and adds it to the stack to facilitate backtracking." When you hit a dead end, pop the stack and try from the previous cell. When the stack is empty, the maze is done.
Iterative form (use this — the recursive form crashes browsers on grids larger than about 50 by 50):
function generateMaze(grid) {
const start = grid[0][0];
start.visited = true;
const stack = [start];
while (stack.length) {
const cell = stack[stack.length - 1];
const neighbours = getUnvisitedNeighbours(cell, grid);
if (neighbours.length) {
const next = neighbours[Math.floor(Math.random() * neighbours.length)];
knockWall(cell, next);
next.visited = true;
stack.push(next);
} else {
stack.pop();
}
}
}
The Wikipedia entry explicitly recommends the iterative form because deep recursion can exceed the browser's call-stack limit — a 100-by-100 maze has 10,000 cells and the recursive form throws "Maximum call stack size exceeded" in Chrome and Firefox. The iterative form scales to any grid the GPU can render.
Prim's algorithm gives a bushy layout with many short dead ends. The rule: maintain a frontier list of walls that border a visited-and-unvisited pair, on each step pick a random wall from the frontier, knock it out, mark the unvisited side visited, add its bordering walls to the frontier. Stylistically the result reads as a hedge maze or garden puzzle rather than a catacomb.
Kruskal's algorithm uses a disjoint-set data structure. Randomly order every internal wall, then walk the list — for each wall, if the two cells it separates belong to different sets, knock the wall and merge the sets. The visual output is similar to Prim's (short dead ends, uniform mesh) but the algorithm is easier to prove correct and runs in essentially constant amortised time per wall.
Recursive division carves the space with straight walls. Start with an empty rectangle, drop a random horizontal or vertical wall through it with a single one-cell gap, then recurse on the two sub-rectangles. The output looks like a building floor plan — long straight walls, rectangular rooms, dungeon-like corridors. Great if the aesthetic you want is "castle interior" rather than "hedge maze".
Wilson's algorithm is the unbiased one — it samples uniformly from every possible maze of the given size using loop-erased random walks. The tradeoff: it's slower to run and more complex to code. Use it only if the visual output actually needs to look statistically random (research contexts, art projects) rather than "look like a maze".
If you can only ship one generator, ship recursive backtracker. If you want a dropdown that lets the player try different vibes, add Prim's and recursive division as the second and third options — they cover the aesthetic range without the complexity of Wilson's.