# Easel

> Easel is a headless painting engine for AI agents: real brush strokes, not pixels. A brush carries a finite load of paint and runs out along a stroke; paint lands wet and mixes in a pigment model where blue and yellow make green rather than grey; the canvas has tooth, so dry brush is what happens when you run out of paint on rough linen. Python library, command line, and MCP server. Python 3.12+, numpy and Pillow only, MIT licence.

If you are a model that has just fetched this page and wants to know whether it can use
this: yes, if you can run Python or call an MCP server, and if you can look at an image
you have made. Start with the code below, then read `PAINTER.md`.

```bash
pip install easel-paint
```

```python
from easel import Session, blob, cell

s = Session(1024, 768, texture="linen", ground="toned_grey", seed=7)
s.palette["shadow"] = s.palette.mix("ultramarine", "burnt_umber", 0.4)

s.block_in(blob(cell("D5")), brush="bristle", color="shadow", density=0.8)
s.look(values=True)        # writes a PNG and returns the path -- then open it
s.stroke([(0.2, 0.6), (0.6, 0.55), (0.9, 0.62)], "bristle", "yellow_ochre")
s.export("painting.png")
```

What to know before reading further:

- The guide ships inside the package: `easel guide` prints its first page, which is
  the whole method in under a thousand words, and `easel guide --full`, `--painting`,
  `--recipes`, `--reference` and `--calibration` print the rest of it. Nothing below has
  to be fetched over the network if you have installed it.
- Coordinates are always normalised `0.0`–`1.0`, origin top-left. Raw pixel coordinates
  are never exposed, because they are what a language model is worst at.
- The engine is built around one habit: **look every five to fifteen strokes**. A stroke
  you did not look at was a guess. The looking tools return an image.
- Nothing here turns a prompt into a picture. There is no model inside Easel. You choose
  and make every mark, and the engine only decides what paint does.
- There are no layers and no free undo. You work in passes and paint over mistakes.
- If you are here to paint rather than to evaluate: read `PAINTER.md` first, and decide
  what you are painting before you open the worked examples below. Each of them names a
  subject, and a named subject chooses for you — six of six fresh sessions once painted
  a noun the guide had merely listed in passing. Once you have chosen, they are the best
  thing here.
- Brushes: `round_soft`, `round_hard`, `liner`, `flat`, `bristle`, `knife`, `smudge`.
  Grounds: `white`, `warm_white`, `toned_grey`, `toned_warm_grey`, `burnt_sienna`,
  `umber_wash`, `cool_grey`. Textures: `smooth`, `linen`, `rough`.
- Three interfaces over one engine: the Python API, an `easel` CLI whose state is a
  single `.easel` file, and an MCP server (`pip install "easel-paint[mcp]"`, then
  `easel-mcp`) whose looking tools hand back the PNG inline rather than a path to it.

## Start here

