Skip to content

Core concepts

Five ideas carry the whole engine. Once these are clear, the rest of the documentation is a list of key names.

1. A content-free engine

The comicforge package is code and nothing but code. It has no default characters, no bundled backgrounds, no fallback sprite. There is no shared asset library anywhere — every project owns all of its own art.

comicforge/          the engine — loads assets, composes SVG, writes output
my-comic/            a project — characters, scenes, sprites, page specs

This is why every asset directory has to be named explicitly, either in the spec or on the command line. A missing one is an error with a sentence telling you which key to set, never a silent fallback to something built in.

The upshot is that upgrading the engine can never change how your comic looks by swapping art underneath you, and two projects on one machine can not collide.

2. A page is a declarative spec

There is no drawing API and no imperative sequence of calls. A page is a YAML document that names its assets and describes its layout:

library: "../characters"
rows:
  - panels:
      - actors:  [{char: tom, face: happy, x: 0.4, y: 0.7, scale: 0.8}]
        bubbles: [{text: "Ahoj!", kind: speech, speaker: tom}]

Because the whole authoring surface is data, the same file can be hand-written, templated, generated by a script, diffed in a pull request, or produced by a language model — and it renders identically in every case.

The companion to the spec is the manifest: cmf characters and cmf scenes print JSON describing exactly what art exists, which slots it has, and what the legal variants are. Spec plus manifest is the entire contract.

3. Posing is stacking overlays

A character is not a rig. It is a base SVG plus overlay SVGs, all drawn in the same viewBox, stacked in order:

base.svg  +  face-happy.svg  +  arms-wave.svg   →   one <g> in the panel

An overlay belongs to a slot (face, arms, …) and is one variant of that slot. Writing face: happy in a spec selects a file. There is no interpolation, no skeleton, and no maths you have to get right — which means every expression is a small hand-editable drawing, and a wrong-looking pose is fixed by editing a shape rather than by tuning a parameter.

Tom's seven face variants, one per panel

Seven files, one slot. Every variant of Tom's face slot — the last panel omits face: entirely and gets the manifest's default.

A character that needs more than one body — sitting versus walking — owns several poses, each with its own base. Shared overlays are drawn once and re-registered onto each pose through an anchor point. See Making the art › Characters for the mechanics.

4. Everything is placed by fraction

No coordinate in a spec is a pixel or a millimetre. Positions are fractions of the panel that contains them:

  • x runs 0 (left edge) → 1 (right edge)
  • y runs 0 (top edge) → 1 (bottom edge)
  • scale, for an actor or a sprite, is its height as a fraction of the panel height

Five sprites at the panel's four corners and centre

Sprites at (0.1, 0.1), (0.9, 0.1), (0.5, 0.5), (0.1, 0.9) and (0.9, 0.9).

The point of fractions is that a panel can change size — a different page format, a re-weighted row, a caption band appearing underneath — without invalidating a single number you wrote. Composition survives layout changes.

The one place this does not apply is page:, which is in millimetres, because paper is.

5. SVG all the way down

Every stage composes SVG. Characters, scenes, bubbles, captions, sprites and frames are all markup; the page is one SVG document; rasterization happens once, at the very end, and only if you asked for a raster.

Extension What you get
.svg The composed document itself. Raster backgrounds are embedded as data URIs, so it stays self-contained when moved.
.pdf True vector. Crisp at any print size. px_per_mm is irrelevant to it.
.png Rasterized at px_per_mm — the one output where that key matters.

How the pieces fit

                       ┌─────────────────┐
   characters/  ─────► │                 │
   scenes/      ─────► │  spec (YAML)    │ ──► composed SVG ──► .svg
   pixel/       ─────► │  rows > panels  │                  ├─► .pdf  (vector)
   images       ─────► │                 │                  └─► .png  (px_per_mm)
                       └─────────────────┘

Within a panel, things are drawn back to front in a fixed order:

  1. the panel background colour (bg, cream #fbfaf6 by default)
  2. the caption: band, if any
  3. a raster image:, if any
  4. a scene: background, if any
  5. pixel: sprites
  6. actors:, in the order they are listed
  7. bubbles:
  8. the frame: outline, last and on top of everything

So a later actor in the list covers an earlier one, and bubbles are always on top of the figures. Everything except the frame is clipped to the panel's rounded rectangle — including the caption band, which is painted early enough that an actor placed low can overlap it.

Vocabulary

Term Means
project A directory owning characters/, scenes/, pixel/ and its page specs.
spec One YAML file describing one page or one standalone illustration.
character An identity — a name, a label, and one or more poses.
pose One body of a character, drawn in its own viewBox.
slot A dimension of variation on a character or scene — face, arms, weather.
variant One value of a slot, backed by one overlay SVG.
anchor The reference point a shared overlay is drawn around, so it can re-register onto every pose.
actor One placement of a character inside a panel.
scene A reusable illustrated background, cover-scaled into a panel.
manifest The JSON printed by cmf characters / cmf scenes — the machine-readable list of what exists.