SpriteForge — A Browser-Based Sprite & Level Editor
Building a zero-dependency, fully static sprite editor and level generator that runs entirely in the browser.
The Problem
Every 2D game project eventually hits the same wall: you need to create sprites and design levels, but you don't want to pull in a full editor suite like Tiled or Aseprite just to iterate quickly. SpriteForge is my answer — a lightweight, browser-based toolset that lives at demo.kmoussouni.dev/spriteforge.
What It Does
SpriteForge is a static web app (no backend, no build step) with two main tools:
Sprite Generator (sprite-generator.html)
Draw and export pixel art sprites directly in the browser. The canvas-based editor supports per-pixel editing, palette management, and PNG export. No uploads, no accounts — everything runs client-side.
Level Generator (level-generator.html)
Design tile-based levels visually and export them as JSON or code snippets ready to paste into a Phaser / canvas game. Supports multiple tile layers and collision masks.
A documentation page (doc.html) covers the keyboard shortcuts and export formats.
Architecture: Zero Dependencies
The entire toolset is vanilla JavaScript + HTML Canvas. No frameworks, no bundler, no node_modules. The reason is pragmatic: a static tool that loads in under 100ms and runs offline is more useful than a polished React app that takes 3 seconds to hydrate.
<!-- index.html — the whole thing -->
<canvas id="editor"></canvas>
<script src="editor.js"></script>
Deployment is trivial — drop the files anywhere. In production, it's served by Caddy as a static directory:
handle /spriteforge* {
uri strip_prefix /spriteforge
root * /srv/demos/spriteforge
file_server
}
Canvas API Patterns
The pixel editor relies on ImageData for direct pixel manipulation — reading and writing individual RGBA values is far faster than drawing colored rectangles for each pixel.
const imageData = ctx.getImageData(0, 0, width, height)
const index = (y * width + x) * 4
imageData.data[index] = r
imageData.data[index + 1] = g
imageData.data[index + 2] = b
imageData.data[index + 3] = 255
ctx.putImageData(imageData, 0, 0)
The zoom system scales the logical canvas (e.g. 16×16 pixels) to the display canvas via ctx.scale() + ctx.imageSmoothingEnabled = false — preserving crisp pixels at any zoom level.
Try It
The editor is live at demo.kmoussouni.dev/spriteforge — no install, no login.
Why Static?
Tools should be fast and self-contained. SpriteForge is used mid-development when you need to iterate quickly. A server round-trip for every export, or a 200KB framework boot, would break the flow. The static approach also means it works offline, can be self-hosted in 30 seconds, and has zero maintenance overhead.