# TraceTile recipes

A recipe is a stained glass, laser, CNC or sticker design written as plain
JSON: what to put where, in which color, without working out any geometry
(or a Picture of pages played, a reel or a storybook: Pictures and pages,
below).
TraceTile turns it into real pieces with shared lines, at real size, and
adds the lead a glazier would so every piece can be cut. Recipes are how a
person or an AI assistant hands a design to TraceTile:

- **Link** to it: `https://tracetile.app/app/#recipe=` followed by the JSON,
  compact and URL-encoded (`encodeURIComponent`), or base64url. The part
  after `#` never leaves the browser, so the design isn't sent anywhere.
- **Open** a `.json` or `.tracetile` file holding it (File ▸ Open, or drop it on the window).
- **Paste** the JSON into the editor (⌘V / Ctrl+V on the canvas).

## Why not draw it, or write the file directly?

An image generator draws a picture, not a pattern: lines stop in mid-air,
pieces wrap around points, slivers no one can cut. A `.tracetile` file is the
finished geometry: every seam shared by exact point numbers, which has to be
computed, and TraceTile writes it sealed, so only TraceTile opens it. A recipe is the part a designer decides (the subject, where it
sits, how big, which glass) and leaves the rest to the engine.

## A drawing

A recipe is a drawing, composed from motifs and shapes. (TraceTile has no
built-in templates for a recipe to name; a person's own templates live on
their device, in Open ▸ Templates.)

```json
{
  "name": "Cat & moon",
  "drawing": {
    "size": [8, 8],
    "outline": { "circle": [50, 50, 48] },
    "lines": [
      { "motif": "crescent-moon", "at": [62, 34], "size": 46, "rotate": 20 },
      { "motif": "cat-sitting", "at": [40, 60], "size": 52 },
      { "star": [80, 72, 7, 5], "fill": "lemon" }
    ],
    "background": ["navy", "cobalt", "purple"]
  }
}
```

## Coordinates

The drawing is a box 100 units wide and `100 × height / width` tall. (0, 0)
is the top left, y points down, (50, 50) is the middle of a square box.
`size` is the panel, `[width, height]`, in inches unless `"unit": "mm"`
(default `[8, 8]` in), within the sizes its method builds (below). 1 unit is
`width / 100` of the unit.

## Drawing keys

| Key | What |
|---|---|
| `size` | `[width, height]` in the unit |
| `unit` | `"in"` (default) or `"mm"` |
| `method` | How it's cut: `foil`, `lead`, `laser`, `cnc`, `blade` (below). Default: from the material, else glass in foil (under a 12 in diagonal) or lead |
| `material` | What it's cut from: `glass`, `acrylic-3`, `plywood-3`, `vinyl`… (below). Default: the method's |
| `level` | `"beginner"`, `"intermediate"` or `"advanced"`: holds it to that many pieces |
| `format` | The picture it's for: `"9:16"`, `"16:9"`, `"1:1"`, `"4:5"`: holds the panel to a shape that fills it |
| `sheet` | `[width, height]`: the biggest stock or machine bed you cut from, when not the usual |
| `border` | Stickers printed then cut (`sticker-vinyl`, `sticker-paper`): the white border round each, in the unit; `0` or `"none"` for none. Default ⅛ in (3 mm), as the editor's stickers start |
| `outline` | The outer edge of the glass: a closed shape. Default: the whole box |
| `lines` | Shapes and motifs, in order; later fills paint over earlier ones |
| `background` | Glass for every piece no fill colors (below) |
| `paint` | `[{ "at": [x, y], "color" }, …]`: the region of the drawing at each point takes that glass (below) |
| `fixCuts` | `false` to skip the automatic lead (default on; leave it on) |

## How it's made, and the limits that holds it to

Say how the piece is made before drawing it: the method and the material
set how small a piece, how narrow a strip and how sharp a point can be cut,
and the panel sizes that make sense.

| Method | What | Line width | Panel, on a side | Biggest piece |
|---|---|---|---|---|
| `foil` | Copper foil: glass wrapped in copper tape, soldered (Tiffany) | 0.04 in | 2–36 in (over 24 in, reinforced) | the glass sheet |
| `lead` | Lead came: glass held in H-shaped lead | 1/16 in | 4–48 in (over 24 in, a bar every 18–24 in) | the glass sheet |
| `laser` | A laser cutter: acrylic, wood, MDF, card, leather | 0.008 in | 1–48 in | a 20 × 12 in bed |
| `cnc` | A CNC router or waterjet: wood boards, acrylic, MDF, ACM, metal | 1/8 in | 4–60 in | a 4 × 8 ft table |
| `blade` | A craft cutter's blade: stickers, decals, vinyl, card, felt | 0.004 in | 1–24 in | a 12 × 24 in mat |
| `print` | Printed on a roll and slit to width: washi or masking tape, ribbon. Nothing is cut | 0.004 in | 0.25–16 in | one cell of the printer's repeat |

| Material | What | Cut with |
|---|---|---|
| `glass` | Glass, 3 mm (⅛ in): see-through cathedral art glass | `foil`, `lead` |
| `glass-opal`, `glass-wispy` | Opalescent glass; wispy and streaky glass, 3 mm | `foil`, `lead` |
| `glass-textured`, `glass-clear` | Textured (hammered, ripple, seedy); clear window glass, 3 mm | `foil`, `lead` |
| `glass-thin` | Thin glass, 2 mm, for lamps | `foil` |
| `acrylic-3`, `acrylic-6` | Acrylic, 3 or 6 mm | `laser`, `cnc` |
| `plywood-3`, `plywood-6` | Birch plywood, 3 or 6 mm | `laser`, `cnc` |
| `plywood-12` | Birch plywood board, 12 mm (½ in) | `cnc` |
| `basswood-3`, `bamboo-3` | Basswood or bamboo plywood, 3 mm | `laser`, `cnc` |
| `hardwood-3` | Walnut, maple or cherry, 3 mm | `laser`, `cnc` |
| `mdf-3`, `mdf-6` | MDF, 3 or 6 mm | `laser`, `cnc` |
| `acm-3` | Aluminum composite (ACM, Dibond), 3 mm: signs | `cnc` |
| `aluminum-3`, `steel-1`, `brass-1` | Sheet metal | `cnc` |
| `card`, `paper`, `chipboard-1` | Cardstock, 0.3 mm; paper, 0.1 mm; chipboard, 1 mm | `laser`, `blade` |
| `leather-2` | Leather, 2 mm | `laser` |
| `felt-3` | Wool felt, 3 mm | `laser`, `blade` |
| `vinyl`, `iron-on` | Adhesive or heat-transfer vinyl | `blade` |
| `sticker-vinyl`, `sticker-paper` | Printable sticker vinyl, sticker paper | `blade` |
| `washi-tape`, `masking-tape` | Washi or masking tape: the panel is one cell of its repeat, its height the tape's width | `print` |
| `ribbon-satin`, `ribbon-grosgrain` | Satin or grosgrain ribbon, printed one side | `print` |

Glass is scored and broken by hand, so its pieces are held to the lead's
rules: at least 6 line widths across, no point under 40°, and no inside
corners or deep inside curves (Fix cuts carries a line on through them).
Anything a beam or a blade cuts takes inside corners and curves, but its
pieces are held to the stock: a strip no narrower than the sheet is thick
(wood, MDF and ACM half again), a piece twice that across (ACM three
times), and points to 30° (25° for metal, paper, leather and vinyl).
Vinyl and sticker paper are weeded off their backing: no strip under
1/16 in, no piece under ⅛ in. Sticker paper and printable sticker vinyl
are printed, then cut: the colors are ink on one sheet, and the blade cuts
only round each sticker, the pieces that touch whatever their colors. Tape and ribbon aren't cut but printed, the
colors meeting edge to edge: nothing under 0.3 mm prints clearly, the
cell's two ends must meet (the next cell carries on from it), and a whole
number of cells fills the printer's repeat (350 mm unless the design says).

**Refused before anything is built**, every one at once, with its fix: a
material the method doesn't cut (glass on a laser cracks; vinyl on a laser
gives off chlorine; ACM's aluminum skins throw a laser's beam back), a
panel outside the method's sizes, a panel the wrong shape for its
`format`, and anything that isn't one of the names above.

**Not cut-ready once built**: more pieces than its `level` has (beginner
10–30, intermediate 30–60, advanced 60 and up), and a piece too big for
the stock or the machine's bed (give `sheet` for your own). They come back
as errors with the rest of the findings, so the picture can still be
looked at.

| Format | For | Panel width ÷ height | Start from |
|---|---|---|---|
| `9:16` | Vertical video (Shorts, Reels, TikTok) | 0.56–1 | 2:3, e.g. `[8, 12]` |
| `16:9` | Wide video | 1–1.78 | 3:2 |
| `1:1` | Square post | 0.75–1.33 | 1:1 |
| `4:5` | Portrait post | 0.6–1 | 4:5 |

## Shapes

Every shape in `lines` is a lead line. A closed shape with `"fill"` is also
colored: **a color always goes on a shape**, so there is no such thing as a
colored region without lead around it (the most common way drawings fail).

| Shape | Arguments |
|---|---|
| `circle` | `[cx, cy, r]` |
| `ellipse` | `[cx, cy, rx, ry, degrees?]` |
| `rect` | `[x, y, width, height]` |
| `poly` | `[[x, y], …]` closed, straight sides |
| `polyline` | `[[x, y], …]` open, straight |
| `line` | `[x0, y0, x1, y1]` |
| `curve` | `[[x, y], …]` closed and smooth; `[x, y, 1]` is a sharp corner |
| `path` | `[[x, y], …]` open and smooth; ends are corners |
| `arc` | `[cx, cy, r, fromDegrees, toDegrees]` (0° = right, clockwise) |
| `star` | `[cx, cy, r, points]`, cut into its points; `"tip"`: 40–90° (default 50) |
| `ray` | `[x, y, dx, dy]`: from (x, y) on past the rim |
| `motif` | `"id"` with `"at": [x, y]` (its middle), `"size"` (how wide its box is, in units; it fills most of that), `"rotate"` (degrees, clockwise; just a number), `"flip": true`, `"colors": { part: color }` |

On any shape: `"fill"` (closed shapes), `"mirror": cx` (adds the mirror image
across x = cx), `"rotate": [degrees, cx, cy]`, `"radial": [copies, cx, cy]`
(copies turned evenly around a point). On a star: `"fill2"` (alternate
points) and `"hubFill"` (the middle).

## Motifs

Pre-drawn, cut-ready shapes, drawn once by hand the way a glazier would. Use
them for anything organic: drawing an ear or a wing from coordinates is where
both people and AI go wrong. Each comes with lead lines running on from its
tips to the next line, so the background never wraps around it. When placed
small, motifs drop details that would be too small to cut. Keep them inside
the outline and apart from each other (a motif cut by the rim gets needle
corners where it meets it).

