Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Timing — flow, named phases & clocks

By default, verbs play one after another. Three wrappers change that — they turn “then, then, then” into “together” or “cascading”.

wrapperplays its steps…use for
(nothing)one after anotherthe normal flow
par { … }all at the same instantreveal a group at once
seq { … }one after another (explicit)grouping inside a par
stagger(d) { … }each one d seconds after the lastcascades / waves
step("name") { … }all at once, with a named exported startchange one reactive world across representations
show(a);  show(b);              // a, THEN b
par    { show(a);  show(b); }   // a and b together
stagger(0.1) { show(a); show(b); show(c); }  // a, then b 0.1s later, then c…

Put a for loop inside one and it just works — the loop expands first, so all its statements land in the wrapper:

par          { for i in 0..6 { show(a{i}); } }   // whole row at once
stagger(0.1) { for i in 0..6 { show(b{i}); } }   // whole row cascading
// the same reveal, three ways: sequence, together, cascade.
title("Timing");  canvas("16:9");
text(t, (cx, 110), "seq  ·  par  ·  stagger");  color(t, cyan);  size(t, 30);  hidden(t);

for i in 0..6 { dot(a{i}, (220 + i*160, 300), 16);  color(a{i}, cyan);     hidden(a{i}); }
for i in 0..6 { dot(b{i}, (220 + i*160, 470), 16);  color(b{i}, magenta);  hidden(b{i}); }

show(t, 0.5);

// top row, all at the same instant
par { for i in 0..6 { show(a{i}); } }
wait(0.5);

// bottom row, cascading 0.1s apart
stagger(0.1) { for i in 0..6 { show(b{i}); } }
wait(1.0);

▶ See it play:

Beats & sections

Two more timing words structure a longer video:

wait(1.2);              // hold — nothing moves for 1.2s
section("Part Two");    // a titled marker (jump to it in preview with keys 1–9;
                        //   also exported for lining up narration)
mark("beat-3");         // a named timestamp for your editor

wait is your friend for pacing — a beat of stillness after something lands reads far better than rushing to the next move.

Named reactive steps

Use step when a beat represents the world’s next conceptual state, not just anonymous timing. Its children start together like par; its duration is the longest child; anything not mentioned remains exactly as it was. The unique name is written to markers.json and becomes a first-class editing boundary: manic stages FILE.manic lists durations, --stage NAME previews or records one stage, and --from-stage / --to-stage export an inclusive arc.

step("explain") {
  rewrite(work, `f'(x)=2x`, 0.9, smooth);
  to(tangent, x, 2.5, 2.0, smooth);
  to(slopeValue, x, 2.5, 2.0, smooth);
  say(caption, "Every representation changes together.");
}

Steps are top-level and names must be non-empty and unique. Put seq { … } inside a step when a small part needs ordered choreography.

An authored wait after a step remains part of that stage until the next step begins. This lets a stage-only export keep the reading hold after its motion. See Story stages for the live navigator and publishing workflow.

Generic Timing v2 — one clock for any scene

Use a generic timing controller when several parts of a scene must share one exact schedule. It is format-neutral: the same controller can coordinate a physics simulation, geometry proof, chart, caption sequence, or ordinary shapes.

Think of it as four small pieces:

pieceresponsibility
timingdeclares the phase names, durations, exact total, and optional clock position
timerstylechanges only the visible clock; it never changes scene timing
timedruns the clock and schedules the complete phase contract
duringcontains the ordinary animation actions for one named phase

The phase declaration is the source of truth. The clock is only one visual view of that choreography.

Quick reference

formuse
timing(clock,"intro=1 demo=6 result=2")declare named phases
timing(clock,(1160,80),"...")declare phases and set the initial clock position
timing(clock,"duration=6")shorthand for one phase named main
timerstyle(clock,"...")change appearance without changing timing
timerstyle(clock,(1160,80),"...")restyle and reposition the clock
timed(clock) { ... }play the complete phase schedule and clock
during("phase") { ... }author one phase inside timed
run(clock)play the clock alone
countdown(id,[at],[seconds],["style"])independent countdown without named phases

Core pattern, after defining the referenced entities and simulation:

timing(clock, (1160,80), "intro=1 demo=6 result=2");
timerstyle(clock, "look=ring number=inside color=cyan");

timed(clock) {
  during("intro")  { show(title, 0.6); }
  during("demo")   { par { run(sim, 6); draw(sim.path, 6); } }
  during("result") { show(answer, 0.6); }
}

Use countdown when you only need an independent visual countdown. Use timing when named phases must govern other animation.

Compose inside phases

Each during block accepts the usual timeline language. In particular, use par for actions that must occupy the same phase together:

timing(clock, "setup=1 experiment=6 result=2");