- [README](https://raw.githubusercontent.com/Gemberkoekje/EaselAPI/main/README.md): what the engine is, how to install it, the full verb list, the command line, the MCP server, and the design decisions that make output look painted rather than generated. ~3,400 words.
- [PAINTER.md](https://raw.githubusercontent.com/Gemberkoekje/EaselAPI/main/PAINTER.md): **the guide written for the agent doing the painting, and the one file here that is required.** It teaches the workflow — tone the ground, paint back to front, check values, refine, edges and highlights last — rather than listing functions, and it is the method only: the loop, the order of work, the five mistakes you will make anyway with the fix on the same row, nine warm-up exercises and a closing checklist. Every rule in it is stated once and linked from the other files. Held to a word budget by the test suite; ~6,000 words, and *the first hour* at the top is enough to start.
- [RECIPES.md](https://raw.githubusercontent.com/Gemberkoekje/EaselAPI/main/RECIPES.md): one situation at a time — a scene of straight edges, a plane, a form that turns, a mass built of planes, a glow, a volume of lit air, a graded field, a small irregular mark, a small container, a lost edge, a hollow thing, a repair, and two entries on composition — each as the calls in order, what it looks like when it goes wrong, and the number behind it. Collected out of ten painters' pass scripts and notes. ~5,600 words. Read one when you are about to lay that thing.
- [PAINTING.md](https://raw.githubusercontent.com/Gemberkoekje/EaselAPI/main/PAINTING.md): how the engine behaves — colour and mixing, wet paint and glazes, the brushes and the shape each tool leaves behind, shaped masses and what they cost, the planning verbs, the rest of the API — and, in its last chapter, copying a photograph. Read once, after the exercises; skip the last chapter unless you have a photograph. ~7,000 words.
- [REFERENCE.md](https://raw.githubusercontent.com/Gemberkoekje/EaselAPI/main/REFERENCE.md): every fact on one page — the units (and that is where the surprises are), the defaults, what each argument does, the shell, and the MCP server. For looking up while holding a brush rather than reading. ~3,600 words. Nothing in it is a rule about painting; the rules are all in the guide, and a fact looked up without its rule is how a painting comes out correct and dead.

## Worked examples

- [Painting a car wash: the notes](https://raw.githubusercontent.com/Gemberkoekje/EaselAPI/main/paintings/car_wash/NOTES.md): a full painting, written up afterwards — the plan, what each stage cost, what was rehearsed and thrown away, and what it got wrong. Read it to see how the engine is actually used, not to choose what to paint.
- [Its nineteen pass scripts](https://github.com/Gemberkoekje/EaselAPI/tree/main/paintings/car_wash): the painting itself, in order, as ordinary Python. Run against a fresh session they reproduce the PNG byte for byte.
- [Painting three pears: the notes](https://raw.githubusercontent.com/Gemberkoekje/EaselAPI/main/paintings/windowsill_pears/NOTES.md): a second painting under the same rules, and the one that started the request list the engine has been answering since.
- [Painting a lighthouse at dusk: the notes](https://raw.githubusercontent.com/Gemberkoekje/EaselAPI/main/paintings/lighthouse_dusk/NOTES.md): a third, in 184 strokes of a 300 budget with eighteen rehearsals and no stroke spent on repainting anything. Its [ten pass scripts](https://github.com/Gemberkoekje/EaselAPI/tree/main/paintings/lighthouse_dusk) rebuild the PNG byte for byte, and most of `RECIPES.md` came out of them.
- [examples/exercises.py](https://raw.githubusercontent.com/Gemberkoekje/EaselAPI/main/examples/exercises.py): abstract warm-ups straight from the guide — a value scale, pressure profiles, wet-into-wet — deliberately not pictures of anything.

## Evidence

- [PAINTINGS.md](https://raw.githubusercontent.com/Gemberkoekje/EaselAPI/main/PAINTINGS.md): every finished painting at full size, the rules they were made under, what they cost, and an honest reading of how good they are.

## Optional

- [CALIBRATION.md](https://raw.githubusercontent.com/Gemberkoekje/EaselAPI/main/CALIBRATION.md): the measurement behind every number the guide quotes — graphite survival, wetness decay, the value floor, load windows, the smudge and glaze curves — indexed by rule at the top, with what painters reported about their own sessions kept in a section of its own. You do not need these to paint.
- [LESSONS.md](https://raw.githubusercontent.com/Gemberkoekje/EaselAPI/main/LESSONS.md): what six measured painting runs and an adversarial review left behind. Read it before changing the engine or the guide, not before painting.
- [SUGGESTIONS.md](https://raw.githubusercontent.com/Gemberkoekje/EaselAPI/main/SUGGESTIONS.md): what thirteen painters asked for after using the guide — what was wrong, and what was done about it. Every item is done, including eight answered by measuring them and finding nothing to fix.
- [CHANGELOG.md](https://raw.githubusercontent.com/Gemberkoekje/EaselAPI/main/CHANGELOG.md): the same history cut by release — what changed in each version and which defaults moved, which is what a script that leaves an argument off will paint differently after an upgrade.
- [Source](https://github.com/Gemberkoekje/EaselAPI/tree/main/src/easel): the engine. `session.py` is the object you hold, `color.py` the Kubelka-Munk pigment mixing, `stroke.py` the paint load and tooth model, `mcp_server.py` the MCP tools.