| Motif | Parts (default glass) | Notes |
|---|---|---|
| `crescent-moon` | moon (gold) | Opens to the right; rotate to tilt it |
| `heart` | left (red), right (ruby) | Split down the middle |
| `leaf` | left (leaf), right (green) | Points up and right, split by its vein |
| `flower` | petals (white), eye (yellow) | A daisy head, eight petals |
| `cat-sitting` | head, body (charcoal), tail (black) | Seen from behind, ears up |
| `butterfly` | upper (orange), lower (amber), body (black) | Antennae run on as lead |
| `dragonfly` | wings (aqua), body (cobalt), head (teal) | Wings cut into cells |
| `paw-print` | pad, toes (charcoal) | Each toe tied in by a line |
| `tree` | leaves (green), trunk (brown) | The trunk runs down to the next line |
| `tulip` | petals (red), middle (ruby), stem (green) | Three petals; the stem runs down to the next line |
| `fish` | body (orange), band (white), tail (amber) | Swims right, a fan tail; the band drops out placed small |
| `songbird` | head (charcoal), breast (orange), back (brown), tail (umber) | A robin facing right; `flip` turns it left |
| `mushroom` | cap (red), stem (white) | The stem runs down to the next line |
| `whale-tail` | left (charcoal), right (gray) | Flukes out of the water, split down the middle; the stock runs down to the next line |

Good sizes on an 8 in glass panel: 30–80 units across (about 2.5–6.5 in).

**Leave room.** Keep every motif and shape at least 3 units inside the
outline and 3 units from each other, or one wholly inside another (a ring,
a frame). Glass between two outlines closer than that is a sliver, and an
outline crossing another at a shallow angle leaves needle corners. A star
needs a radius of about 8 or more on an 8 in panel (more on bigger panels,
whose lead is wider). The notes (below) name anything placed too close,
crossing, past the outline or too small, with the place and what to change.
A shape may run past the outline where it meets it near square (a trunk
down past the bottom edge, a sun setting behind it): the rim cuts it like
any line. Keep motifs and stars inside.

## Glass colors

`"#RRGGBB"`, or a glass name: `clear ice sky blue cobalt navy teal aqua mint
leaf green forest olive lemon yellow gold amber orange red ruby pink rose
magenta lavender purple plum peach tan brown umber white gray charcoal black`.
The names the editor shows glass by are the same glass (`"Sky blue"`,
`"Forest green"`). Any CSS color name is its own color (`"indigo"` is
`#4B0082`), and a phrase ending in a color's name is that color
(`"deep purple"` is purple), each said in a note; where a glass and CSS
share a name (`black`), it's the glass.

## Background

| Value | Pieces get |
|---|---|
| `"sky"` | One glass for all |
| `["navy", "cobalt", "purple"]` | Those, with neighbors always different (default: `["sky", "ice"]`) |
| `{ "colors": […], "by": "angle", "center": [x, y], "count": 12 }` | Wedges around a point |
| `{ "colors": […], "by": "ring", "center": [x, y], "width": 10 }` | Rings |
| `{ "colors": […], "by": "band", "count": 5 }` / `"column"` | Horizontal bands / columns |

## Paint: building from circles and lines

A logo, a silhouette or a crest is usually built the way a designer
constructs one: a few circles, ellipses and straight lines laid over each
other, and the subject is some of the regions they make (a crescent is one
circle less another, not any one shape). `fill` can't say that, since it
colors a whole shape. `paint` colors a region: give a point inside it, in the
drawing's units, as a tap with the editor's Fill tool would. A region Fix cuts
cuts in two stays one color.

```json
{ "name": "Crescent from two circles", "drawing": { "size": [8, 8], "outline": { "circle": [50, 50, 48] },
  "lines": [ { "circle": [40, 50, 30] }, { "circle": [58, 37, 26] } ],
  "background": ["navy", "cobalt"],
  "paint": [ { "at": [20, 60], "color": "gold" }, { "at": [50, 42], "color": "navy" } ] } }
```

Shapes laid over each other are fine here; where two outlines cross at
under 40° the glass between them comes to a needle, and the notes say where.

## What happens on its own, and why

Fix cuts adds the lead a glazier would, so a recipe comes out cuttable:

- **A shape floating inside another is tied in** with lines from its top and
  bottom: glass can't be cut with a hole in it.
- **A line runs on from every tip** poking into another piece: a score breaks
  straight on, so it can't turn into a notch.
- **A piece wrapped around a bump is split** from the bump outward: a deep
  inside curve won't break out of the sheet.
- **Where the glass is narrow, the line goes the short way across**: an arm
  is tied off where it joins a trunk, from its armpit to its underside, and
  glass that necks down between two wider parts gets a line across the neck.
- **Lines drawn a hair apart become one**: a strip that thin can't be cut.
- **Lines that touch are pulled apart and tied**: a circle touching the
  outline or another circle is drawn a little smaller, so the glass beside
  it ends square instead of tapering to nothing.
- **A line meeting another at a shallow angle (under 30°) is bent** over
  its last stretch, so the wedge between them is wide enough to cut.

It can't make a narrow point blunt or a tiny piece bigger; those are the
design's. So:

- **Points**: give a point a base at least ¾ as wide as it is long (40°+).
  Stars do this for you.
- **Pieces**: at least 3 × 3 units on an 8 in foil panel; details from a few medium pieces, not many small ones.
- **Crossings**: lines that meet at a shallow angle leave a needle of glass.
  Cross near square; keep separate shapes apart rather than overlapping them.

## Mistakes come back as notes and errors

