# Texel for agents

> Read this once, then make the skin. Protocol, spec reference and art guide in one file. More: https://texel-skins.vercel.app/llms.txt

---

# Skin Agent Protocol

> The loop an AI agent follows to design, test and ship a Minecraft skin with Texel. Version `texel/1`.

A skin is **data, not pixels**: a JSON *spec* made of a palette and an ordered list of drawing operations. The spec compiles deterministically to a 64×64 PNG. Because the source is structured, an agent can reason about it, diff it, patch one layer at a time, and verify every step.

## The loop

```
BRIEF → READ → DRAFT → RENDER → REVIEW → PATCH ──┐
                          ▲                      │
                          └──────────────────────┘
                                  … → SHIP
```

### 1. Brief

Write down what you are making before touching pixels. Put it in the spec's `description`: reviewers (you, later) compare the render against it.

> "A desert ranger: tan skin, sun-bleached cloak with hood on the hat layer, leather belt with pouches, dusty boots. Classic arms."

Requests can arrive in any language. Answer the person in their language and write `description` in it too (the studio shows it to them); keep palette keys and layer `id`s in plain ASCII English. Don't open with a round of questions: make tasteful choices for anything unspecified, state them in one line, and let the person steer while they watch (see *Live session*).

### 2. Read

| Resource | Why |
| --- | --- |
| [/agent.md](https://texel-skins.vercel.app/agent.md) | This protocol, the spec reference and the art guide in **one file**. Fetch it instead of the three pages separately. |
| [/docs/spec.md](https://texel-skins.vercel.app/docs/spec.md) | The format: selectors, coordinates, every op. **Required.** |
| [/docs/art-guide.md](https://texel-skins.vercel.app/docs/art-guide.md) | Where eyes go, how to shade, what makes skins look good. |
| [/schema/skinspec.v1.json](https://texel-skins.vercel.app/schema/skinspec.v1.json) | JSON Schema for validation / structured output. |
| [/examples/index.json](https://texel-skins.vercel.app/examples/index.json) | Complete, working specs to learn from or fork. |
| [/docs/api.md](https://texel-skins.vercel.app/docs/api.md) | How to render: browser, URL, WebMCP, or CLI. |

### 3. Draft

1. **Palette first.** For each material (skin, hair, shirt, pants, shoes, metal…) define a base color plus derived tones: `"shirtDark": "shirt:-10"`. 2–4 tones per material.
2. **Broad → fine.** Start with `fill` on `all` (so no base pixel is transparent), then fill whole parts, then bands (`rect` with only `y`/`h`), then shading and texture on those broad areas (`gradient`, `shade`, `noise`), then small details (`pixels`, `points`, `line`). Texture goes *before* details: `noise` and `shade` change every pixel in their area, so running them last smears eyes, collars and buttons. Later layers overwrite earlier ones, so don't fill an area you repaint completely afterwards; the review flags those layers as `overwritten-layer`.
3. **Paint one side, mirror the other.** Design `rightArm`/`rightLeg`, then `mirror` them. Add asymmetric details *after* the mirror.
4. **Use the overlay** (`@overlay`) for things that stick out: hair tufts, hoods, helmets, jackets, backpacks.
5. **Give layers `id`s** for anything you might revisit (`"id": "eyes"`), so patches are surgical.

### 4. Render

Pick whichever interface your runtime has. They all run the same compiler:

- **Code execution:** `curl -O https://texel-skins.vercel.app/texel.mjs && node texel.mjs build skin.json -o skin.png --sheet sheet.png`.
- **MCP:** `texel_render` returns the review and the sheet image.
- **Browser agent:** open `/studio/`, then call `window.texel.setSpec(spec)` (or the WebMCP tool `texel_set_spec`).
- **URL only:** open `/studio/?view=inspect#spec=<encodeURIComponent(JSON)>` and take a screenshot.

#### Live session

When a person is waiting on the skin, let them watch it being made instead of seeing only the end result. Start the session **before your first draft** and give them the URL:

- **Code execution:** run `node texel.mjs live skin.json --open` in the background. It prints a studio URL (`/studio/?live=<port>`); every time you save `skin.json` their tab updates. Keep using `build` for your own review.
- **MCP:** call `texel_live` and share the returned URL; every `texel_render` then shows up in their tab.
- **Browser agent:** work in a studio tab the person can see; `window.texel` updates it directly.

They can react mid-way ("shorter hair", "more pink") and you patch, instead of starting over after the reveal.

### 5. Review

Every render returns a **review**: `score` (0–100 technical health), `issues` (with `path`, `code` and a `hint`), `stats`, and a **text render** of the front and back views, so even text-only agents can see the result.

Then look at it. The score only checks hygiene; it cannot tell whether the skin looks good. Screenshot `/studio/?view=inspect` (3D angles + flat sheet) or open the `--sheet` PNG and judge it against the rubric:

| # | Check | Pass when |
| --- | --- | --- |
| R1 | Brief match | Every feature in `description` is visible. |
| R2 | Face | Eyes, brows and mouth read clearly at 1× in the front view. |
| R3 | Silhouette | Head, torso, arms and legs are distinguishable by color/value. |
| R4 | Shading | Light comes from above: top rows lighter, bottoms and inner sides darker. |
| R5 | Texture | No large perfectly flat areas (use noise jitter 2–5 or patterns). |
| R6 | All sides | Back and sides are designed, not just filled. |
| R7 | Depth | The overlay layer adds at least one 3D element (hair, hood, collar, gear). |
| R8 | Hygiene | Review has 0 errors and 0 warnings. |

### 6. Patch

Change the smallest thing that fixes the weakest rubric item, then render again. In the browser: `texel.updateLayer("eyes", { rows: [...] })`, `texel.addLayers([...])`, `texel.toggleLayer(3)`. With files: edit the JSON and rebuild.

Stop when R1–R8 all pass, or after ~6 iterations with diminishing returns.

### 7. Ship

Deliver three things:

1. The **share link**: `node texel.mjs share skin.json`, `texel_share` or `texel.shareURL()`. It is short (`/s/<id>`) and opens the exact skin in the studio, where the person can also download the PNG.
2. The **PNG** (CLI `build` output, `texel_save`, or `texel.download()`), uploaded at minecraft.net or any launcher with the model (classic/slim) matching `model`.
3. The **spec JSON**, the editable source. Give the file path; paste the JSON into the chat only when you cannot save files.

### Continuing a skin

Asked to change an existing skin, start from its link: `node texel.mjs pull <link> -o skin.json` (MCP: `texel_pull`), keep a copy of the original, go live, and change only what was asked by patching the layers involved. Check with `diff` that nothing else moved, and ship a new link: links are immutable, so every version has its own.

## Contract

- Compilation is deterministic: same spec → same PNG, byte for byte, in every interface.
- Compilation never throws. Invalid layers are skipped and reported; valid ones still render.
- Layers apply in order; later layers overwrite earlier ones (no blending).
- Issue `code`s are stable identifiers you can branch on.


---

# Skin spec reference

> Complete reference for the Texel skin spec (`version: 1`): structure, colors, selectors, coordinates and all 13 operations.

## Shape

```json
{
  "$schema": "/schema/skinspec.v1.json",
  "version": 1,
  "name": "Explorer",
  "description": "What this skin should look like (the brief).",
  "model": "classic",
  "palette": { "skin": "#d9a066", "skinShade": "skin:-8", "shirt": "#2f8f83" },
  "legend": { "S": "skin", "s": "skinShade", "W": "#ffffff", "E": "#2d5ba8" },
  "layers": [
    { "op": "fill", "target": "all", "color": "skin" },
    { "op": "fill", "target": "body", "color": "shirt" },
    { "op": "pixels", "target": "head.front", "y": 4, "rows": ["SWESSEWS"] }
  ]
}
```

| Key | Type | Notes |
| --- | --- | --- |
| `version` | `1` | Required. |
| `model` | `"classic"` \| `"slim"` | Arm width 4px or 3px. Default `classic`. |
| `palette` | object | Name → color. Names: letters, digits, `_`, `-`; start with a letter. |
| `legend` | object | Single char → color, for `pixels` rows. `.` and `_` are reserved. |
| `layers` | array | Operations, applied top to bottom. Required. |
| `name`, `description`, `author`, `tags` | metadata | `description` is the brief. |

## Colors

A color expression is one of:

- `#rgb`, `#rrggbb`, `#rrggbbaa`
- `transparent`
- a palette key: `"shirt"`
- any of the above plus `:<n>`: shift HSL lightness by *n* points: `"shirt:-12"`, `"#88aaff:+6"`

Palette entries may reference each other, which is the idiomatic way to build tone ramps:

```json
"palette": { "cloth": "#7a5c3e", "clothLight": "cloth:+10", "clothDark": "cloth:-12", "clothDeep": "cloth:-24" }
```

Base-layer pixels must be opaque (Minecraft renders transparent base pixels black). Overlay pixels are either opaque or `transparent`. Avoid partial alpha.

## Model anatomy

The character faces you. **`right`/`left` are the character's own sides**: `rightArm` appears on the viewer's *left* in the front view.

| Part | front / back (w×h) | right / left (w×h) | top / bottom (w×h) |
| --- | --- | --- | --- |
| `head` | 8×8 | 8×8 | 8×8 |
| `body` | 8×12 | 4×12 | 8×4 |
| `rightArm`, `leftArm` (classic) | 4×12 | 4×12 | 4×4 |
| `rightArm`, `leftArm` (slim) | 3×12 | 4×12 | 3×4 |
| `rightLeg`, `leftLeg` | 4×12 | 4×12 | 4×4 |

Every part has two layers: `base` (the body) and `overlay` (a slightly larger shell: hat, jacket, sleeves, pants). Overlay starts fully transparent.

### Face-local coordinates

Each operation works in **face-local coordinates**: `(0, 0)` is the top-left pixel of the face *as seen from outside the model*. Drawing is clipped to the face, so it never bleeds into neighbours.

- On `front`, x grows toward the character's left (viewer's right).
- On `right`, x=0 is the back edge and the last column touches the front.
- On `left`, x=0 touches the front and the last column is the back edge.
- On `back`, x grows toward the character's right.
- On `top`, the last row touches the front.

Negative `x`/`y` count from the far edge: `"y": -2` means "the last 2 rows" when `h` is omitted.

## Selectors

`target` picks one or more faces:

```
<parts>[.<faces>][@<layer>]
```

| Piece | Values |
| --- | --- |
| parts | `head` `body` `rightArm` `leftArm` `rightLeg` `leftLeg` · groups: `arms` `legs` `limbs` `all` |
| faces | `top` `bottom` `right` `front` `left` `back` · groups: `sides` (the four vertical faces), `all` (default) |
| layer | `base` (default), `overlay`, `both` |

Join alternatives with `+`: `"head.top+back"`, `"arms+legs.sides"`. An array of selectors is a union.

Examples: `"all"` · `"head.front"` · `"legs.sides"` · `"body.front+back@overlay"` · `"arms.top@both"`.

When a selector matches several faces, the operation runs **once per face** in that face's local coordinates. `{"op":"rect","target":"legs.sides","y":-2,"color":"boots"}` paints the bottom two rows all the way around both legs.

## Area options

`rect`, `clear`, `gradient`, `pattern`, `noise` and `shade` accept an optional area: `x`, `y` (default 0, negatives from the far edge), `w`, `h` (default: to the edge). Omit all four for the whole face.

## Operations

Every op accepts `id` (string handle for patching), `note` (free text) and `enabled` (`false` skips it).

### fill
Paint whole faces.
```json
{ "op": "fill", "target": "legs", "color": "pants" }
```

### rect
Paint an area. With only `y`/`h` it becomes a band.
```json
{ "op": "rect", "target": "body.sides", "y": 8, "h": 1, "color": "belt" }
```

### clear
Make an area transparent (overlay only, in practice).
```json
{ "op": "clear", "target": "head.front@overlay", "x": 1, "y": 3, "w": 6, "h": 2 }
```

### pixels
Pixel art as strings: one string per row, one char per pixel. Chars come from the op's `legend`, then the top-level `legend`. `.` keeps the existing pixel, `_` erases it. `x`/`y` offset the grid.
```json
{ "op": "pixels", "target": "head.front", "rows": [
  "HHHHHHHH",
  "HhHHHhHH",
  "HSSSSSSH",
  "SDDSSDDS",
  "SWESSEWS",
  "SSSssSSS",
  "SSsMMsSS",
  "SSSSSSSS"
] }
```

### points
Individual pixels.
```json
{ "op": "points", "target": "body.front", "points": [[3, 8], [4, 8]], "color": "buckle" }
```

### line
Bresenham line between two points (inclusive).
```json
{ "op": "line", "target": "body.front@overlay", "from": [1, 0], "to": [7, 7], "color": "strap" }
```

### gradient
Linear blend `from` → `to`, `vertical` (top→bottom, default) or `horizontal`. `steps` posterizes into N bands; pixel art usually wants 3–5.
```json
{ "op": "gradient", "target": "legs.sides", "from": "steelLight", "to": "steelDark", "steps": 4 }
```

### pattern
Repeating pattern: `checker` (first two colors), `stripes-h`, `stripes-v`, `diagonal` (cycles all colors). `size` = cell size in px. A color of `"."` keeps the existing pixel.
```json
{ "op": "pattern", "target": "body", "kind": "checker", "colors": ["steel", "steelDark"] }
```

### noise
Seeded, deterministic texture. `colors` + `density` (0–1, default 0.2) scatters colors; `jitter` (0–50) randomly shifts the lightness of existing pixels by ±jitter points. Use either or both. `seed` defaults to the layer index.
```json
{ "op": "noise", "target": "body.sides", "jitter": 4, "seed": 3 }
```

### shade
Shift lightness of existing pixels by `amount` (−100…100). The workhorse for depth.
```json
{ "op": "shade", "target": "rightLeg.left+back", "amount": -7 }
```

### copy
Copy one face (`from` must select exactly one) onto target faces. `flip`: `h`, `v`, `hv`. Sizes are resampled if they differ.
```json
{ "op": "copy", "from": "head.right", "to": "head.left", "flip": "h" }
```

### mirror
Mirror a whole part onto another (left/right faces swap, everything flips horizontally). `layer`: `base`, `overlay`, `both` (default).
```json
{ "op": "mirror", "from": "rightArm", "to": "leftArm" }
```

### symmetrize
Make faces left-right symmetric by copying one half onto the other. `source`: `left` (default, low x) or `right`.
```json
{ "op": "symmetrize", "target": "head.front" }
```

## Texture map (for importing / debugging)

UV origin of each box in the 64×64 PNG. Within a box of size w×h×d at (u, v): top `(u+d, v)`, bottom `(u+d+w, v)`, right `(u, v+d)`, front `(u+d, v+d)`, left `(u+d+w, v+d)`, back `(u+2d+w, v+d)`.

| Part | base (u, v) | overlay (u, v) |
| --- | --- | --- |
| head | 0, 0 | 32, 0 |
| body | 16, 16 | 16, 32 |
| rightArm | 40, 16 | 40, 32 |
| leftArm | 32, 48 | 48, 48 |
| rightLeg | 0, 16 | 0, 32 |
| leftLeg | 16, 48 | 0, 48 |

## Review issue codes

| Code | Level | Meaning |
| --- | --- | --- |
| `bad-json`, `bad-spec`, `no-layers` | error | Spec cannot be read. |
| `unknown-op`, `missing-key`, `bad-selector`, `bad-color`, `unknown-char`, `bad-number`, `out-of-range`, `bad-point`, … | error | Layer skipped. |
| `unknown-key`, `version`, `clipped` | warning | Ignored input / pixels outside the face. |
| `base-transparent` | warning | Base pixels left transparent (render black in-game). |
| `blank-face` | warning | `head.front` has fewer than 3 colors. |
| `flat-surface` | info | A visible face is ≥90% one color. |
| `few-colors` | info | Fewer than 6 colors overall. |
| `hat-covers-face` | info | Hat layer is fully opaque over the face. |
| `unused-palette` | info | Palette keys never referenced. |
| `overwritten-layer` | info | A layer is completely painted over by later layers, so it does nothing. The `fill` on `all` safety net is exempt. |

Score = 100 − 25 per error − 8 per warning − 2 per info (transparent-base penalty capped at 20).


---

# Skin art guide

> Practical pixel-art rules for 64×64 Minecraft skins, written for agents: where features go, how to shade, and the mistakes that make skins look amateur.

## The face (head.front, 8×8)

The face is 8 pixels wide, so every pixel is a decision. A reliable layout:

| Row | Content |
| --- | --- |
| 0–1 | Hair (or helmet/hat) |
| 2 | Forehead, hair fringe on the edges (x=0 and x=7) |
| 3 | Eyebrows (hair color or darker), optional but adds expression |
| 4 | Eyes: white at x=1 and x=6, iris at x=2 and x=5 |
| 5 | Cheeks; nose as 1–2 slightly darker skin pixels at x=3–4 |
| 6 | Mouth: 2–4 pixels centered, a *darker skin tone*, not black |
| 7 | Chin / jaw, beard if any |

```
HHHHHHHH   H hair      h hair highlight
HhHHHhHH   S skin      s skin shadow
HSSSSSSH   D brow      W eye white
SDDSSDDS   E iris      M mouth
SWESSEWS
SSSssSSS
SSsMMsSS
SSSSSSSS
```

Rules of thumb:

- Eyes 2 px wide (white + iris) read best. Iris toward the center gives a friendly look; toward the edges looks surprised.
- Never outline the face in black. Contrast comes from value, not lines.
- Hair should wrap: continue it on `head.top`, `head.back` and the upper/back part of `head.right` / `head.left`.

## Body landmarks

| Area | Where |
| --- | --- |
| Collar / neckline | `body.front` rows 0–1 |
| Belt | `body.sides` row 8 or 9 (1 px) |
| Pants start | `body.sides` rows 9/10–11 (so the waist continues onto the legs) |
| Sleeves | `arms.sides` rows 0–3 (short) or 0–9 (long) |
| Hands | `arms.sides` last 2–3 rows + `arms.bottom` |
| Shoes | `legs.sides` last 2–3 rows + `legs.bottom` |
| Knees | `legs.front` row 5–6 |

## Shading

Minecraft's lighting is flat, so skins carry their own shading. Assume light from **above and slightly in front**.

1. **Tone ramp per material:** 3 to 4 tones: highlight (+8…+12), base, shadow (−8…−12), deep (−20…−28). Define them in the palette with `:` shifts.
2. **Top-down:** lighter on top rows/`top` faces, darker toward the bottom of each part. `gradient` with `steps: 3–5`, or `shade` on the last row.
3. **Inner and back faces darker:** limbs' inner faces (`rightArm.left`, `rightLeg.left`, …) and `back` faces by −6…−10.
4. **Separate overlapping parts:** the row where sleeves end, where shirt meets pants, and where boots begin should have a 1 px shadow.
5. **Texture, not noise soup:** `noise` with `jitter` 2–5 for cloth/metal grain; 6+ looks dirty. Don't jitter faces or small details.
6. **Hue-shift shadows** for richer art: shadows slightly cooler/more saturated, highlights warmer. (Pick explicit hex tones for that instead of `:` shifts.)

## Color

- 15–60 distinct colors is typical for a good skin. Fewer than 6 looks flat.
- 1 dominant hue, 1–2 secondary, 1 accent (small area, high saturation: buckles, eyes, gems, lights).
- Keep adjacent parts at different *values* (lightness), not just hues. Silhouettes must read in grayscale.
- Avoid pure `#000000` and pure `#ffffff` in large areas.

## The overlay layer

The overlay is a shell 0.5 px (head) / 0.25 px (body, limbs) outside the base. It is what makes skins feel 3D.

- **Hair volume:** extend hair on `head.*@overlay` a pixel or two past the base hairline.
- **Hoods / helmets:** fill `head@overlay`, then `clear` where the face should show.
- **Jackets, scarves, belts with pouches, backpacks:** `body.*@overlay`.
- **Cuffs, gloves, boot tops:** `arms/legs.*@overlay` bands.
- Keep overlay pixels fully opaque or fully transparent.

## Workflow that works

1. `fill all` with the dominant skin/suit color (no transparent base).
2. Fill parts: head, body, arms, legs.
3. Bands: sleeves, belt, pants, shoes.
4. Head: `pixels` for the face, hair on top/back/sides (`copy` right → left with `flip: "h"`).
5. Shading and texture on the broad areas: `gradient` / `shade` / `noise`.
6. Details on one side (they stay crisp because they come after the texture); `mirror` limbs; then asymmetric details.
7. Overlay pass.
8. Review, screenshot, patch.

## Common mistakes

| Mistake | Fix |
| --- | --- |
| Transparent base pixels | Start with `{ "op": "fill", "target": "all", "color": "…" }`. |
| Mixing up left/right | `rightArm` is on the **viewer's left** in the front view. |
| Arms painted 4 px wide on a slim model | Slim arm fronts are 3 px wide; use `pixels` rows of 3 chars. |
| Forgetting back and sides | Check `back`, `right` and `left` views in the review. |
| Black outlines everywhere | Use darker tones of the local color instead. |
| `noise` / `shade` as the last layers | They also hit eyes, collars and buttons. Texture broad areas first, then paint details. |
| Same value head/body/legs | Vary lightness between parts. |

