Builder Guide

Cutscene Timelines

Author frame-by-frame ASCII cinematics — tracks, keyframes, layered color art, motion, and the damage cue.

4 min read Summons summons animation ascii-art timelines

The timeline file is where a summon becomes theatre. It describes an animation as tracks of keyframes, each keyframe placing a block of ASCII art at a position, in a color, for a number of frames. The engine compiles this into playback data and draws it over the battlefield.

The canvas

Cutscenes draw inside the battle field window on a canvas of roughly 132 columns by 27 rows, addressed from the top-left corner. Party members stand on the right, enemies on the left — so the strongest staging summons your creature on the right and sends its attack travelling right to left.

The file shape

return [
  'formatVersion' => 1,
  'fps' => 12,
  'lengthFrames' => 108,        // 9 seconds at 12 fps
  'tracks' => [
    [
      'type' => 'glyph',        // or 'text'
      'id' => 'my-track',
      'keyframes' => [
        [
          'frame' => 12,        // when it appears
          'duration' => 6,      // how many frames it stays
          'content' => $art,    // single- or multi-line string
          'position' => [88, 5], // [x, y] on the canvas
          'color' => 'brown',
          'zIndex' => 3,
        ],
      ],
    ],
  ],
  'cues' => [
    ['id' => 'apply_ifrit_damage', 'frame' => 71, 'type' => 'applyEffect'],
  ],
];

Because the file is plain PHP, define your art in nowdoc strings above the return — backslashes survive untouched, which matters a great deal for ASCII art:

$eagleHead = <<<'ART'
  ____
=<  o \
 \____/\
ART;

Leading spaces inside art blocks are preserved, so draw your art exactly as it should appear.

Colors are named: red, light_red, yellow, brown, green, light_green, cyan, light_cyan, blue, light_blue, purple, light_purple, white, light_gray, dark_gray, black.

Layering and the start-frame rule

Within a frame, segments draw in a fixed order: earlier start frame first, then lower z-index first. Later draws overwrite earlier ones — including their interior spaces.

This has one important consequence: a long-lived overlay is painted over by any layer beneath it that restarts later. If your creature's brown body re-draws every few frames (a wing beat, a pawing hoof) and its white head is a single long keyframe, the body will erase the head on every beat.

The fix is a pattern worth learning once: restart your color overlays in lockstep with the layer they sit on. Give the head the same start frames and durations as the body poses, with a higher z-index. Same start frame, higher z — the overlay wins every time.

// body poses alternate every 6 frames …
['frame' => 12, 'duration' => 6, 'content' => $bodyA, 'zIndex' => 3, /* … */],
['frame' => 18, 'duration' => 6, 'content' => $bodyB, 'zIndex' => 3, /* … */],
// … so the white head restarts on the same beats, one layer up
['frame' => 12, 'duration' => 6, 'content' => $head, 'zIndex' => 4, /* … */],
['frame' => 18, 'duration' => 6, 'content' => $head, 'zIndex' => 4, /* … */],

This is how a brown bull gets white horns and a burning red eye, or a brown fish eagle gets its white head and yellow talons — the base art carries the silhouette, and small exact-position patches recolor its features.

Motion techniques

Everything moves by keyframes; a few idioms cover nearly all of it.

Flicker — alternate two art variants of the same shape on a short cadence: two flame-mane drawings, two wing poses, two swirl rotations (swap the / and \ rows). Vary the color every few beats for extra life.

Travel — a projectile is one art block re-keyed at advancing positions, two frames per step: a fireball at x 68 → 55 → 42 → 30 → 19. Grow the trail as it flies and let it climb or descend a row every few steps.

Impact staging — the classic beat: a small white flash (2 frames), an expanding burst (3), the full blast at peak (3, flashed white), then the aftermath — plumes, debris, settling dust in darkening colors.

Rumble — jitter a block's x position by one column between keyframes and it reads as shaking earth.

Entrances and exits — creatures should arrive (a growing fire column, a rising geyser, rubble bursting) and leave (a burst of feathers, sinking back into the ground). One or two staged keyframes each way is enough.

The damage cue

The cues array schedules the moment damage applies. The summon's data file points at it by id (effectTiming.cueId), and the engine fires the skill's effects at exactly that frame:

'cues' => [
  ['id' => 'apply_ifrit_damage', 'frame' => 71, 'type' => 'applyEffect'],
],

Put the cue on your impact's peak frame — the flash-white blast, the tornado's full height, the wave's crash. Damage numbers landing inside the explosion is the difference between a cutscene and an attack.

A working rhythm

At 12 fps, 108 frames is nine seconds — a good default length. A structure that consistently reads well:

Frames Beat
0–11 Ambience: embers, ripples, tremors, wind
12–35 The summon arrives and idles (flicker cadence)
36–55 The attack charges — something grows in its grasp
56–65 Launch: the attack travels across the field
66–82 Impact, peak (cue), aftermath
84–95 The summon departs
92–107 The field settles

Author art in any editor, keep every block inside the canvas, and iterate: the compiled cutscene is deterministic, so you can render single frames to plain text and inspect them without launching a battle. Stage, don't rush — the pause before the launch is what makes the launch land.