• English
  • Shapes

    A character is a drawing: one body outline, plus optional extras such as ears, leaves and antennae. Click any of them below to enlarge it.

    Round

    Rounded rectangle

    Polygon

    Petal

    Garden

    Fruit

    Figure

    Sea

    Critter

    People

    The default shape is mochi. Under each heading is one family, in the order of SHAPE_FAMILIES in the engine. A character is filed in exactly one family, so when the engine gains one, this page grows with it.

    <CharacterMark shape="mochi" state="happy" />
    <CharacterMark shape="bunny" state="happy" />

    What a drawing holds

    Each character has an entry in the engine's shapes/characters/, one file per family (round.ts, critter.ts, …):

    • The body: one closed outline. An SVG path that was good as drawn goes in as is; a dense outline is sampled into a ring of points and rebuilt with a Catmull-Rom spline.
    • Where the face goes: when the body's mass is not at the centre of its bounding box (a character with ears sits low), the entry says where to put the face.
    • Parts (optional): ears, leaves, antennae. See the next section.

    A shape is derived the first time it is used: the outline is scaled to 228.44 units on its longer side and re-centred. Registering a shape gets you far more than an outline.

    PropertyUsed for
    pathThe body
    faceWhere the eyes go and how big they are
    radiusFarthest distance from the centre
    beltRadiusRadius of the particle orbit
    tiltScaleHow far the body may rotate
    spanAt(y)Clamps the face inside the outline
    ringMorphing between shapes, and collapsing to a core
    partsThe pieces outside the body
    shaperadiusbeltRadiustiltScaleface.eyeparts
    mochi118.1114.21.000.87—
    circle114.2114.21.001.00—
    blob125.5112.11.000.87—
    pebble115.9114.20.880.89—
    egg114.2103.91.001.00—
    bun121.2114.21.000.853
    tofu129.5114.20.150.86—
    pill114.2114.20.360.85—
    hex114.4114.10.981.00—
    onigiri136.4114.20.160.85—
    gem119.1114.21.000.851
    cloud120.9113.90.150.85—
    drop115.393.71.000.85—
    leaf149.3103.51.000.851
    flower114.2114.11.000.94—
    clover130.5102.80.320.851
    sprout127.4109.81.000.852
    mushroom115.2114.20.990.85—
    acorn110.3114.21.000.853
    cactus110.8114.20.280.852
    pear114.280.71.000.852
    apple114.4114.21.000.852
    strawberry115.4114.21.000.8510
    cherry130.5114.21.000.864
    star126.6114.20.150.85—
    heart126.1114.21.000.85—
    flame114.786.21.000.85—
    bell138.7114.10.150.851
    ghost151.2110.70.171.00—
    bot145.4108.50.150.852
    crescent125.3110.71.000.85—
    boba120.594.30.240.856
    fish114.2114.21.000.921
    octopus100.5114.21.000.854
    cat137.8114.21.000.85—
    bunny124.4103.10.990.854
    bear123.4114.21.000.864
    chick100.6114.21.000.886
    penguin102.3114.21.000.883
    pig119.1114.21.000.893
    fox124.9114.20.990.857
    bob102.9114.21.000.851
    topknot114.288.01.000.852
    spiky114.292.51.000.851
    curly103.9114.21.000.851
    twinbuns87.4114.21.000.853
    import { getShape, SHAPE_IDS } from '@aora-bot/react';
    
    export default function Demo() {
      return (
        <table>
          <thead>
            <tr>
              <th>shape</th>
              <th>radius</th>
              <th>beltRadius</th>
              <th>tiltScale</th>
              <th>face.eye</th>
              <th>parts</th>
            </tr>
          </thead>
          <tbody>
            {SHAPE_IDS.map((id) => {
              const s = getShape(id);
              return (
                <tr key={id}>
                  <td>
                    <code>{id}</code>
                  </td>
                  <td>{s.radius.toFixed(1)}</td>
                  <td>{s.beltRadius.toFixed(1)}</td>
                  <td>{s.tiltScale.toFixed(2)}</td>
                  <td>{s.face.eye.toFixed(2)}</td>
                  <td>{s.parts.length || '—'}</td>
                </tr>
              );
            })}
          </tbody>
        </table>
      );
    }

    Parts

    Ears, leaves and antennae stay out of the body's main ring. ring (96 points) is the only basis for morphing, face fitting, belts and spanAt; the extras live in ShapeDef.parts and share the body's normalisation (the box is the union of body and parts, the area is the body's alone), so a part never pushes into the margin the animation needs.

    Parts ride the body's pose and never morph. When the shape changes, the old parts fade out and the new ones fade in, so an ear does not vanish halfway through a morph.

    Each part is painted one of four ways:

    tonePainted as
    bodyThe body's own ink, as if part of the outline (bunny ears, antenna, the cactus's arms)
    leafGreen, read from --aora-leaf (a lighter green on a green body)
    innerHalf ink, half white, for the inside of an ear and the bun's creases
    hairA step darker than the ink on a filled variant (lighter on a black body in light mode), solid ink in outline and glass

    hair also steers the face fitter: no eye goes where the bangs are, so a person's face always sits below the hairline.

    Every vector variant draws parts: outline and glass merge them into the body outline and stroke it once, with no seam; the sticker border and the paper hard shadow wrap them too. The pixel series rasterises them onto the same grid.

    buninner, inner, inner
    geminner
    leafbody
    cloverbody
    sproutleaf, leaf
    acornbody, body, inner
    cactusbody, body
    pearbody, leaf
    applebody, leaf
    strawberryleaf, leaf, leaf, inner, inner, inner, inner, inner, inner, inner
    cherrybody, body, body, leaf
    bellbody
    botbody, body
    bobabody, body, inner, inner, inner, inner
    fishbody
    octopusbody, body, body, body
    bunnybody, body, inner, inner
    bearbody, body, inner, inner
    chickbody, body, body, body, body, body
    penguinbody, body, inner
    pigbody, body, inner
    foxbody, body, inner, inner, body, inner, inner
    bobhair
    topknothair, hair
    spikyhair
    curlyhair
    twinbunshair, hair, hair
    import { CharacterMark, SHAPE_IDS, getShape } from '@aora-bot/react';
    
    export default function Demo() {
      return (
        <div className="site-demo">
          {SHAPE_IDS.filter((id) => getShape(id).parts.length > 0).map((id) => (
            <div className="site-demo__cell" key={id}>
              <CharacterMark shape={id} color="orange" state="happy" sizePx={84} />
              <span className="site-demo__label">{id}</span>
              <span className="site-demo__label" style={{ opacity: 0.6 }}>
                {getShape(id)
                  .parts.map((part) => part.tone)
                  .join(', ')}
              </span>
            </div>
          ))}
        </div>
      );
    }

    defineShape takes a parts option too, with the same four tones. See Custom shapes.

    Why flat-bottomed shapes barely tilt

    A flat-bottomed shape looks wrong when it tips — the base lifts and the sense of weight goes with it. So tiltScale is measured from the outline, not configured: tofu, cloud, star, bell and bot come out at 0.15 and rock rather than topple; ghost and onigiri land just above, and mochi and blob come out at 1 and tilt freely; no shape carries a hand-set tilt coefficient. How it is measured: Shape derivation.

    Adding your own

    Register a function that returns points with defineShape, and everything in the table above is derived for you. The full walkthrough is in Custom shapes.

    Next