Adrian . Matías Quezada

Image overlay editor

A D&D character sheet open in the editor

An editor for putting things on top of a picture. You drop an image or a PDF in as the background, then place boxes over it and drag them where they belong: boxes of text, boxes that are a circle or a rectangle, boxes that hold another image. What comes out is a YAML file.

I wrote it for D&D character sheets and that is what it runs — it has been holding the sheets of every campaign I DM since April 2026 — but nothing inside it knows what a character sheet is. A scanned form with boxes where the fields go is the same problem. So is a generator of custom cards: one file with the frame and the layout, one short file per card. So is putting a face on top of a photo.

The reason it is a YAML file and not a drawing is inheritance. A file can extend other files, one or several, and what it inherits is both the layout and the values. So the boxes and their positions are written once, on a base file; the repeated content — in D&D, classes, races, backgrounds — is written once each; and the final thing is a short file that lists what it extends and fills in a handful of values. Change where a box sits on the base and every file that extends it moves too.

Any value can be an expression in braces, evaluated with every other field of the file in scope: {10 + Perception} for passive perception, or a nest of ternaries that turns level into proficiency bonus. Fields are resolved in passes, so an expression can depend on a field that is itself an expression, and overriding one in a child gives you base to refer to what the parent said. That is the whole point of the thing — I stopped recomputing modifiers by hand at the table. The one part that does know about D&D is a mod filter, which prints a number as +3 or -1.

There is no build step and no framework: one HTML file, one CSS file, one JS file, and four libraries checked into the repo. Files are served by their path — /campaign/character is not a file, it is nginx noticing the YAML exists and handing you the editor, which reads the path back off the URL and loads it.