Authoring with Claude¶
ComicForge was built so that a language model can author comics without any special integration. Nothing in the engine is aware of an LLM — the affordances are the same ones that make the tool scriptable:
- The whole authoring surface is text. A page is YAML; there is no canvas state, no drawing calls, no mouse.
- The art is machine-readable.
cmf charactersandcmf scenesprint JSON listing every character, pose, slot and variant that exists. - Mistakes are enumerable.
cmf validatereports every problem in a spec at once, with the legal values, instead of failing on the first one. - Feedback is cheap and targeted.
cmf panelrenders one panel andcmf characterrenders one figure, so a correction does not require re-reading a whole page.
The bundled skill¶
The repository carries an authoring skill at
skills/comicforge/ —
SKILL.md is the authoring contract, reference.md the deeper reference.
cmf init copies it into every project it scaffolds:
Open that project in Claude Code and the skill loads when the work calls for it — writing a page spec, adding a character, placing bubbles, answering a question about the grammar. You do not have to mention it.
Refresh it after upgrading the engine:
The loop that works¶
Whether the author is a person or a model, the same sequence keeps things honest:
- Read the manifests first.
cmf characters --library charactersandcmf scenes --scenes scenes. Never guess a pose or a variant name — the manifest is the list of what may legally appear in a spec. - Write the spec under
pages/, withlibrary:,scenes_dir:andpixel_dir:relative to the spec file. - Validate before rendering.
cmf validate pages/strip.yamlcatches the silent failures — a mistyped slot, an unknown panel key, aspeakernaming nobody — that would otherwise render as a plausible-looking wrong panel. Its bubble layout warnings (reading order, crossing tails, a covered head) are worth clearing too. - Render, then look.
cmf renderfor the page, orcmf panel --row --colfor the one panel being worked on. - Adjust
x/y/scale/toand repeat.
Step 3 is the one that is easy to skip and expensive to skip. render silently
ignores keys it does not recognise, so fcae: happy produces a perfectly
rendered default face and no complaint at all.
Things worth telling a model explicitly¶
Habits from other tools that do not apply here:
- Do not crop a full-page render to inspect a panel.
cmf panelexists and is far cheaper. The same goes for ImageMagick and friends. - Read the
.small.png.cmf characterwrites a reduced companion image beside its output specifically so that reading it back costs fewer tokens. - Do not auto-vectorize a generated image. Tracing destroys the slot structure and anchor registration that make a character poseable. Reference images are for looking at; the SVG is authored by hand. See Reference images.
- Omit bubble coordinates when in doubt. Bubbles stack by measured height
and are clamped inside the panel, so leaving
xandyout is more reliable than guessing them. Over raster panels, which have no actors, mark each speaking head in the panel'sspeakers:and give every bubble aspeaker: the bubbles are then placed in reading order with tails aimed at the heads. - The engine ships no art. There is no default character to fall back on. A new project starts empty and every asset directory must be named.
Generating specs from a script¶
The same contract works without an LLM. Read the manifest, emit YAML, validate, render:
import yaml
from comicforge.library import Library
from comicforge.validate import validate_spec
from comicforge import render_spec
manifest = Library("characters").manifest()
faces = manifest["tom"]["slots"]["face"]
spec = {
"page": [200, 60],
"library": "characters",
"rows": [{"panels": [
{"actors": [{"char": "tom", "face": f, "x": 0.5, "y": 0.6, "scale": 0.85}],
"caption": f}
for f in faces
]}],
}
problems = validate_spec(spec)
assert not problems, problems
render_spec(spec, "faces.png")
A dict spec has no file to resolve relative paths against, so "characters"
here means "relative to the current working directory" — see
Python API.
That is how the contact sheets in the gallery are made — except they are checked-in specs rather than generated ones, so they are also documentation.