A lightweight, browser-based grid editor for placing and arranging objects on an infinite canvas. Think of it as a minimal tile/floor-plan tool — no backend, no build step, just open index.html.
Note: mobile is untested!
- Infinite pan & zoom — mouse-scroll to zoom, drag the background to pan.
- Grid snapping — objects snap to grid cells when dropped.
- Placeables — define reusable objects (colored rectangle or image).
- Multi-select — click an object to select it; shift-click to add/remove from the selection; shift-drag on the background to rubber-band select a region.
- Group move — drag any selected object to move the entire selection together.
- Multiple layouts — page is not limited to just one layout (sets of placeables).
Layouts are defined in JSON files and linked from index.html:
<link rel="grid-config" href="default.json" title="Default">
<link rel="grid-config" href="dungeon.json" title="Dungeon">If only one config is linked the picker is hidden and the layout loads automatically.
Each placeable requires:
| Field | Type | Description |
|---|---|---|
width |
number | Width in grid cells |
height |
number | Height in grid cells |
image |
string (optional) | Path to an image file |
fill |
string (optional) | CSS colour used when no image is set |
Each object instance requires:
| Field | Type | Description |
|---|---|---|
x |
number | Column (grid units) |
y |
number | Row (grid units) |
template |
string | Key from placeables |
| Library | Role |
|---|---|
| D3 v7 | SVG rendering, zoom/pan, drag-and-drop |
| Alpine.js v3 | Lightweight UI state (config picker, toolbar) |
- default assets from www.kenney.nl
kenney_tiny-townassets from www.kenney.nl
Things that are on the roadmap:
- rotation of placeables
- line drawing
- text placement
- saving and loading instances
- loading layouts
- clone selection
- delete selection
{ "settings": { "gridSize": 25, // pixels per grid cell "gridColor": "#a4a4a4", "dragStroke": "black", "dragStrokeWidth": 2, "selectionStroke": "#4a90f4", "selectionStrokeWidth": 2, "selectionFill": "rgba(74, 144, 244, 0.15)" }, // Initial viewport in grid units. Omit to auto-fit placed objects. "startView": { "x": -1, "y": -1, "width": 20, "height": 20 }, // Named object templates. "placeables": { "crate": { "width": 1, "height": 1, "image": "crate.png" }, "wall": { "width": 3, "height": 1, "fill": "#888" } }, // Instances placed on the map. "objects": [ { "x": 2, "y": 5, "template": "crate" }, { "x": 5, "y": 5, "template": "wall" } ] }