Building Dungeon Designer

How I built a browser-based 2D tile map editor from scratch — and what I've already made with it.

While watching a talk by John Romero, one line stuck with me: "Great tools help make great games. Spend as much time on tools as possible." That resonated. I was already thinking about building a tile-based game and kept running into the same friction: why isn't there a simple, no-nonsense tile map editor I can just open in a browser? The tools that existed were either too heavy, too opinionated, or required installing something I didn't want to maintain. So I built my own. What started as a side project became something I've used to build real games for me and my friends to play — and I'll have articles about those coming soon. For now, I want to walk through what Dungeon Designer is, why I built it the way I did, and how it gets from my machine to the web.

What Is Dungeon Designer?

Dungeon Designer is a browser-based 2D tile map editor built on the HTML5 Canvas API. It's project-oriented — each dungeon is its own self-contained project with its own settings, tilesets, and layer configuration. The editor supports up to five independent render layers, which lets you separate concerns like background terrain, foreground objects, and collision markers. Tile dimensions are configurable at project creation time: 16, 32, 48, or 64 pixels per tile. You import your own sprite sheets as tilesets and paint from them directly onto the canvas.

The app is live at dungeon-designer.marshall-labs.com and the source is available on GitHub. No login, no account — open it and start designing.

What Is It Used For?

Dungeon Designer sits in a specific place in a tile-based game development pipeline: it's the authoring tool for level maps. In practice, my workflow pairs it with Aseprite — I draw and export sprite sheets in Aseprite, then import those tilesets directly into Dungeon Designer. From there the workflow is straightforward — create a project, configure the map dimensions and tile size, import your tileset images, and start painting. Each layer is painted independently, so you can build up a scene in exactly the order your renderer expects to draw it. When the map is ready, you export it and load the result into your game engine.

The output is structured map data that a game engine can interpret directly: layer definitions, tile indices, and tileset references. I've used it to author levels for games I've built for me and my friends — those projects will get their own write-ups — and the format was designed to be straightforward to parse on the engine side. No magic, no proprietary binary blobs. If you don't have your own assets yet, itch.io has a large collection of free tilesets to get you started.

How It Was Built

The most deliberate choice in this project was to use vanilla JavaScript — no framework, no build pipeline, no bundler. When I was evaluating the stack, I kept coming back to the same observation: a canvas-based editor doesn't benefit from a virtual DOM. React and Vue are great at keeping complex state in sync with a declarative UI, but the editor's rendering surface is a <canvas> element that I'm managing imperatively anyway. Introducing a framework would have added toolchain weight without solving any real problem I had.

The result is an app with exactly one external runtime dependency: jszip, used for packaging the exported map data into a ZIP archive. Everything else is plain JavaScript, HTML, and CSS. There's no build step — changes are live on reload. That kept iteration fast and the project easy to reason about.

Dungeon Designer was originally built as a cross-platform desktop application using Electron. As the scope became clearer, I realized it didn't actually need any native OS access — no filesystem calls, no system APIs, no reason to run outside a browser security model. Electron was solving a problem I didn't have. So I rewrote it as a static web application. The Electron release pipeline is still in the repository for those who prefer a desktop build, but the web version is the primary deployment target.

The architecture is intentionally flat. landing.html and landing.js handle project selection and creation. Once a project is open, control passes to main.html and renderer.js. The renderer is the monolithic core of the application — canvas rendering, tool management, editor state, and export logic all live there. I made a conscious choice to keep it that way rather than prematurely splitting things into abstractions that would have made the code harder to follow. A single, well-organized file is often easier to work in than a dozen loosely-coupled modules with unclear ownership.

Two areas that required real attention during development were the flood fill tool and the tileset cursor. Flood fill on a canvas sounds simple until you actually implement it — a naive approach visits far too many pixels and bogs down immediately on anything but a trivially small map. Getting it to a point where it felt instant required careful work on the traversal logic. The tileset cursor had a similar story: rendering a highlight that tracks your mouse position across a sprite sheet without introducing noticeable lag meant being deliberate about when and how the canvas was being redrawn.

Dungeon Designer was originally hand-coded from scratch, and I used it in that form while building games. Coming back later to polish and extend it using Claude Code was a genuinely great experience — having an AI assistant that could hold the context of a large, single-file renderer and make targeted improvements without losing the thread was exactly the kind of productivity boost that makes tools like that worth using.

Deploying to Marshall Labs

Marshall Labs is my home lab. The public-facing part of the infrastructure runs on a Raspberry Pi that lives in its own isolated VLAN — public internet traffic reaches the Pi, but not the rest of the home network. I manage the Pi remotely over Tailscale rather than exposing an SSH port to the public interface. It's a simple setup, but it gives me real network isolation without a lot of overhead.

Written by Daniel Marshall

Published on April 17th 2026