Skip to content

Instantly share code, notes, and snippets.

@greggman
Last active August 21, 2025 23:33
Show Gist options
  • Save greggman/802656cfbbd28d751c110d17030e085c to your computer and use it in GitHub Desktop.
Save greggman/802656cfbbd28d751c110d17030e085c to your computer and use it in GitHub Desktop.
Tiny-ish maze code
:root {
color-scheme: light dark;
}
html, body {
margin: 0;
height: 100%;
display: flex;
flex-direction: column;
}
html, input {
font-family: monospace;
}
input {
width: 4em;
}
#ui, input {
display: inline-block;
border: none;
}
#maze {
flex: 1 1 auto;
min-height: 0;
}
#error {
display: inline-block;
font-weight: bold;
color: red;
}
canvas {
image-rendering: pixelated;
display: block;
width: 100%;
height: 100%;
background-color: black;
}
<div>
<fieldset id="ui">
<label>width:<input id="width" type="number" placeholder="width" value="91"></label>
<label>height:<input id="height" type="number" placeholder="height" value="49"></label>
<label>speed:<input id="speed" type="number" placeholdr="speed" value="25"></label>
</fieldset>
<button id="go" type="button">Go!</button>
<div id="error"></div>
</div>
<div id="maze"><canvas width="91" height="49"></canvas></div>
// converted from https://archive.org/details/1981-12-compute-magazine/page/58/mode/2up
const kEdge = 0xFF0000FF;
const kWall = 0xFFFF0000;
const kHall = 0xFFFFFFFF;
const kMaxSize = 2048;
// random integer from 0 to max - 1
const rnd = max => Math.random() * max | 0;
const uiElem = document.querySelector('#ui');
const widthElem = document.querySelector('#width');
const heightElem = document.querySelector('#height');
const speedElem = document.querySelector('#speed');
const goElem = document.querySelector('#go');
const errorElem = document.querySelector('#error');
const canvas = document.querySelector('canvas');
let running = false;
goElem.addEventListener('click', go);
function getValue(elem, min, max, name) {
const width = parseInt(elem.value);
if (width >= min && width <= max) {
return width;
}
errorElem.textContent = `${name} must be >= ${min} and <= ${max}`;
return undefined;
}
async function go() {
if (running) {
running = false
} else {
running = true;
errorElem.textContent = '';
const width = getValue(widthElem, 1, kMaxSize, 'width');
const height = getValue(heightElem, 1, kMaxSize, 'height');
const speed = getValue(speedElem, 0, kMaxSize, 'speed');
if (width === undefined || height === undefined || speed === undefined) {
return;
}
canvas.width = width * 2 + 1;
canvas.height = height * 2 + 1;
goElem.textContent = 'Stop!';
await generateMaze(canvas, speed);
goElem.textContent = 'Go!';
running = false;
}
}
go();
async function generateMaze(canvas, speed) {
const { width, height } = canvas;
if (width % 2 !== 1 || height % 2 !== 1) {
throw new Error(`canvas must be odd size`);
}
const ctx = canvas.getContext('2d');
const imgData = new ImageData(width, height);
const data = new Uint32Array(imgData.data.buffer);
const show = (() => {
let count = 0;
return async function show(force) {
if (force || (speed > 0 && (count++) % speed === 0)) {
ctx.putImageData(imgData, 0, 0);
await new Promise(resolve => requestAnimationFrame(resolve));
}
}
})();
prep(data, width, height);
await makeMaze(data, width, height);
async function makeMaze(data, width, height) {
const dirOffset = [2, -width * 2, -2, width * 2];
const startX = rnd((width - 1) / 2) * 2 + 1;
const startY = rnd((height - 1) / 2) * 2 + 1;
let offset = startY * width + startX;
data[offset] = 5;
while (true) {
// 220
let dir = rnd(4);
let startDir = dir;
// 230
while(true) {
if (!running) {
return;
}
let newOffset = offset + dirOffset[dir];
if (data[newOffset] === kWall) {
data[newOffset] = dir + 1;
data[offset + dirOffset[dir] / 2] = kHall;
offset = newOffset;
await show()
break;
} else {
dir = (dir + 1) % 4;
if (dir !== startDir) {
continue;
} else {
dir = data[offset];
data[offset] = kHall;
await show();
if (dir < 5) {
offset = offset - dirOffset[dir - 1];
break;
} else {
await show(true);
return;
}
}
}
}
}
}
function prep(data, width, height) {
data.fill(kWall);
data.fill(kEdge, 0, width); // Set top wall
const bottomOff = (height - 1) * width;
data.fill(kEdge, bottomOff, bottomOff + width); // Set the bottom wall
for (let y = 0; y < height - 1; ++y) {
data[y * width] = kEdge; // Set left wall
data[(y + 1) * width - 1] = kEdge; // Set right wall
}
}
}
{"name":"Tiny-ish maze code","settings":{},"filenames":["index.html","index.css","index.js"]}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment