The sprites in To Arms, Squire!, and on this site, live in the code as text: a grid of characters each. That makes art easy to version and re-colour, and miserable to draw by typing. So I built an editor that paints the grid and writes it back into the source for me.
.....o...... ....oHo..... ....oHo..... ....oHo..... ....oHo..... ....oHo..... ...ooHoo.... ..oAAAAAo... ....oBo..... ....oBo..... ...oBBBo.... ....ooo.....
ooutlineAmetalBwoodHshine.clear
The loop
Pick a library (the game's icons, its characters, the tarot art, or the site's own sprites), load a sprite or start a blank one, and paint. The game's three libraries share one palette and the site has its own, and each sprite points the letters A to H at any colour in it. The canvas takes any size from 3×3 up to 64×64, except the game's icons, which are locked at 12×12 because the interface is laid out for them.
When it looks right, Save writes it into the library's file directly, or Copy code gives you the entry to paste by hand. The dev server notices the file change and hot-reloads it, so for most sprites the change is on the page a moment later: in the editor's own list, and anywhere the game draws it.
Saving into source, safely
A button that edits source files is a button that can break the build, so the save route is careful:
- It only exists in development. Anywhere else it answers 404, and the Save button isn't rendered at all.
- The client never names a file. It sends a library id, and the server looks the path up in its own registry.
- Names are plain identifiers. Lowercase letters, digits and underscores only, which is also what stops a crafted name from injecting code.
- The grid is checked row by row: a rectangle, 3 to 64 on a side, only the characters the format allows, and 12×12 for the icons.
- Every letter needs a colour from that library's palette, or the save is refused with the reason.
Then the route finds the two blocks it owns in the file, one for grids and one for colour roles, upserts the entry by its exact name and keeps both blocks alphabetical. If the file doesn't look the way it expects, it refuses rather than guesses.
Small things that make it pleasant
- Undo works in strokes, not pixels. One drag is one undo step, and repainting a pixel its own colour records nothing.
- Size fields apply when you're done typing, so the "2" on the way to "24" never crops your canvas.
- Shift moves the whole sprite a pixel at a time, wrapping at the edges, and flip mirrors it either way.
- Previews show the sprite at small and large whole-number scales, in grayscale to check it reads by lightness and not just by hue, and for the game's icons, in the muted palette the world map uses.
What's next
A mirror mode for symmetric sprites, keyboard shortcuts for the tools, and importing a PNG by snapping each pixel to the nearest palette colour.