Skip to content
All builds

EaselImage Gen Studio

What it does for you

Easel: a prompt-to-vector image studio with a seeded local engine, optional Claude SVG mode behind a tested sanitizer, and a full gallery.

Easel screenshot

Screenshots

Easel screenshot 1
Easel screenshot 2

In more detail

Easel turns a text prompt into a vector picture you can vary, remix, save, compare, and export as SVG or PNG. Claude's API does not return pixels, so I wanted a studio that is honest about that and still feels like a real image tool: fast variations, repeatable seeds, a gallery, and safe handling of whatever the model sends back.

Honest limits

Claude mode was only tested against a mocked fetch. The live API was never called, so the system prompt has not been tuned against real model output. The local engine is procedural SVG art, not a pixel model.

How I built it

This was on the Claude Code list on the tandem day with Codex. I directed the honest framing, the two-engine design, the sanitizer rules, and the "never call the API during the build" rule. Claude Code wrote the pure core first (seeded random numbers, prompt lexicon, scene builder, six style renderers, sanitizer, gallery logic), then the 313-assertion test suite, then the server and UI.

There are two engines feeding one gallery. The local engine is a seeded SVG generator with six styles, from flat landscape to risograph grain to paper cutout, and the same prompt plus seed gives a byte-identical file. Claude mode asks the model for one SVG document and runs it through an allowlist sanitizer before showing it.

When the app was done, Claude drove it through the Chrome DevTools Protocol from a throwaway script: generate, keep, favorite, vary, remix, search, compare, PNG export, gallery persistence, theme toggle, and Claude mode against a mocked fetch that returned a hostile SVG. That run found a real bug. The Generate button stayed disabled in Claude mode after typing, because typing did not re-render.

The API connection dropped long responses that day, so every file went out in chunks under about 4 KB.

What I learned

  • Determinism needs separate random streams. Palette, scene, and style each get their own seeded generator, so a palette remix keeps the exact geometry and a new style does not reshuffle the shapes.
  • A sanitizer is only as good as its parser. Regex stripping would miss <svg:script>, entity tricks, and CSS escapes. Parsing to a tree, keeping only allowlisted pieces, and re-serializing makes those cases boring. It is tested against 26 hostile samples.
  • Showing the result as an image data URL adds a second wall, because SVG in an image context cannot run script.
  • A prompt lexicon collides with itself. "poster" mapped to the flat style and beat "grainy" for risograph, and "stars" as a negative word erased the whole night scene. Tests caught both, and each keyword rule now has one.
  • The shell collapsed double backslashes in heredocs and broke three regexes, so those lines had to be fixed with direct edits.

What carried forward

Same sprint kit: pure core in Node, serve.mjs, small modules because of the tiny-writes rule. The idea I would carry anywhere is treating model output as untrusted input: parse it, allowlist it, re-serialize it, and render it where script cannot run.

If I took it further

I would make the local engine richer with layered scenes per subject and a small grammar for spatial words like "left," "behind," and "above." Then I would run a live Claude pass with a real key to tune the prompt.

#078 Built with Claude Code September 23, 2026 313 automated checks

Want one like this?