Sinua
Reference

Character recipe

Every key a character recipe may hold: the box and its spaces, the palette, the rig, shapes, limits and every part.

A character is data: a recipe, a JSON object the engine reads and draws. The seven built-in characters are recipes, and your own goes into an FX Spec 1.12 file's recipe, with the file's pattern naming its id. Make your own character walks through one; this page lists everything a recipe may hold.

  • Schema: put "$schema": "https://sinua.dev/schema/character-recipe-1.json" in a recipe file, or use the FX Spec schema (https://sinua.dev/schema/fx-spec-1.json), which includes it. Your editor then completes and checks the recipe as you type.
  • The engine has the last word: it reports the same mistakes, with a JSON Pointer into the recipe (/recipe/parts/2/rise: expected a number).
  • Generated: the part tables below come from the engine's own tables, so they are always current.

The box and the spaces

Everything is drawn in a 200 × 200 box, x to the right, y down; the character usually stands on y ≈ 184. Each part names the space it moves in:

SpaceMoves withTypical parts
bodythe body and head: lean, squash, hop, the head turnbody, arms, crest, steam
facethe face: turned on its surface, looking roundeyes, mouth
mounta pivot rig's mount: bobs between the stand and the bodyHum's yoke
groundnothing: stays on the floorshadow
wholethe character's box, without the turnfeet, stand

A surface (surfaces) is a sphere or cylinder (c, r, depth, cylinder) that face parts sit on, so they wrap round when the head turns. A shallower depth turns them less.

Top level

KeyMeaning
reciperequiredThe format version: 1.
idrequiredThe character's name: 1–32 of a–z, 0–9 and -. A file's pattern names it.
profileoptionalThe built-in whose voice-state behaviour it borrows (how it turns, looks and moves per state). A remix of Cuppa says "cuppa"; without it, the shared character profile.
paletterequiredColours by name, [hue 0–360, saturation 0–1, lightness 0–1]. Parts name them; they are also the slots the palette option repaints.
huerequiredbase: the hue the palette is drawn at; turns: the names the hue option rotates.
contrastoptional[[ink, ground], …]: a face colour and what it sits on, so a palette override keeps it readable.
rigrequiredpivot (pivot, lean, optional squash, sway, mount), upright (base) or float (center, lean, float { rate, amp }).
surfacesoptionalNamed surfaces for face parts.
partsrequiredWhat is drawn, back to front.
burstrequiredThe celebrate burst: at, r, three colors, optional space.
grainoptional{ "strength": 0–1 }: film grain inside every body (FX Spec 1.13). See The richer look.
rolesoptional{ "primary": "<slot>", "secondary": "<slot>", "accent": "<slot>" } (FX Spec 1.13): the slots a role colour ({ "primary": "#E63946" }) repaints. A role left out isn't repainted.

Every part has part (its kind) and space (a body layer inside inner may leave it out), and may have when (notSmallOrAccessories: left out at 20 px with accessories off), show ({ idle, listening, thinking, speaking }, 0–1: how visible it is per voice state).

Removed in 0.1.0-beta.9, and an error if a recipe still has them: slots, tags, cosmetics, a part's role, the eyes / faceScreen fields style, iris and sclera, and the iris role.

Limits

Over a limit is an error, never trimmed: a file from outside can't make the engine slow.

  • The recipe: at most 64 KB; at most 96 parts, a body's layers included (48 before FX Spec 1.13); at most 4,096 points in all its path shapes together, after flattening.
  • Lists (feathers, bars, stripes, glints, stops, layers): at most 32 entries.
  • segments: 3–128. count: 1–24. Rounding steps: at least 1.
  • Every number within ±1000.
  • An SVG path: at most 16 KB, 512 commands, 512 points, 8 holes; the commands M L H V C S Q T Z (no arcs: fitPath converts them); later subpaths are holes, and a subpath outside the first is an error.

Shapes

A shape is one of:

  • { "ellipse": [cx, cy, rx, ry, rotation, segments] }
  • { "roundRect": [x, y, w, h, radius, step] }
  • { "path": "M… Z" }

A body's light is { "radial": [cx, cy, r] }, an elliptical { "radial": [cx, cy, rx, ry, angle] } (radians), or { "linear": [x0, y0, x1, y1] }, with stops [[offset 0–1, colour], …]. A stop may carry an alpha, [offset, colour, alpha], and a light takes any number of stops.

The richer look (FX Spec 1.13)

Airbrushed shading, rim light and film grain on today's 2D painter, with no blur (spec/examples/rich-bean.fxspec.json and rich-buzzy.fxspec.json show all of it):

  • shade (a body layer): its own shape and light, clipped to the body. Give the light stops that fade to alpha 0 and it is a soft mass: a core shadow, light bounced from below, a highlight, a head's soft shadow on the body, or, with "surface": "face", a blush that turns with the face. Draw the shape round the light's ellipse; outside it the light is transparent anyway, and a smaller shape paints less.
  • rim (a body layer): the body minus itself moved by offset, the edge the light reaches first, in color at alpha; fade thins it out round the sides.
  • "light": "none" on a body: no fill of its own, only its layers, grain and outline. Use it as an overlay to add soft layers over a part that has none (BUZZY's helmet), with outline width 0.
  • grain: { "strength": 0.08 } lays a still noise of light and dark specks inside every body, under its outline; the face stays crisp. The same tile on every platform.
  • Options: grain (0–1) and shading (on/off) on the view or in an FX Spec's params. Grain and rims are left out at 20 px.
  • Low power: an FX Spec resolved under low power turns off the grain and the soft layers of a character that has them, so it is "light" again. Plain characters are untouched.
  • Cost: soft layers and grain add coverage; the showcases are "medium" at 64 px (BEAN 1.10, BUZZY 1.53 of the medium line).

The built-in characters

idWhat it isParts worth borrowing
buzzyA round-helmeted robot with a glowing face screen and ear pods.faceScreen, earPods, chestCore
humA studio microphone: a capsule on a stand whose grille lights as it speaks.stand, yoke, grille, tally
wispA floating spirit with a curling tail and sparkles.spirit, halo, sparkles
chirpA round bird with a crest and wings; its beak is its mouth.crest, wings, beak, notes
cuppaA coffee mug on its saucer: steam rises while it listens and thickens with its voice.steam, stand, a path body with a hole (the handle)
beanA coffee bean on little feet, with aroma sparkles.feet, a path body, patch
beepA small tin robot with arms: a hand to the ear to listen, to the chin to think.arms, tally, chestCore

Parts

shadow

The soft shadow under the character.

FieldTypeMeaning
at[2 numbers]requiredWhere it sits: [x, y] in the 200-unit box.
r[2 numbers]requiredIts radii [rx, ry].
floatstrue / falseoptionalWidens with the float rig's drift (a floating character's shadow breathes).

body

A shape filled with its light, its inner layers clipped to it, and an outline. turnLight [dx, k] moves a radial light as the body turns. "light": "none" (and no stops) draws only the layers: an overlay that adds soft layers over a part that has none (BUZZY's helmet).

FieldTypeMeaning
shapeshaperequiredA shape: { "ellipse": [cx, cy, rx, ry, rotation, segments] }, { "roundRect": [x, y, w, h, radius, step] } or { "path": "M… Z" } (an SVG path with M L H V C S Q T Z; later subpaths are holes).
lightlightrequired{ "radial": [cx, cy, r] }, or an ellipse [cx, cy, rx, ry, angle] (radians; the centre stays put while the body turns under it), { "linear": [x0, y0, x1, y1] }, or "none" (an overlay: no fill).
light.stops[[offset, colour, alpha?], …]requiredThe light's gradient: [[offset 0–1, colour], …], each with an optional alpha [offset, colour, alpha]; any number of stops.
inner[layers]optionalLayers clipped to the body: patch, band, stripes, glints, grille, shade, rim or eyes.
outline.widthnumberrequiredThe outline width (scaled with the character's line weight).
outline.colorcolourrequiredThe outline colour (a palette name).

eyes (a part or a body layer)

The shared eyes: they blink, look round, follow the voice state and take the expression. A part on its own (in face space) or a body layer.

FieldTypeMeaning
at[2 numbers]requiredThe point between the eyes.
scale[2 numbers]requiredIts size: [at 64 and 32 px, at 20 px].
inkcolourrequiredThe ink colour (a palette name); effects tint it.
glownumber or [2 numbers]requiredA glow round the eyes, 0–1: one number, or [at 64 and 32 px, at 20 px].
surfacesurfaceoptionalThe surface it is drawn on (a surfaces name), so it wraps when the head turns. Absent: flat.

feet

Two feet of three strokes each, standing on the ground.

FieldTypeMeaning
xnumberrequiredThe centre line between the feet.
gapnumberrequiredEach foot's distance from x.
topnumberrequiredWhere the legs start (y).
groundnumberrequiredWhere they meet the floor (y).
toe[2 numbers]requiredA toe's reach [dx, y] from the foot.
widthnumberrequiredThe stroke width.
colorcolourrequiredIts colour (a palette name).

crest

Curling feathers that lift while listening (higher with the user's voice), perk while speaking and droop while thinking.

FieldTypeMeaning
base[2 numbers]requiredWhere the feathers grow from [x, y].
spread[numbers]requiredOne number per feather: its sideways direction and spread (negative to the left).
midnumberrequiredWhich feather (0-based) is the middle one; it rises highest.
rootnumberrequiredHow far apart the roots are (times spread).
ctrl[4 numbers]requiredThe curve's control point: [x factor, y, lift factor, middle extra].
tip[5 numbers]requiredThe tip: [x factor, per-feather offset, y, lift factor, middle extra].
lift.ears[2 numbers]requiredHow much listening lifts it: [at rest, with the user's voice].
lift.talk[2 numbers]requiredHow much speaking lifts it: [at rest, with the level].
lift.dotsnumberrequiredHow much thinking lowers it.
turnShiftnumberrequiredHow far it slides sideways as the head turns.
segmentsnumberrequiredHow many points a curve or ellipse is drawn with: 3–128.
widthnumberrequiredThe stroke width.
colorcolourrequiredIts colour (a palette name).

wings

Two wings that flutter with the voice; turned, they swing round the body (the near one grows, the far one thins).

FieldTypeMeaning
xnumberrequiredThe centre line it is mirrored round (x).
at[2 numbers]requiredA wing's centre [dx from x, y].
size[2 numbers]requiredA wing's radii [rx, ry].
anglenumberrequiredIts tilt (radians, mirrored).
segmentsnumberrequiredHow many points a curve or ellipse is drawn with: 3–128.
flutter.ratenumberrequiredHow fast it flutters.
flutter.gainnamerequiredThe option name that scales the flutter (e.g. flutterGain).
surfacesurfaceoptionalThe surface it is drawn on (a surfaces name), so it wraps when the head turns. Absent: flat.
turn.nearnumberrequiredHow much the near wing grows when turned.
turn.farnumberrequiredHow much the far wing thins when turned.
colorcolourrequiredIts colour (a palette name).
outline.widthnumberrequiredThe outline width (scaled with the character's line weight).
outline.colorcolourrequiredThe outline colour (a palette name).

beak

The beak is the mouth: its lower half opens with the voice.

FieldTypeMeaning
lower[3 points]requiredThe lower half's three points [[x, y], …]; the last one drops when it opens.
dropnumberrequiredHow far the lower tip drops, fully open.
upper.quad[3 points]requiredThe upper half's curve: three points.
upper.segmentsnumberrequiredPoints along that curve: 3–128.
upper.tip[2 numbers]requiredThe upper half's tip [x, y].
upper.risenumberrequiredHow far the upper tip rises, fully open.
open.effectnumberrequiredHow open it is during success / celebrate.
open.restnumberrequiredHow open it is at rest.
colors[2 colours]required[lower, upper] (palette names).
outline.widthnumberrequiredThe outline width (scaled with the character's line weight).
outline.colorcolourrequiredThe outline colour (a palette name).
surfacesurfaceoptionalThe surface it is drawn on (a surfaces name), so it wraps when the head turns. Absent: flat.

notes

Music notes rising while it speaks, and thought dots lighting in turn while it thinks.

FieldTypeMeaning
countnumberrequiredHow many: 1–24.
ratenumberrequiredHow fast the notes rise.
from[2 numbers]requiredWhere a note starts [x, y].
travel[2 numbers]requiredHow far it travels [dx, dy].
staggernumberrequiredEach next note's vertical offset.
scale[2 numbers]requiredA note's size: [at 64 px, at 32 and 20 px].
floornumberrequiredThe lowest note opacity while speaking.
colorcolourrequiredIts colour (a palette name).
dots.at[2 numbers]requiredThe first dot [x, y].
dots.step[2 numbers]requiredEach next dot's offset [dx, dy].
dots.r[2 numbers]requiredA dot's radius [first, growth per dot].
dots.segmentsnumberrequiredPoints per dot: 3–128.
dots.ratenumberrequiredHow fast the lit dot moves along.
dots.basenumberrequiredAn unlit dot's opacity.
dots.gainnumberrequiredHow much brighter the lit dot is.

stand

A stand: a shadowed round foot and a stem.

FieldTypeMeaning
under[4 numbers]requiredThe shadow ellipse under the foot [cx, cy, rx, ry].
foot[4 numbers]requiredThe foot ellipse [cx, cy, rx, ry].
segmentsnumberrequiredHow many points a curve or ellipse is drawn with: 3–128.
stem[6 numbers]requiredThe stem, a rounded rectangle [x, y, w, h, radius, step].
darkcolourrequiredThe shadow's colour.
colorcolourrequiredIts colour (a palette name).
outline.widthnumberrequiredThe outline width (scaled with the character's line weight).
outline.colorcolourrequiredThe outline colour (a palette name).

yoke

A U-shaped yoke holding the body at its sides: a dark stroke under a lighter one.

FieldTypeMeaning
x[2 numbers]requiredIts two sides [left, right].
topnumberrequiredWhere the sides start (y).
kneenumberrequiredWhere they start to curve (y).
bottomnumberrequiredThe bottom of the U (y).
midnumberrequiredThe bottom's middle (x).
segmentsnumberrequiredHow many points a curve or ellipse is drawn with: 3–128.
under.widthnumberrequiredThe dark stroke's width.
under.colorcolourrequiredThe dark stroke's colour.
over.widthnumberrequiredThe light stroke's width.
over.colorcolourrequiredThe light stroke's colour.

tally

A tally light: red while listening, amber blinking while thinking, off otherwise.

FieldTypeMeaning
at[2 numbers]requiredWhere it sits: [x, y] in the 200-unit box.
rnumberrequiredIts radius.
segmentsnumberrequiredHow many points a curve or ellipse is drawn with: 3–128.
offcolourrequiredIts colour when off.
listeningcolourrequiredIts colour while listening.
thinkingcolourrequiredIts colour while thinking.
outline.widthnumberrequiredThe outline width (scaled with the character's line weight).
outline.colorcolourrequiredThe outline colour (a palette name).

halo

A soft halo: a radial fade (not drawn at 20 px).

FieldTypeMeaning
at[2 numbers]requiredWhere it sits: [x, y] in the 200-unit box.
rnumberrequiredIts radius.
segmentsnumberrequiredHow many points a curve or ellipse is drawn with: 3–128.
colorcolourrequiredIts colour (a palette name).
stops[[a, b], …]requiredThe fade: [[offset 0–1, opacity 0–1], …].

spirit

A spirit's body: a round head flowing into a tail that curls (with curlGain), sways and trails the head turn, with smoke puffs off the tail tip and a shine.

FieldTypeMeaning
head[3 numbers]requiredThe head [x, y, radius].
stops[[offset, colour, alpha?], …]requiredThe body's gradient [[offset 0–1, colour], …].
linecolourrequiredThe outline colour (a palette name).
smokecolourrequiredThe smoke puffs' colour.
shinecolourrequiredThe head's shine colour.
tail.lagnumberrequiredHow late the tail follows the head turn (seconds).
tail.swingnumberrequiredHow far the tail swings with the turn.

ovalMouth

A mouth that is a small smile at rest, thinking dots, or an oval that opens with the voice; it takes the expression's resting mouth.

FieldTypeMeaning
at[2 numbers]requiredWhere it sits: [x, y] in the 200-unit box.
scale[2 numbers]requiredIts size: [at 64 and 32 px, at 20 px].
halfWidthnumberrequiredHalf the mouth's width.
inkcolourrequiredThe ink colour (a palette name); effects tint it.
surfacesurfaceoptionalThe surface it is drawn on (a surfaces name), so it wraps when the head turns. Absent: flat.

sparkles

Sparkles round the head: wandering at rest, gathering while listening, orbiting while thinking, streaming out with the voice.

FieldTypeMeaning
countnumberrequiredHow many: 1–24.
head[2 numbers]requiredThe point they move round [x, y].
colors[2 colours]required[odd, even] sparkles' colours.

torso

A robot torso: a rounded shoulder shape lit top to bottom.

FieldTypeMeaning
shades[3 colours]required[light, mid, dark] (palette names).
outline.widthnumberrequiredThe outline width (scaled with the character's line weight).
outline.colorcolourrequiredThe outline colour (a palette name).

chestCore

A voice core on the chest: a dark disc, a ring and bars that move with the voice; it follows the head turn at 40 %.

FieldTypeMeaning
at[2 numbers]requiredWhere it sits: [x, y] in the 200-unit box.
rnumberrequiredIts radius.
bars[numbers]requiredEach bar's horizontal offset.
colors[3 colours]required[disc, ring, bars] (palette names).

earPods

Ear pods with chevrons and listening arcs; turned, they swing round the head.

FieldTypeMeaning
xnumberrequiredThe centre line it is mirrored round (x).
reachnumberrequiredEach pod's distance from x.
fronttrue / falseoptionalThis is the near pod's copy in front of the helmet (list it after the helmet).
podcolourrequiredThe pods' colour.
linecolourrequiredThe outline colour (a palette name).
chevroncolourrequiredThe chevrons' colour.
arcscolourrequiredThe listening arcs' colour.

helmet

A helmet: a rounded square lit from the top left; turned, its back edge shows.

FieldTypeMeaning
shades[3 colours]required[light, mid, dark] (palette names).
linecolourrequiredThe outline colour (a palette name).

fin

A fin on top of the helmet (64 and 32 px, with accessories).

FieldTypeMeaning
colorcolourrequiredIts colour (a palette name).
linecolourrequiredThe outline colour (a palette name).

faceScreen

A face screen: the eyes and mouth on it (clipped to it), its rim, and a glass visor with a drifting reflection; it rides the face surface.

FieldTypeMeaning
screencolourrequiredThe screen's colour.
inkcolourrequiredThe eyes' and mouth's colour.
linecolourrequiredThe outline colour (a palette name).
glasscolourrequiredThe visor's colour.
glassEdgecolourrequiredThe visor rim's colour.
glintcolourrequiredThe reflection's colour.
surfacesurfaceoptionalThe surface it is drawn on (a surfaces name), so it wraps when the head turns. Absent: flat.

steam

Soft wisps rising and fading: higher while listening, swaying more while thinking, thicker with the voice.

FieldTypeMeaning
at[2 numbers]requiredWhere the wisps rise from [x, y].
countnumberrequiredHow many: 1–24.
spreadnumberrequiredHow far apart the wisps start.
risenumberrequiredHow high they rise (kept inside the box).
widthnumberrequiredA wisp's width.
swaynumberrequiredHow far they sway.
ratenumberrequiredHow fast they rise.
blurnumberrequiredHow soft they are.
colorcolourrequiredIts colour (a palette name).
alphanumberrequiredIts opacity, 0–1.

arms

Two arms (upper arm, forearm, hand): hanging at rest, a hand to the ear while listening and to the chin while thinking, beats while speaking. arms: false turns them off.

FieldTypeMeaning
shoulder[2 numbers]requiredThe right shoulder [x, y]; the left mirrors it round mirror.
mirrornumberrequiredThe centre line the arms mirror round (x).
length[2 numbers]required[upper arm, forearm].
widthnumberrequiredThe stroke width.
handnumberrequiredThe hand's radius.
ear[2 numbers]requiredWhere the right hand goes while listening [x, y].
chin[2 numbers]requiredWhere the right hand goes while thinking [x, y].
backtrue / falseoptionalThis is the copy behind the body: the far arm, shown as the head turns away (list it before the body).
colorcolourrequiredIts colour (a palette name).
handColorcolourrequiredThe hands' colour.
outline.widthnumberrequiredThe outline width (scaled with the character's line weight).
outline.colorcolourrequiredThe outline colour (a palette name).

patch (body layer: inside a body's inner)

A body layer: a shape in one colour, clipped to the body (on a surface, it wraps when the head turns).

FieldTypeMeaning
shapeshaperequiredA shape: { "ellipse": [cx, cy, rx, ry, rotation, segments] }, { "roundRect": [x, y, w, h, radius, step] } or { "path": "M… Z" } (an SVG path with M L H V C S Q T Z; later subpaths are holes).
surfacesurfaceoptionalThe surface it is drawn on (a surfaces name), so it wraps when the head turns. Absent: flat.
colorcolourrequiredIts colour (a palette name).

band (body layer: inside a body's inner)

A body layer: a shape in one colour clipped to the body, flat (a sleeve, a belt).

FieldTypeMeaning
shapeshaperequiredA shape: { "ellipse": [cx, cy, rx, ry, rotation, segments] }, { "roundRect": [x, y, w, h, radius, step] } or { "path": "M… Z" } (an SVG path with M L H V C S Q T Z; later subpaths are holes).
colorcolourrequiredIts colour (a palette name).

stripes (body layer: inside a body's inner)

A body layer: thin rounded stripes across the body.

FieldTypeMeaning
at[numbers]requiredEach stripe's y.
rect[5 numbers]requiredA stripe: [x, w, h, radius, step].
colorcolourrequiredIts colour (a palette name).
alphanumberrequiredIts opacity, 0–1.

glints (body layer: inside a body's inner)

A body layer: rounded highlights.

FieldTypeMeaning
at[[a, b], …]requiredEach glint [y, height].
rect[4 numbers]requiredA glint: [x, w, radius, step].
colorcolourrequiredIts colour (a palette name).
alphanumberrequiredIts opacity, 0–1.

grille (body layer: inside a body's inner)

A body layer and a mouth: slots that light with the voice while speaking and one light scanning while thinking.

FieldTypeMeaning
countnumberrequiredHow many: 1–24.
xnumberrequiredThe first slot's x.
pitchnumberrequiredThe distance between slots.
ynumberrequiredThe slots' y.
slot[3 numbers]requiredA slot at 64 and 32 px: [width, height, height when lit].
smallSlot[3 numbers]requiredA slot at 20 px: [width, height, height when lit].
rise[2 numbers]requiredHow it moves up [at rest, when lit].
stepnumberrequiredThe slots' rounding step (at least 1).
inkcolourrequiredThe ink colour (a palette name); effects tint it.
surfacesurfaceoptionalThe surface it is drawn on (a surfaces name), so it wraps when the head turns. Absent: flat.

shade (body layer: inside a body's inner)

A soft mass clipped to the body (FX Spec 1.13): its own light, usually an ellipse whose stops fade to alpha 0, so it needs no blur. An airbrushed shadow or highlight, light bounced from below, a head's soft shadow on the body, or (on the face surface) a blush. Left out when shading is off and under low power.

FieldTypeMeaning
shapeshaperequiredA shape: { "ellipse": [cx, cy, rx, ry, rotation, segments] }, { "roundRect": [x, y, w, h, radius, step] } or { "path": "M… Z" } (an SVG path with M L H V C S Q T Z; later subpaths are holes).
surfacesurfaceoptionalThe surface it is drawn on (a surfaces name), so it wraps when the head turns. Absent: flat.
lightlightrequiredIts light: { "radial": [cx, cy, r] } or [cx, cy, rx, ry, angle], or { "linear": [x0, y0, x1, y1] }. Draw the shape round the light's ellipse: outside it the light is transparent anyway, and a smaller shape paints less.
light.stops[[offset, colour, alpha?], …]required[[offset, colour, alpha], …]: fade the last stop to 0 for a soft edge.

rim (body layer: inside a body's inner)

A rim of light along the edge the light reaches first (FX Spec 1.13): the body minus itself moved by offset. Left out at 20 px, when shading is off and under low power.

FieldTypeMeaning
offset[2 numbers]required[dx, dy]: the body moved away from the light; the rim is as wide as the move ([6, 7] lights the top-left edge).
colorcolourrequiredIts colour (a palette name).
alphanumberrequiredIts opacity, 0–1.
fadenumberrequiredHow far (box units) it fades out against the offset, so it thins round the sides; 0 = even.

On this page