timed(clock) {
  during("setup") { show(title, 0.6); }

  during("experiment") {
    par {
      run(pendulum, 6);
      draw(pendulum.path, 6);
      karaoke(caption, 0.35);
    }
  }

  during("result") {
    par {
      show(formula, 0.6);
      show(explanation, 0.6);
    }
    wait(1.4);
  }
}

Rules that prevent drift

  • Give a generic controller a fresh id. Do not reuse an entity, quiz, simulation, or group id.
  • Put constructors and style modifiers outside timed/during; put timeline actions such as show, draw, run, wait, par, and seq inside.
  • A short phase block is padded automatically. A block that exceeds its phase is an error.
  • A phase may have at most one during block. Combine related work inside it with par or seq.
  • An omitted phase is valid and becomes a blank hold.
  • Phase blocks may appear in any source order: they are placed at the absolute offsets declared by timing.
  • timed(clock) already runs the clock. Do not also call run(clock) inside it.
  • run(clock) is timer-only playback. run(clock, dur) is rejected because the phase declaration already owns the duration.

Choose the clock’s look

The clock uses native manic shapes, so it stays sharp at any output size and can be targeted like the rest of the scene. No SVG workflow is required.

lookbest fit
ringneutral default; compact and familiar
barlong processes or wide layouts
segmentsenergetic stages and presentations
ticksprecise, technical, or measurement-led scenes
numberminimal layouts where the value is enough
pulseshort, urgent moments; use sparingly
nonekeep exact phase choreography without showing a clock

The main controls are:

  • number=inside|outside|none and direction=fill|drain
  • size=small|medium|large|0.5..2.0 and thickness=0.4..3.0
  • color, track, label, and font=mono|display
  • finish=fade|hold|flash|pulse for the completion cue

Start with the default ring, then change the look only when it supports the scene’s meaning. For legibility, keep strong contrast between color and track, and avoid combining a busy clock with dense content in the same corner.

Advanced styling can target the stable tags clock.timer, clock.timer.track, clock.timer.progress, clock.timer.value, clock.timer.label, and clock.timer.effects (replace clock with the controller id).

Generic controller or Creator quiz?

The same visual clock system serves two different timing contracts:

  • timing(fresh_id, "phase=seconds ...") creates a generic controller used by timed and during.
  • timing(quiz_id, "calm ask=... think=... reveal=...") configures a Creator quiz and is played with run(quiz_id).

Do not wrap a quiz in timed; the quiz runner already owns its ask, options, think, reveal, hold, and stagger phases.

Common fixes

message or symptomfix
controller id is already in usechoose a fresh id for generic timing
a phase overrunsshorten its sequence, compose simultaneous work with par, or increase the declared phase
unknown phasematch the name in during to the timing declaration
duplicate phase blockcombine the work into one during block
competing durationremove the duration argument from run(clock, dur)
timer appears twiceremove run(clock) from inside timed(clock)

Complete non-quiz example using a pendulum, caption, formula, and segmented clock:

// Generic Timing v2 — one named clock coordinates a non-quiz physics scene.
// `timed` runs the visual timer and places every `during` block at the exact
// offset declared by `timing`; short blocks are padded, overruns are errors.
title("Generic Timing v2 — Pendulum");
canvas("16:9");
template("mono");
watermark(manicMark, (w*0.955-100, h*0.045+24), "Made With Manic");

text(head, (315, 72), "ONE CLOCK. EVERY BEAT.");
size(head, 30); bold(head); color(head, fg); hidden(head);
text(sub, (315, 112), "Generic Timing v2 — not a quiz");
size(sub, 18); color(sub, dim); hidden(sub);

pendulum(p, (640, 245), 1.5, 42, 140, 0.03);
untraced(p.path);

equation(law, (640, 602), `T\approx2\pi\sqrt{L/g}`, 54);
color(law, gold); hidden(law);
text(note, (640, 655), "The scene and clock share the same named phases.");
size(note, 21); color(note, dim); hidden(note);
text(done, (640, 680), "INTRO  →  EXPERIMENT  →  INSIGHT  →  OUTRO");
size(done, 18); color(done, cyan); hidden(done);

// A fresh id creates a format-neutral timing controller. Its phases total
// 10.4 seconds; no duplicate duration is passed to `timed` or `run`.
timing(showclock, (970, 86), "intro=1.2 experiment=6 insight=2 outro=1.2");
timerstyle(showclock,
           "look=segments number=outside direction=drain size=small thickness=1.15 color=cyan track=dim label=MASTER_CLOCK font=display finish=pulse");

timed(showclock) {
  during("intro") {
    par { show(head, 0.7); show(sub, 0.7); }
  }

  during("experiment") {
    par { run(p, 6); draw(p.path, 6); }
  }

  during("insight") {
    par { show(law, 0.6); show(note, 0.6); }
    pulse(p.bob, 0.7);
  }

  during("outro") {
    show(done, 0.6);
  }
}

Next: the palette, glow, and easings → Colour & style.