Writing a .cel model · celworld docs

Writing a .cel model

Shapes, colours, joints and motion, coarse to fine, held to a budget the compiler enforces.

A .cel script is a seeded family: every seed grows a different, equally good variant. It compiles to the same primitives, toon materials and ink as the hand-coded generators, streams line by line, enforces a complexity budget, rejects z-fighting at compile time, and carries motion states and cel effects.

The authoritative reference is generated from the compiler's own tables, so it can't drift: see the language reference for every kind, key, default, preset and palette name. This page is how to use them well.

Anatomy

asset <name> "Title" complexity=N [float]       first line; name is snake_case and becomes the library slug
let NAME = expr                                  a gene: drawn once per seed
<kind> <name> [xN] [flags] key=value …           a part, or N copies
fx [state:] <type> on=<part> key=…               particles, after the parts and before the anims
anim [state:] <part> <preset> [key=…]            motion on a jointed part
anim [state:] <part> <channel> <wave> key=…

One statement per line, // comments. Write coarse to fine: the asset line, then lets (proportions, then colours, then style picks), the biggest masses, secondary masses, features, small details, effects, anims.

Coordinates and values

Coordinates. Metres, Y up, the model faces +Z, and +X is the model's own left. The lowest point sits within 3 cm of y = 0 unless the asset line says float. Typical sizes: a critter 0.3 to 0.8 m, a person 1.6 m (chibi 0.8), a vehicle about 3 m long, a house 5 m, a boss 3 to 6 m.

Where a kind sits on its origin. lathe, cyl, cone and box stand on it and grow up +Y; ball, puff, torus and wheel are centred; limb hangs down from it (the joint is at the top); rod runs from it to to=; tube goes through pts=; slab is an x, y outline with thickness along z; face sits on its parent's surface.