A recipe that builds may still come back with **notes**: things it ignored
(an unknown key) and things that will cost you cut-ready pieces, each with
its place and the fix, e.g. `drawing.lines[0] (the crescent-moon motif) runs
past the outline near (85, 7): move it in or make it smaller…`,
`drawing.lines[4] (the circle) overlaps drawing.lines[1] (the cat-sitting
motif)…`, `drawing.lines[2].star: its points are too small to cut… Make r at
least 8`. Act on every note first: they are the usual cause of findings.

A recipe that can't be built says where and how, all at once, e.g.
`drawing.lines[3]: unknown shape "circel". Shapes: circle [cx, cy, r]; …`
or `drawing.lines[1].fill: "blurple" isn't a color. Use "#RRGGBB" or a glass
color: …`. Keys it doesn't know are ignored with a note; `"color"` on a
shape is taken for its `"fill"`, and `"fill"` in `paint` for its `"color"`.
A link that can't be built opens nothing, and the editor says why beside
Projects, the whole of it in a field to copy. A built design that
still has findings is checked by the editor's Cut check (Properties ▸ Rules ▸ Cut check).

## Common mistakes

What an assistant's first try usually does, and what to write instead (from
assistants' own tries, 2026-10-21):

- **A scene stacked from filled shapes**: a sky `rect`, mountains as a filled
  `poly` on it, the ground as another `rect` over both. Every outline under
  another is still lead, so the glass comes out in slivers and needles where
  they overlap. Draw each horizon once, as a `polyline` or `line` from rim to
  rim, and color the regions between with `paint` (or a `background` by
  `band`).
- **A subject from overlapping shapes**: a cactus as a trunk `rect` and two
  arm `rect`s laid over it. Draw its outline once, one `curve` with `[x, y,
  1]` at its corners; lines inside it, if any, split it.
- **Lines added to please the check**: a `ray` from a subject up to the rim
  wherever a finding was. Each one runs through every piece on its way, a
  stripe across the design, and Fix cuts already adds the short lines inside
  corners and bumps need. Change the drawing at the finding (move a shape,
  open a gap, blunt a point); if a line is wanted, end it on the nearest
  line, a little past it.
