DUNGEON MASTER
A retro 3D maze crawler that runs in your browser.
Find the key or pick the gate lock, reach the exit, and trade your loot on the way down.
Three worlds
What's down there
Features
- Every level is generated from scratch. The maze, the walls, the props and the skyline are all new each time, and each exit takes you one level deeper into a bigger maze. The exit gate can be in any wall far from the entrance, the outer wall or a corridor's, so no direction gives it away.
- Voxel brickwork. Walls are built from 25 cm voxels with real bricks: chipped corners, holes, niches, stepped vaults and crumbling tops.
- The desert. The third world: sandstone ruins half buried in sand, with pyramids, a sphinx and obelisks on the dunes, and the painted tombs beneath them, with glyph-covered walls, gilded doorways and sand trickling from the vault.
- Pick your path. One map by name for the whole run, a world whose maps take turns level by level, or any map of any world. Every map has its picture in the menu.
- A living skyline. Outdoor mazes are ringed by a castle with towers, gatehouses, keeps and halls, with temples, aqueducts and churches on the hills beyond. The forests around them are pixel-art billboards, the whole forest in one draw call.
- Torch and shadows. You come down without a torch: underground only the fires on the walls light the way. The trader sells one for a good sum. It burns down over time, and you relight it at any fire. For a small fortune he sells Candlelight instead: an orb of light that floats at your shoulder, never goes out and outshines any torch. Both are with you on every map, in the halls below and under the night sky. Shadows patrol the corridors and see only what is in front of them (one with its back to you won't notice you, unless you come within two metres), and they don't stop while you pick a lock: lantern wraiths, cave spiders, lost souls (sheet ghosts that glow cold blue) and floating watchers, any of them on any map. A watcher screams when it catches you in its gaze, calling every shadow on the level. Nothing shows where a watcher looks, and if you bump into one, its touch blinds you: for 5 to 30 seconds the world is a dark blur. If one catches you, the level starts over: the same maze with every chest closed again, and your purse back to what you brought in.
- Loot. You can find gold, lockpicks and 17 kinds of gems and soul gems in chests, burial urns and handfuls of coins left in wall pits. Pray at an altar for a blessing: stealth, sturdier lockpicks, swift feet, a ward against one catch, spare lockpicks or a brighter torch. Each maze has its own blessing, rolled once.
- Trader and gate lock. Between levels a wandering trader sells a torch, Candlelight, lockpicks and the next maze's map, and buys your gems. No key? The exit gate can be picked too, though its lock is a hard one.
- Behind the grates. Barred windows look out into more of the underworld: corridors, caves, catacombs, sewers or old mines.
- Play together. Up to six players on one local network go through the same maze together: shared chests, gate and shadows, each player a frog in a cloak of their own colour. One small server file, with the whole game inside it, is all it takes: nothing to install.
- Synthesised sound. Footsteps, fire, dripping water, thunder and a Nordic choir are all generated live with WebAudio, without a single audio file.
Play
There's no build step and nothing to install. Clone the repository and open index.html in a browser:
git clone https://github.com/kerminator-dev/Labyrinth.git
cd Labyrinth
open index.html # or just double-click it
| Key | Action |
|---|
| W A S D | move |
| Shift | run |
| mouse | look |
| E | open, search, pick a lock or the gate, pray |
| F | throw a coin: shadows nearby go to see what clinked |
| left mouse button | playing together: your frog points where you look, for the others to see |
| M | map (once you've found it): held low and tilted, so you can walk and read; press again to hold it up flat, once more to put it away |
| Esc | menu: continue, a new maze for this level, a new game, settings |
When lockpicking, move the mouse to position the pick, then hold W, E, Space or the mouse button to turn the lock. Press Q to back off.
The game runs only while it has the mouse: if the browser lets it go, everything stands still until you click the view. A new level is built behind a loading screen.
Play together
One of you runs the co-op server, everyone on the same network plays from a browser:
npm run build-server # once (needs Rust): dist/DungeonMasterServer, under 2 MB, the game is inside it
- Start
dist/DungeonMasterServer (double-click it, or run it in a terminal). It needs nothing installed. It opens the game in your browser and shows two addresses: one for this computer and one for the others, like http://192.168.1.20:7777. If the macOS firewall is on, it asks whether the server may accept incoming connections: allow it, or the others can't join.
- Everyone opens that address in a browser and picks Play together, types a name (or leaves it empty and gets their cloak's colour for a name) and joins. A copy of the game opened from its folder can join too: type the server's address.
- The first one in leads: they pick the path and the maze size, the others see the pick, and the leader presses Start. A leader whose game is out of view (another tab, the window put away) hands the lead to the next one until they are back.
Not on one network: a tunnel such as ngrok (ngrok http 7777, the port the server shows) gives an https://… link; the others open it (past ngrok's warning page: Visit Site) and join as above. Every browser builds the very same level (Safari, Chrome and the rest), and everyone must have the same version of the game: a page from an older server is not let in.
The rules of a run together:
- One maze for the team. Everyone is in the same maze, with the same chests, the same gate and the same shadows, and a shadow or two more than alone. The others are felt frogs in cloaks of their own colour, their names over their heads, walking and running the way they go (backing away, they keep facing where they look) and looking where they look, and they carry the light they bought: a torch in the right fist, Candlelight over the shoulder.
- Loot. Whoever opens a chest, an urn or a wall pit takes what is in it, into their own purse. The key and the map too: they are their finder's alone, and only the one with the key opens the gate with it (once open, it is open for everyone). If the shadows catch that player, or they leave the game, the key or the map goes back into its chest for the others to find. The map of the next maze bought from the trader is the team's, and only one player pays for it.
- Caught. A player a shadow catches becomes a ghost: they watch a teammate's view until the level is over (Space switches to the next one). If the shadows catch everyone, the level starts over, with every purse as it came in.
- The exit. The level is done when everyone still in the maze is through. Everyone trades on their own, and the next level begins when all are ready. Nobody is waited for who is away (their game out of view, no connection), and once someone is ready the rest have 15 seconds (the trader counts it down). Whoever is not ready by then stays at the trader while the others play, and goes into the maze, at the entrance, when they press Ready.
- The world does not wait. Playing together nothing pauses: not the menu, not the trader, not a lock being picked. A player whose game is out of view (another tab, a window put away) stays where they stand, marked away, and the shadows can still catch them there.
If a connection drops, the game joins again by itself, and a page that reloads comes back as the same player with its purse. The leader can end the run from the menu, sending everyone back to the lobby.
The server file can be copied to another Mac of the same kind (Apple silicon; --universal below makes one for Intel Macs too) and started there the same way. macOS asks to confirm a program from elsewhere the first time: right-click it and pick Open.
Server options: --port N (7777, or the next free one), --no-open (do not open the browser), --dir D (serve the game from folder D instead of the copy inside it, for working on the game: npm run server). The game is baked in when the server is built, so build it again after changing the game. node server/build.js --universal makes one file for both Apple silicon and Intel Macs.
Performance
The game is built to stay fast on modest hardware:
- the low-resolution pixel look is the main saving: the scene renders at a fraction of screen resolution
- Auto quality lowers the resolution further whenever the frame rate drops
- masonry and floors are compact indexed meshes: a brick face is one quad of small vertices
- bricks, props and decor are drawn with instancing; the scattered ones are merged into one mesh per stretch of maze
- all flames and halos are drawn in two batches, the light shafts in four, and the forests round the outdoor mazes in one
- a cheap grid ray test skips parts of the maze hidden behind walls, and the world behind the grates unless a grate is in view
- the sky is drawn last, so it is shaded only where nothing covers it
- what costs a slow GPU most and the eye least is left out: no crumbs round fallen bricks, half the grass and ivy, far hills on a 36 m grid (trees and towns stand on it as drawn), clouds from three layers of noise
Settings has draw distance, pixel size, FOV, an FPS limit and a toggle for post effects.
Under the hood
Plain JavaScript and three.js. There's no bundler, every file is a plain <script>, and they load in order:
| File | What it does |
|---|
js/00-util.js | random numbers, Perlin noise, settings |
js/01-textures.js | procedural canvas textures |
js/02-themes.js | themes and weather |
js/03-maze.js | maze generation, collision grid |
js/04-walls.js | voxel walls, vaults, niches, burial alcoves |
js/05-world.js | floors, sky, terrain, lights, portals |
js/05b-props.js | props, remains, glowing crystals and mushrooms |
js/05c-audio.js | synthesised sound and music |
js/05d-gameplay.js | chests, key and gate, lockpicking, torch and Candlelight, shadows |
js/05e-creatures.js | pixel-art kit for the HUD icons and the tree sprites |
js/05k-mobs.js | low-poly mobs: the lantern wraith, the cave spider, the lost soul and the watcher |
js/05m-hero.js | the players' frog (the model, built once, a skeleton for each player, and its walk and run), the cloak in each player's colour, the light they carry |
js/05n-net.js | playing together: joining, the lobby, the others, the shadows' snapshots, the caught ones' view |
js/05f-ambience.js | banners, roots, light shafts, ground fog, rubble |
js/05g-hud.js | inventory panel |
js/05h-skyline.js | castles, towers, temples and villages on the horizon |
js/05i-beyond.js | the world behind the grates |
js/05l-decor.js | theme details: doorways, painted walls, sand, cistern water, wisps |
js/05j-trader.js | the trader between levels |
js/06-game.js | renderer, movement, culling, menu, loading screen, main loop |
js/07-post.js | bloom, colour grading, vignette |
|
Tests
The tests drive the real game in headless Chromium through Playwright:
npm install
npm test # walk-through, game mechanics, stress test over all themes, memory leaks, playing together
npm run test-server # the co-op server's rules (cargo test)
npm run test-live # the real server with three browsers joining it
npm run shot dungeon # screenshots of a level
npm run thumbs # the map pictures in the menu (img/maps)