Values. Numbers .3, vectors (x, y, z), lists [a b c], stations [t:value …], colours (a palette name, #rrggbb or a colour let). a..b is a seeded uniform draw; red:3|teal|cobalt is a seeded weighted pick. Arithmetic works on numbers, vectors and colours (H*.4, coat*.85 for darker, coat~ for a hue jitter). In a part line you also have i, n, t = i/(n-1), parent.i, parent.<key> and <part>.<key>. Logic: if=chance(.4), if=style=="horned", sel(k, a, b, c).

Placement. on=<part> rides the parent: its frame, its bone and its copies. at=(x, y, z) is in the parent's frame, or an anchor plus an offset: at=top+(0, .05, 0), at=end, at=front-(0, .1, 0). surf=(azimuth, elevation) sticks a part on the parent's surface (0 is the front, 90 its left, 180 the back; elevation 90 is straight up) with +Y out of the surface. decal lies flat on it, upright keeps the parent's axes, and lift= moves along the normal (negative sinks it in). rot=(x, y, z) is degrees, or use yaw=, tilt= and roll=.

Copies. x3 or x2..4. ring spreads copies round the parent's Y; row=(dx, dy, dz) lines them up centred on at=; chain=(dx, dy, dz) makes each copy ride the last (tails, garlands, necks). mirror adds a twin across the parent's X = 0: write the original at x > 0.

Kinds at a glance

KindForKey keys
lathepots, bodies, columns, domesh r prof=[t:r …] linear sq depth bands stripes panels open cap
cyldrums, posts, wheels on endh r r2 round, plus the lathe family
coneroofs, ears, spikes, hatsh r r2, plus the lathe family (panels=4 for a pyramid roof)
ballheads, bodies, eyesr=(rx, ry, rz) under half bands segs rings
pufffoliager under noise
boxrounded boxes, panelssize=(w, h, d) round bev bands stripes panels axis
tubehandles, pipes, spiralspts=[…] r smooth sides
rodpoles, legs, railsto r sides bands
limbarms, legs, neckslen r=(top, bottom) bands depth
torusrims, hoops, arcsR r arc segs sides
slabsigns, wings, leaves, flagspts=[x,y …] t round edge
wheelwheels on an X axler w tyre hub hubR
faceeyes, mouth, blusheyes mouth lid size spacing eyeY mouthY mouthW iris skin blush brows detail
spotseats, saddles, stand spots, decks (no geometry)use=seat|saddle|stand|deck size=(w, d)

Every part also takes on at surf lift decal upright rot yaw tilt roll scale color ink joint mirror x ring ring0 row chain if detail. ink= is full (the default), thin, none or glow (unlit, for real lights).

Complexity budgets

complexity=N on the asset line caps shapes (parts after copies and mirror twins), triangles and draw calls (each ink class in use, plus one for a face, plus one per fx line). Going over any cap is a compile error.

cShapesTrianglesDraw callsTypical
1161,5002a pebble, a sign
2303,2002a mailbox, a fence, a tombstone
3454,5003small props, toys, a canoe
4706,0004critters, machines, a dock
51008,0004houses, desks, a skeleton guard
614010,0005big furniture, an elk
720015,0005a log cabin, a waterfall, a teacup ride
830022,0006a big top, a research lab
942032,0007a ferris wheel, a dragon skull
1064048,0008a kaiju

Aim for 60 to 90% of the shape and triangle caps. Rough triangle costs per shape at default settings: ball 170 to 480, puff 280, lathe or cyl 260 to 530, cone 100 to 190, limb 260, tube 300 to 400, torus 380, wheel 420, box 110 even unrounded, rod 70, slab 50. With segs and rings set, a ball or lathe costs about 2 × segs × rings. A round-eyed face costs about 2,500 triangles at detail=1 and 900 at detail=.6.

Folding small parts into ink=full is the cheap way back under the draw-call budget.

States and anims

  • joint makes a part a bone at its origin. Only jointed parts animate: put joint on the root body and on every part that moves.
  • anim <part> <preset> with no state always plays (idle life). anim walk: leg gait … plays only while a game or a scene puts the model in walk.
  • Presets: gait, sway, bob, bounce, wag, flap, nod, spin, breathe. Channels rx ry rz (degrees), px py pz (metres) and s (scale swell); waves sin spin bounce keys.
  • keys=[0:0 .3:40 .7:40 1:0] is a keyframed cycle; once plays one cycle and holds the end (doors, a bow, a lid).
  • twin=.5 alternates mirror twins; stagger=.5 offsets each copy (a trot on diagonals); stagger=.1 along a chain runs a wave down a tail. A negative hz runs the motion backward.
  • Scene movers play the first state they have from walk run hop move waddle trot scurry crawl fly swim. Name your gait from that list.
  • A placement with any joint or effect is dynamic and can't batch. Drop joints and effects from scenery that doesn't need them: one showcase dungeon went from 476 to 314 draw calls that way.

Faces

face look on=head eyes=round mouth=smile … draws the house face: unlit, never inked, with the same rules on every skin and fur colour. Defaults are bright and alert; lid stays 0 unless the brief names a mood (.3 calm, .5 sleepy, .7 grumpy, .9 and up shut). Mouths: smile, grin, fang, open, cat, flat. Eyes: round, dot, happy, sleepy. Use blush only on skin, fur or pale heads; on green or blue it reads as a wound. Use detail=.6 unless the face is the star.

Spots: seats, saddles, stand spots and decks

A spot is a part with no geometry: a place a scene can use. It takes every placement key and rides its parent's bone, so a seat on a spinning cup spins its rider. Spots don't count toward the shape budget.

spot sit on=seat at=(0, .09, .04) use=seat                    a seat: a person sits on it facing its +Z
spot rider on=horse at=(0, .2, -.04) use=saddle               a saddle: sits astride, facing +Z
spot deckhand on=deck at=(0, .1, 0) use=stand                 a stand spot: someone stands here and rides along
spot walk at=(0, .66, 2.5) size=(1.5, 5.5) use=deck           a deck: a walkable rectangle, centred on the spot
spot ramp at=(0, .6, -2.2) tilt=-14 size=(1.3, 1.5) use=deck  tilt a deck for a ramp or steps

Put the spot on the surface: a seat at the cushion's top, a deck at the walking surface. A model with a deck may span paths and water (a bridge, a dock), and walkers step onto any deck within half a metre of where they stand.

Mistakes everyone makes once

Names. Lets and parts share one namespace with palette names, kinds and functions. light, shade, clamp and floor are taken. Call a colour let coatC or furC, and rename on the first error.

Copies and parents.

  • row= copies are centred on at=, not started from it.
  • Children of a copied part are copied with it: mug x3 on=peg with peg x3 makes nine mugs. Drop the inner x and use parent.i for per-parent variety.
  • xN with no row or ring stacks the copies in one place and z-fights. mirror alone is the pair.
  • row and ring on one line multiply out; use two lines.
  • Children of a rotated parent live in its rotated frame. Make the root an unrotated part and give every child world-like coordinates.

Rotation and shape.

  • rot= is a three.js Euler in YXZ order. A slab laid flat with rx=-90 turns in the ground plane with ry.
  • Materials are front-side only, so open=top lathes show through from inside. Fake the inside with a contrasting part poking through a closed top.
  • Keys are per kind: the check names the kind and the key it doesn't know.

Anims.

  • anim … s keys=[…] values are offsets: scale = 1 + v.
  • cel shot model --state s poses at t = 0.6 s unless you pass --t. Shoot a slow cycle at a few times.
  • Which sign raises a limb depends on how it was rotated. Shoot the state and flip the sign or the channel.

Z-fighting and ink.

  • Thin flat parts stacked on a surface z-fight unless each has its own height: offset by a few millimetres.
  • Keep inner parts at least 10 cm under an outer surface, or their ink pokes through.
  • Markings follow the surface: under=, bands=, or a ball on surf= sunk in with negative lift. Never a flat disc pasted on.

More to learn from

The worked examples the generator learns from are in the language reference, each with an Open in Playground button: a vehicle with glass and glowing lamps, a tree with every gene, and a fountain with cel effects. Every model in the library shows its script too.