- **Reading "is open: 27 pieces" as done**: the status line then says whether
  every piece can be cut ("Every piece can be cut." or "Won't cut as
  drawn: …"), and Properties ▸ Rules ▸ Cut check lists each spot with its
  place. Done is "Every piece can be cut."
- **Too little in it**: a scene in a handful of regions (a sun, three bands
  of hills) builds cut-ready and reads as a placeholder. Say a `level` and
  fill it (beginner 10–30 pieces, intermediate 30–60): the detail goes in
  the subject, not in lines across the background. On stickers the colors
  are ink and only round each sticker is cut, so detail inside one costs
  no cutting.

## More examples

A geometric panel, all from shapes:

```json
{ "name": "Rose window", "drawing": { "size": [12, 12], "outline": { "circle": [50, 50, 48] },
  "lines": [ { "circle": [50, 50, 12], "fill": "gold" }, { "circle": [50, 50, 32] },
             { "line": [50, 38, 50, 0], "radial": [12, 50, 50] },
             { "star": [50, 50, 10, 6], "fill": "ruby", "fill2": "red" } ],
  "background": { "colors": ["cobalt", "sky", "ruby"], "by": "ring", "center": [50, 50], "width": 16 } } }
```

A memorial with a heart and a paw, in a rectangle:

```json
{ "name": "Forever loved", "drawing": { "size": [10, 8], "outline": { "rect": [0, 0, 100, 80] },
  "lines": [ { "motif": "heart", "at": [32, 40], "size": 44, "colors": { "left": "rose", "right": "pink" } },
             { "motif": "paw-print", "at": [70, 42], "size": 38 } ],
  "background": ["lavender", "ice", "mint"] } }
```

A coaster in 3 mm plywood, laser cut:

```json
{ "name": "Leaf coaster", "drawing": { "size": [4, 4], "method": "laser", "material": "plywood-3",
  "outline": { "circle": [50, 50, 48] }, "lines": [ { "motif": "leaf", "at": [50, 50], "size": 92 } ],
  "background": ["tan", "brown"] } }
```

A vinyl sticker, cut on a craft cutter:

```json
{ "name": "Paw sticker", "drawing": { "size": [3, 3], "method": "blade", "material": "vinyl",
  "outline": { "circle": [50, 50, 48] }, "lines": [ { "motif": "paw-print", "at": [50, 52], "size": 70, "colors": { "toes": "rose" } } ],
  "background": "white" } }
```

## Pictures and pages: reels, explainers, storybooks

A recipe's `"picture"` is a Picture, not a drawing: shapes stacked one over
another, each whole, nothing cut (the editor's What you're making ▸
Picture). It may have many pages, played one after another as the editor's
Play plays them: each page a keyframe, what's on two pages carried from one
to the next. It opens in the editor as a project of those pages (a link, a
file or a paste, as any recipe), and Share ▸ GIF or video makes the video
there.

```json
{ "name": "Wave hello", "picture": { "size": [9, 16], "pages": [
    { "play": { "hold": 1 }, "caption": "Say *hello*", "items": [
      { "key": "sky", "rect": [0, 0, 100, 178], "fill": "navy" },
      { "key": "arm", "polyline": [[50, 90], [60, 100]], "stroke": { "weight": 9, "color": "white" } } ] },
    { "change": { "arm": { "polyline": [[50, 90], [62, 78]] } } } ] } }
```

The box is the recipes' own: 100 units across, as tall as the page is in
proportion (a 9 × 16 in page, a 9:16 reel, is 100 × 177.8). Every point is
where it is on that page.

| Picture key | What |
|---|---|
| `size` | `[width, height]` in the unit (default `[9, 16]`: a 9:16 reel) |
| `unit` | `"in"` (default) or `"mm"` |
| `format` | `"9:16"`, `"16:9"`, `"1:1"`, `"4:5"`: holds the page to that shape |
| `loop` | `false`: the last page doesn't go back into the first (default `true`, as Play loops) |
| `layers` | Its layers, bottom first, as Layers stacks them: `[{ "name", "depth" }, …]`, 8 at most. `"depth"`: how far it is as the camera moves, `far`, `back`, `middle`, `near` (as drawn, the default) or `front`; far ones slide less, front ones more. An item says its layer by name (`"layer": "sky"`); one that says none is on the bottom one. None given, one layer |
| `captions` | How every caption looks, as the editor's Captions set it: `"font"` (`sans`, `heavy`, `stencil`, `line`, `script`), `"size"` (`s`, `m`, `l`), `"back"` (what's behind the words: `none`, `outline`, `card`, `pill`), `"place"` (`top`, `middle`, `bottom`), `"color"`, `"backColor"`, `"lit"` (a lit word's); and `"lines"`: `[{ "from", "to", "text" }]`, words over a time range (seconds) rather than a page |
| `pages` | The pages, in order (below): 24 at most, as many as the editor opens of a project |

### A page

The first page is whole: `"items"`, bottom first. Every page after it is the
one before with changes (as Duplicate page and a few drags make it), or whole
again with `"items"` of its own.

| Page key | What |
|---|---|
| `items` | Everything on it, bottom first |
| `change` | `{ "key": { what changes } }`: new points (where it is now), a new look, a move, turn or size from where it was |
| `add` | Items laid on top, or each `"under"` or `"over"` a key |
| `remove` | Keys taken off (a group with all in it) |
| `from` | The page it starts from, 1 first (default the one before): `"from": 1` back to the first pose |
| `play` | How long it shows and how it goes into the next (below) |
| `caption` | Its line of words over the frame, after the camera (never carried or moved by it), a lit word in `*stars*`; the editor's Captions row, and an .srt beside the video |
| `name` | Its name in the Pages row |

### Keys: what's carried between pages

An item is on its picture's bottom layer unless it says another, `"layer":
"<name>"` (a group's members are on the group's). One added `"under"` or
`"over"` a key goes on that one's layer. In `"items"`, each layer's are in
their order, bottom first; a layer's always under the layers above it. A
thing stays on its layer: to move it to another, remove it and add it there.

Every item can have a `"key"`: its name in Layers, and what Match & Move
carries from one page to the next (a shape's key written as one is, letters,
digits, `-` and `_`, 24 at most, is its key in the file too). On both pages, it glides, turns, resizes
and changes color as it goes; a line on top with the same number of points
bends at its joints (a limb about its shoulder); a group goes as one. Only on
one page, it fades out or comes in. Keep a figure's limbs' points the same in
number on every page, and give everything that moves a key. A key names one
thing on a page; no key is a thing of its own on each page it's written on.
A shape or a group that holds a line's point on both pages rides that line
between them, turning as it turns (a plane in the hand, a head on the neck).
To let go, put the point outside it on the next page: still holding it there,
it rides on with the line (down an arm's swing) before it flies off.

### Items

| Item | What | With |
|---|---|---|
| `circle` | `[cx, cy, r]` | |
| `ellipse` | `[cx, cy, rx, ry, degrees?]` | |
| `rect` | `[x, y, width, height]` | `"round"`: its corners' radius |
| `poly` | `[[x, y], …]` closed, straight sides | `"bend"`: each side's bend (below) |
| `curve` | `[[x, y], …]` closed and smooth; `[x, y, 1]` a corner | |
| `star` | `[cx, cy, r, points]` | `"tip"`: its points' angle, 20–120° (default 50) |
| `polyline` | `[[x, y], …]` a line on top, straight; two points for a straight line | `"bend"` |
| `path` | `[[x, y], …]` a line on top, smooth; `[x, y, 1]` a corner | |
| `arc` | `[cx, cy, r, fromDegrees, toDegrees]` (0° right, clockwise) | |
| `text` | The words, in TraceTile's letters | `"at"`: `[x, y]` its baseline's middle (or start, end); `"size"`: the capitals' height (default 8); `"font"`: `sans` (lowercase too), `heavy`, `capitals`, `stencil`, `line`, `script` (`line` and `script` are single strokes, lines on top); `"align"`; `"spacing"` |
| `group` | `[items]`: one thing, carried as one (one level: no group in a group) | |
| `blend` | `[shapes]`: two or more filled shapes, the steps between them drawn (a glow, a smear, a fade of color) | `"steps"`: 1–100 (default 12) |
| `offset` | `{ "of": "<key>", "by": units }`: a shape's outline grown out (negative: in), a shape of its own (a halo, a border, a shadow); `"corners"`: `round` or `sharp` | |

`"bend"` is a number for each segment: how far its middle is off the
straight line, in units, positive to the left of the way it's drawn (a mouth
`[[37, 75], [43, 75]]` with `"bend": [-1.5]` smiles).

### How it looks

| Key | On | What |
|---|---|---|
| `fill` | a shape, text | A color; a gradient (below); or `"none"`: its outline alone, a line on top in its `line` |
| `line` | a shape | Its Line: its outline's ink, `{ "color", "weight" }` as a stroke (below); `"none"` |
| `stroke` | a line, a `line` or `script` text | `{ "color", "weight", "brush", … }` (below) |
| `opacity` | any | 0 (unseen) to 1 |
| `mode` | a shape | How its color meets what's under it: `normal`, `multiply`, `screen`, `overlay`, `darken`, `lighten`, `color-dodge`, `color-burn`, `hard-light`, `soft-light`, `difference`, `exclusion` |

| Stroke key | What |
|---|---|
| `color` | Default ink, `#1A1A1A` |
| `weight` | In points, 1/72 in (default 3; on a 9 in page, 8 is a bold limb) |
| `brush` | One of the eight: `pen`, `ink` (tapered), `nib`, `marker`, `dots`, `swell`, `dry`, `chalk`; or one written out as BRUSHES.md writes it, `{ "size", "coats": [ … ] }` |
| `cap`, `join` | `butt`, `round`, `square`; `miter`, `round`, `bevel` |
| `dash` | `[dash, gap]` in points |
| `profile` | Its width along it: `even`, `taper`, `fade`, `swell`, `nib` |
| `taper` | `[start, end]`: how much of it each end draws in to a point over, 0–0.5 |
| `start`, `end` | `arrow`, `dot` or `none` |
| `grain`, `opacity` | `dry` or `chalk`; 0–1 |

A gradient is `{ "linear": [x0, y0, x1, y1], "colors": ["#…", …] }` or
`{ "radial": [cx, cy, r], "colors": […] }`, with `"opacity": [1, 0]` (one for
each color: where it fades, what's under it shows) and `"at": [0, 0.4, 1]`
(where each color is, 0 its start to 1 its end).

### Moves

On an item as it's written, or in a change: each from where it is now, in
this order.

| Key | What |
|---|---|
| `distort` | Its box's four corners go to `[[x, y], …]`, clockwise from the top left (a perspective, a lean) |
| `warp` | A grid over its box, 3, 4 or 5 points a side, row by row from the top left: where each goes (9, 16 or 25 points; start from even steps across its box) |
| `scale` | Its size from now: `1.2`, or `[across, down]` for a squash or a stretch |
| `turn` | Degrees, clockwise |
| `about` | `[x, y]`: what it turns and sizes about (default its middle) |
| `move` | `[across, down]` in units |

In a change, a group's move goes first: what's written for something in it
is where it is on that page.

### Play

| Play key | What |
|---|---|
| `hold` | Seconds it shows, 0–30 (default 2); 0: a pose Match & Move goes on through without a stop, on a curve through the poses either side |
| `kind` | Its way into the next: `move` (Match & Move, the default), `morph`, `dissolve`, `none`, `slide`, `stack`, `flow`, `line-wipe`, `color-wipe`, `circle-wipe`, `chop`, `wave`, `spin`, `punch` |
| `dur` | Seconds that takes, a frame (1/30) to 5 (default 0.8) |
| `feel` | How what moves moves: `smooth`, `snap` (fast, a dead stop), `windup` (back a little first), `settle` (past a little, back) |
| `camera` | Over its time: `still`, `in`, `out`, `left`, `right` |
| `frame` | What its camera frames (the editor's Camera ▸ Frame…): `[x, y, width]` in units, its middle and its width, as tall as the page's shape is. Between two pages the camera goes from frame to frame; none, the whole page. A pan is a frame a page; a push-in, a frame narrower than the last |
| `paths` | Each shape's way into the next page bent (the editor's path knob): `{ "<key>": bend }`, how far its way's middle sits to the side, a share of the way's length, −2 to 2 (positive to the right of the way it goes). A shape's only: a line bends at its joints |
| `times` | A loop of pages: the pages from `back` pages before it (default 1) to it, played this many times in all, 1–20 (breathing, a walk cycle, a blink) |
| `back` | Where its loop starts: how many pages before it |

Times are kept to a thousandth of a second (a frame is 1/30); one written finer is said in a note.

### What it says

A picture that can't be read is refused, every problem at once with its
place: `pages[2].change.arm-r: there's no "arm-r" on page 2 (it has: …)`,
`pages[0].items[3].fill: "blurple" isn't a color`. Each page is then laid as
the editor lays shapes on a Picture, one over another; one it can't lay is
left off that page and said, by page and key, with what it nearly meets and
where: `Page 4, "head": left off the page: its edge comes too close to
another's ("glow") near (58, 55.9)`. A shape's edge running a hair along
another's, or nearly touching it, is what's refused: cross edges cleanly or
keep them a unit or two apart. A shape may lie up to a page past the page
each way, whole, waiting to be carried in (a pan's sky, a bird flying in);
the page clips it where it's shown. One reaching further is refused.

A stick-figure explainer, 15 s at 9:16:

```json
{ "name": "The 2-minute rule", "picture": { "size": [9, 16], "format": "9:16", "captions": { "place": "bottom", "back": "card" }, "pages": [
    { "play": { "hold": 2, "dur": 0.6, "feel": "settle", "camera": "in" }, "caption": "Can't start a *big* habit?",
      "items": [
      { "key": "wall", "rect": [0, 0, 100, 178], "fill": "#F4EFE6" },
      { "key": "glow", "circle": [40, 100, 36], "fill": { "radial": [40, 100, 36], "colors": ["#FFD08A", "#FFD08A"], "opacity": [0.8, 0] } },
      { "key": "floor", "rect": [0, 140, 100, 38], "fill": "#E2D5BF" },
      { "key": "title", "text": "The 2-minute rule", "at": [50, 34], "size": 7, "fill": "#1A1A1A" },
      { "key": "kai", "group": [
        { "key": "body", "polyline": [[40, 82], [40, 112]], "stroke": { "weight": 8 } },
        { "key": "arm-l", "polyline": [[40, 88], [32, 100], [30, 112]], "stroke": { "weight": 7 } },
        { "key": "arm-r", "polyline": [[40, 88], [48, 100], [50, 112]], "stroke": { "weight": 7 } },
        { "key": "leg-l", "polyline": [[40, 112], [35, 126], [34, 140]], "stroke": { "weight": 8 } },
        { "key": "leg-r", "polyline": [[40, 112], [45, 126], [46, 140]], "stroke": { "weight": 8 } },
        { "key": "head", "circle": [40, 71, 11], "fill": "white", "line": { "weight": 5 } },
        { "key": "eye-l", "ellipse": [36.5, 70, 1.1, 1.6], "fill": "#1A1A1A" },
        { "key": "eye-r", "ellipse": [43.5, 70, 1.1, 1.6], "fill": "#1A1A1A" },
        { "key": "mouth", "polyline": [[37, 75.5], [43, 75.5]], "bend": [-1.5], "stroke": { "weight": 3, "brush": "ink" } } ] } ] },
    { "play": { "hold": 2, "dur": 0.6, "feel": "windup" }, "caption": "It feels *huge*.", "remove": ["title"],
      "change": {
      "arm-r": { "polyline": [[40, 88], [51, 82], [47, 68]] },
      "mouth": { "polyline": [[37, 76.5], [43, 76.5]], "bend": [1.5] } },
      "add": [
      { "key": "task", "rect": [58, 66, 34, 74], "round": 2, "fill": "#C8553D", "line": { "weight": 4 }, "under": "kai" } ] },
    { "play": { "hold": 2, "dur": 0.5, "feel": "snap" }, "caption": "Shrink it to *2 minutes*.",
      "change": {
      "task": { "scale": 0.3, "about": [75, 140] },
      "arm-r": { "polyline": [[40, 88], [52, 94], [64, 92]] },
      "mouth": { "polyline": [[37, 75.5], [43, 75.5]], "bend": [-1.5] } },
      "add": [
      { "key": "shrink", "path": [[78, 60], [88, 92], [82, 114]], "stroke": { "brush": "dots", "weight": 5, "end": "arrow", "color": "#8A6F55" } } ] },
    { "play": { "hold": 1.8, "dur": 0.6, "feel": "settle" }, "caption": "Just *start*.", "remove": ["shrink"],
      "change": {
      "kai": { "move": [18, 0] },
      "arm-r": { "polyline": [[58, 88], [66, 100], [72, 118]] },
      "leg-l": { "polyline": [[58, 112], [52, 126], [50, 140]] },
      "task": { "distort": [[68, 121], [83, 121], [84, 140], [67, 140]] } } },
    { "play": { "hold": 2, "dur": 0.6, "feel": "settle" }, "caption": "Tomorrow, *2 more*.",
      "change": {
      "arm-l": { "polyline": [[58, 88], [49, 74], [51, 55]] },
      "arm-r": { "polyline": [[58, 88], [67, 74], [65, 55]] },
      "leg-l": { "polyline": [[58, 112], [53, 126], [52, 140]] },
      "task": { "rect": [51, 42, 14, 14], "round": 1, "turn": -6 },
      "mouth": { "polyline": [[55, 75], [61, 75]], "bend": [-3] } },
      "add": [
      { "key": "burst-glow", "offset": { "of": "burst", "by": 3 }, "fill": "#FFE7A3", "under": "task" },
      { "key": "burst", "star": [58, 66, 30, 8], "tip": 60, "fill": "#FFC93C", "under": "task" } ] },
    { "play": { "hold": 2.6, "dur": 0.6, "camera": "out" }, "caption": "*Start* small.", "remove": ["task", "burst", "burst-glow"],
      "change": {
      "arm-l": { "polyline": [[58, 88], [50, 100], [48, 112]] },
      "arm-r": { "polyline": [[58, 88], [66, 100], [68, 112]] } },
      "add": [
      { "key": "end", "text": "Start small.", "at": [50, 34], "size": 9, "fill": "#C8553D" } ] } ] } }
```

A storybook scene, 15 s at 9:16:

```json
{ "name": "Goodnight, little house", "picture": { "size": [9, 16], "format": "9:16", "captions": { "place": "top", "back": "pill" }, "pages": [
    { "play": { "hold": 2.6, "dur": 0.9, "camera": "in" }, "caption": "On the hill stood a *little house*.",
      "items": [
      { "key": "sky", "rect": [0, 0, 100, 178], "fill": { "linear": [50, 0, 50, 130], "colors": ["#8EC5E8", "#DDF0F7"] } },
      { "key": "sun-glow", "offset": { "of": "sun", "by": 5 }, "fill": "#FFF1BF", "opacity": 0.6 },
      { "key": "sun", "circle": [72, 60, 9], "fill": "#FFD45C" },
      { "key": "hill-back", "curve": [[-6, 118, 1], [24, 108], [58, 114], [106, 104, 1], [106, 184, 1], [-6, 184, 1]], "fill": "#8DBF6E" },
      { "key": "tree", "group": [
        { "key": "trunk", "rect": [72, 116, 3, 26], "fill": "#6B4A2E" },
        { "key": "crown", "curve": [[64, 108], [73.5, 96], [83, 108], [82, 118], [73.5, 122], [65, 118]], "fill": "#3C7A3E" } ] },
      { "key": "hill", "curve": [[-6, 148, 1], [30, 141], [66, 139], [106, 132, 1], [106, 184, 1], [-6, 184, 1]], "fill": "#5E9A48" },
      { "key": "house", "group": [
        { "key": "wall", "rect": [20, 116, 26, 20], "fill": "#F2E3C6", "line": { "color": "#5A3E2B", "weight": 3 } },
        { "key": "roof", "poly": [[16, 117], [33, 101], [50, 117]], "fill": "#B5523B", "line": { "color": "#5A3E2B", "weight": 3 } },
        { "key": "door", "rect": [26, 123, 6, 15], "round": 3, "fill": "#7A4E2D" },
        { "key": "window", "rect": [36, 122, 6, 6], "fill": "#F7F3E9", "line": { "color": "#5A3E2B", "weight": 2.5 } } ] },
      { "key": "title", "text": "Goodnight, little house", "font": "script", "at": [50, 158], "size": 7, "stroke": { "color": "#2B2D42", "weight": 3, "brush": "ink" } } ] },
    { "play": { "hold": 2.2, "dur": 0.9, "feel": "settle" }, "caption": "Each evening the *sun* went down,", "remove": ["title"],
      "change": {
      "sky": { "fill": { "linear": [50, 0, 50, 130], "colors": ["#F08A5D", "#FBD9A6"] } },
      "sun": { "move": [-8, 62], "fill": "#FF9F43" },
      "sun-glow": { "move": [-8, 62], "fill": "#FFD3A1" },
      "crown": { "warp": [[67, 96], [76.5, 96], [86, 96], [65.5, 109], [75, 109], [84.5, 109], [64, 122], [73.5, 122], [83, 122]] } } },
    { "play": { "hold": 2.2, "kind": "dissolve", "dur": 1 }, "caption": "and the house lit its *window*.", "remove": ["sun", "sun-glow"],
      "change": {
      "sky": { "fill": { "linear": [50, 0, 50, 130], "colors": ["#1D2547", "#3B4A7A"] } },
      "hill-back": { "fill": "#2F4A3A" },
      "hill": { "fill": "#22382B" },
      "crown": { "curve": [[64, 108], [73.5, 96], [83, 108], [82, 118], [73.5, 122], [65, 118]], "fill": "#1E3A28" },
      "wall": { "fill": "#8C7E6A" },
      "roof": { "fill": "#6E3326" },
      "window": { "fill": "#FFD45C" } },
      "add": [
      { "key": "moon-halo", "blend": [{ "circle": [70, 56, 16], "fill": "#3B4A7A" }, { "circle": [70, 56, 9], "fill": "#C9D2F2" }], "steps": 8, "under": "hill-back" },
      { "key": "moon", "circle": [70, 56, 8], "fill": "#FFF6D5", "under": "hill-back" } ] },
    { "play": { "hold": 2.2, "dur": 0.8, "camera": "out" }, "caption": "while the *stars* came out, one by one.",
      "add": [
      { "key": "star-1", "star": [16, 52, 3, 5], "fill": "#FFF6D5", "under": "hill-back" },
      { "key": "star-2", "star": [36, 44, 2.2, 5], "fill": "#FFF6D5", "under": "hill-back" },
      { "key": "star-3", "star": [48, 64, 2.6, 5], "fill": "#FFF6D5", "under": "hill-back" },
      { "key": "star-4", "star": [88, 66, 2.2, 5], "fill": "#FFF6D5", "under": "hill-back" },
      { "key": "star-5", "star": [12, 76, 2.4, 5], "fill": "#FFF6D5", "under": "hill-back" } ] },
    { "play": { "hold": 2.8, "dur": 0.8 }, "caption": "*Goodnight*, little house.",
      "change": {
      "star-1": { "scale": 1.4 },
      "star-3": { "scale": 1.3 },
      "moon": { "move": [-2, -2] },
      "moon-halo": { "move": [-2, -2] } },
      "add": [
      { "key": "end", "text": "Goodnight, little house", "font": "script", "at": [50, 158], "size": 7, "stroke": { "color": "#F7E7B4", "weight": 3, "brush": "ink" } } ] } ] } }
```

Over the hills, 16:9: three layers, the sky far and the clouds back, so they
slide less as the camera follows the plane; its way to the second page bows
up:

```json
{ "name": "Over the hills", "picture": { "size": [16, 9], "format": "16:9",
  "layers": [ { "name": "sky", "depth": "far" }, { "name": "clouds", "depth": "back" }, { "name": "land" } ],
  "pages": [
    { "play": { "hold": 0.8, "dur": 1.4, "paths": { "plane": -0.25 } },
      "items": [
        { "key": "sky", "layer": "sky", "rect": [-60, -10, 260, 80], "fill": { "linear": [50, -10, 50, 50], "colors": ["#3E6FB0", "#F6C28B"] } },
        { "key": "sun", "layer": "sky", "circle": [120, 16, 6], "fill": "#FFD45C" },
        { "key": "cloud-1", "layer": "clouds", "ellipse": [36, 14, 10, 3.5], "fill": "white", "opacity": 0.9 },
        { "key": "cloud-2", "layer": "clouds", "ellipse": [112, 10, 12, 4], "fill": "white", "opacity": 0.9 },
        { "key": "hills", "layer": "land", "curve": [[-60, 46, 1], [0, 40], [60, 45], [120, 38], [190, 43, 1], [190, 70, 1], [-60, 70, 1]], "fill": "#4F8A3C" },
        { "key": "plane", "layer": "land", "poly": [[16, 29], [27, 32], [16, 35], [19, 32]], "fill": "white", "line": { "weight": 1.5, "color": "#2B2D42" } } ] },
    { "play": { "hold": 0.4, "dur": 1.2, "frame": [85, 27, 80] },
      "change": { "plane": { "move": [62, -8], "turn": -8 } } },
    { "play": { "hold": 1.2, "frame": [140, 25, 64] },
      "change": { "plane": { "move": [56, 6], "turn": 14 } } } ] } }
```

## Handing it over

What to give the person, and where its cut files come from: https://tracetile.app/llms.txt
