guide
The contract AI agents read at /llms.txt
- This page renders /llms.txt for people.
Agents should fetch /llms.txt directly. - People: give your AI the prompt from “PASTE THIS TO YOUR AI” on the top page.
This page shows what the agent will follow.
read / paint
Compact contract for AI agents painting watercolor in a browser the owner can watch. Static and browser-local: no remote paint API, model, account or cloud sync.
The owner gives a subject; this file is the rest of the job: paint one watercolor where the owner can watch, accept a trial only after looking at its actual image, then deliver as in Finish.
Start
- Any browser works: the owner's open Emaki tab, a new window, or your own built-in browser. Prefer one the owner can see (not headless), so they can watch the painting form; if you can only run headless, send the owner an image after each stage. Open
/or/en/and wait fordocument.body.dataset.workspaceReady==="true". const S=window.emakiStudio; let o=S.observe({includeImage:false});Requireo.writable && !o.busy. One Emaki tab per browser profile can write. Ifwritableis false, another Emaki tab in this browser holds the paper: work in that tab instead, or stop and ask the owner to close it, then reload.- Saved art lives in that browser. If
o.marks>0, firstawait S.execute({op:"snapshot"}). Mutations require the currentexpectedRevision; after any error re-observe and fix the named cause. Never blindly retry.
First painting
o = S.observe({includeImage:false});
if (o.marks > 0) await S.execute({op:"snapshot"});
o = await S.newDocument({aspectRatio:"3:4", expectedRevision:o.revision});
const scene = S.scene(); // keep this fixed header
scene.title = "Evening sea";
scene.layers.push({id:"sky", intent:"Pale sky wash, then let it settle", marks:[
{type:"brush", points:[[.1,.2],[.5,.18],[.9,.2]], width:.08,
color:"#6f9fd8", opacity:.35, water:.4, duration:1.2,
pressure:[.6,1,.5]},
{type:"wait", seconds:5}
]});
const trial = await S.preview(scene,{expectedRevision:o.revision});
// Inspect the actual trial image. Accept only if it serves the aim.
o = await S.accept(trial.candidateId,{expectedRevision:trial.baseRevision});
const files = await S.exportBundle(); // PNG + scene JSON
const movie = await S.exportVideo(); // required process video
preview renders a candidate on the paper without changing accepted art. On archiveWarning, keep the tab open; see recovery. accept adopts the active candidate; undo returns one accepted step. newDocument makes a new sheet, not a resized painting.
Scene essentials
- Keep the header returned by
S.scene(): output width 720, material grid width 640, seed 42,watercolor-standard-v2/paper-brush-v5. Choose only an aspect ratio:1:1, 4:5, 3:4, 2:3, 9:16, 5:4, 4:3, 3:2, 16:9. - v5 simulates paper, water, pigment and brush. Dark drying edges, blooms, granulation and dry-brush breaks happen from water and timing, not from settings. Read material-v5.md before painting.
- Layer:
{id,intent,marks}. IDs are unique and stable.intentis the short truthful stage caption shown to the owner and in the video. At most 32 layers. - Brush:
{type:"brush",points,width,color,opacity,water?,duration?,pressure?,tip?,pigment?}. Points are 2–256 normalized[x,y]centerline positions. Width is0.0001..0.15of paper width; color is#rrggbb; pressure is one0.05..1value per point.opacityis paint in the mix (0 clear water, 1 near tube paste);wateris how charged the brush is (default 0.6). A faster stroke leaves less water. tip:round(default),flat,fan,liner,hake,mop,hog, or{preset,...}to changebristles,length,point,stiffness,capacity,spread,thickness.pigment: a named paint or a mix, e.g.[{name:"french-ultramarine",part:1},{name:"burnt-sienna",part:1}].- Drying:
{type:"wait",seconds:0.1..180}; a wash loses its shine in about 3 minutes and is dry in about 18. Layerenvironment:{drying:"hold"|"fast", tilt:0..60}pauses the clock, uses a hair dryer (6× faster) or tilts the board. Brush durations + waits: at most 7200 material seconds. - Other tools:
spatter,salt,mask(masking fluid) andunmask; see material-v5.md. No fills, shape tools, pasted images, post-filters or image-generation substitutes. White paint is not an eraser; lift with a damp brush. - Technique cards (
/sketch/skills/agent-index.json) were tested on older paper: ideas carry over, numbers do not.
Iterate without resending the full scene
Use stable layer IDs. All operations below create an unaccepted preview:
await S.execute({op:"append_layer", layer:newLayer, expectedRevision:o.revision});
await S.execute({op:"replace_layer", layer:completeReplacement, expectedRevision:o.revision});
await S.execute({op:"remove_layer", layerId:"old-detail", expectedRevision:o.revision});
await S.execute({op:"preview_patch", expectedRevision:o.revision, patch:{
title:"Refined evening sea",
replaceLayers:[completeReplacement], appendLayers:[newLayer],
removeLayerIds:["old-detail"]
}});
Add trialId to patch a saved trial instead of the accepted scene. A patch may change only title, appendLayers, replaceLayers, and removeLayerIds; full validation still applies. Use full S.preview(scene,...) for canvas/material changes or broad rewrites.
For long computation use {op:"submit"} jobs (see workspace.md).
Inspect and decide
- Screenshot
#studio-paper, or read a saved trial crop withS.execute({op:"image",trialId,crop:[x,y,w,h],maxWidth:720}). Treat valid JSON and metrics as execution evidence, never as art-quality evidence. - Compare one hypothesis at a time. Record aim, what to preserve, change, visible observation and reason with
{op:"record",trialId,notes,decision:"kept"|"rejected"|"deferred"}. No-change and defer are valid. - For an older candidate, call
{op:"branch",trialId,expectedRevision}and accept the newly returned candidate ID. A later preview replaces the active candidate, but archived trials remain. - Without JavaScript access, use the page's scene form: see agent-reference.md.
Finish and deliver
- Export accepted files:
await S.exportBundle()returns PNG/JSON artifact metadata and downloads both.S.artifacts()orS.execute({op:"artifacts"})lists stable re-download URLs for files made during this page session; the same links appear under the paper. - Export the required process video:
await S.exportVideo(). It begins with the finished painting for 0.5 s, replays the brush process with layer-intent captions, and ends on the finished painting. It repaints the painting first (minutes on v5), then records in real time; keep the tab visible. For short tool timeouts useS.execute({op:"video",wait:false}), then pollS.observe({includeImage:false}).video. - Give the owner the real PNG, JSON and video plus a short note: aim, main stages, rejected trial/reason, and remaining limitation. Use the owner's language.
- Ask whether the owner wants matching Gemini BGM added. Do not make music before explicit approval; if approved, follow video.md.
Load more only when needed
- Full API, validation, compatibility and material detail: agent-reference.md
- Trial, patch, artifact and WebMCP commands: workspace.md
- Scene fields: schema.json
- Technique selector: agent-index.json
- Video details: video.md