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

Motion graphics — move ideas, not layers

Motion Graphics V2 is built around continuity. Keep one object alive while it moves, follows another object, changes visual form, joins a layout, or turns as part of a system. The timeline remains deterministic and freely scrubbable.

There is no V2 mode or production flag. Write an ordinary .manic file and use the relationship only where the story needs it.

The complete everyday vocabulary

Creator intentManicResult
Keep one object beside anotherattach(child, target, [(dx,dy)])the child follows every resolved target position
Stop followingattach(child, none)the child releases at the settled position without a second verb
Change visual identitybecome(source, blueprint, [dur], [ease])the source keeps its id and settles exactly on the blueprint
Turn a whole arrangementturn(id_or_tag, pivot, degrees, [dur], [ease])every member follows the same circular pivot motion
Move a real object on a pathtravel(object, path, dur, ease)the object arrives and remains at the endpoint
Send temporary path emphasisflow(path, dur)a pulse passes without moving an authored object
Add contained ambient lifewander(particles, dur)seeded, repeatable motion within the container
Reorganize the same particles`arrange(particles, region, “randomgrid
Change an ordinary propertyto(id, property, value, dur, ease)the general escape hatch remains available

The three V2 words add relationships; they do not replace move, travel, flow, arrange, spin, transform, rewrite, or morph.

attach — author the relationship

dot(marker, (180,620), 8);
text(readout, (180,580), "sample A");
plot(curve, (180,620), 90,140,"1-exp(-x)",(0,4));

attach(readout, marker, (0,-40));
travel(marker, curve, 2, smooth);
attach(readout, none); // release at the settled endpoint

The child follows after normal tracks, reactive bindings, derived geometry, links, particle layouts, and path travel have resolved. Its opacity is multiplied by the target opacity, so a label naturally disappears with the thing it explains.

Use an offset to keep the child readable. Release at the end of a movement, then move, fade, or reuse the child normally. Attachment cycles are rejected while building the movie rather than failing during rendering.

become — preserve identity across a visual change

Declare the destination like any other entity and hide it when it is only a blueprint:

circle(seed, (220,700), 16); color(seed, cyan);
circle(node, (820,700), 62); color(node, magenta);
outlined(node); stroke(node, 7); hidden(node);

become(seed, node, 0.9, smooth);

Compatible geometry interpolates continuously, including circle, rectangle, line, arrow, curve, coil, arc, and equal-topology polygon/polyline pairs. Unsupported pairs use a local fade/swap/fade instead of producing broken geometry. Both paths settle on the exact target geometry and styling while the source id remains alive. A hidden blueprint does not make the transformed source disappear, and the blueprint’s own visibility is never changed.

Use rewrite for equations because it understands matching LaTeX visual parts. Use morph plus to(..., morph, ...) when you explicitly need a fraction-driven point morph or winding angle.

turn — rotate the system, not every member

circle(orbit, (540,700), 220); hidden(orbit);
particles(dots, orbit, 16, 7, 42, "ring");

turn(dots, orbit, 24, 0.65, out);

The first argument may be one entity or a tag. The pivot may be a point or an entity. Positions follow circular paths, path endpoints and curve controls turn with their paths, and ordinary shapes retain their group-local orientation. Because the target is resolved from the latest authored state, turn composes after move, travel, arrange, or an earlier turn without snapping back.

Use spin for one object’s in-place rotation. Use transform when the actual idea is a matrix, shear, reflection, or other precise linear map.

travel versus flow

par {
  draw(curve, 2, out);
  travel(marker, curve, 2, out); // the marker moves and stays
  flow(curve, 1);                // temporary travelling emphasis
}

Use travel for a vehicle, probe, token, particle, or graph marker. Use flow for energy, attention, traffic, or a signal that should disappear after passing.

Complete V2 example

This generic Reel demonstrates all three relationship words, release, path travel, an identity-preserving blueprint change, particle arrangement, a shared pivot turn, and a readable final hold:

// motion-graphics-v2.manic — relationships, identity, and a deliberate settle

title("Motion Graphics V2 — Follow, Become, Turn");
canvas("9:16");
template("mono");
watermark(manicMark, (150, 132), "Made With Manic");

creator(me, "@anish2good name=Manic_Motion tagline=Describe_the_relationship yt=zarigatongy x=@anish2good web=8gwifi.org/manic accent=cyan secondary=magenta footer=social cta=Create_yours safe=reels");
socials(me);

text(kicker, (540, 155), "MOTION GRAPHICS V2");
size(kicker, 23); color(kicker, dim); bold(kicker);
text(headline, (540, 228), "Move the idea, not every coordinate");
size(headline, 40); bold(headline);
text(subhead, (540, 292), "attach · become · turn");
size(subhead, 25); color(subhead, dim);

rect(stage, (540, 865), 900, 1020);
color(stage, panel); outline(stage, dim); opacity(stage, 0.78);

// One authored route and one persistent object.
plot(route, (205, 1040), 172, 190, "0.55*sin(1.2*x)+0.18*x", (0, 4.25));
color(route, cyan); stroke(route, 6); untraced(route);

dot(marker, (205, 1040), 12);
color(marker, gold); glow(marker, 1.6);
text(markerLabel, (205, 994), "one persistent object");
size(markerLabel, 24); color(markerLabel, fg); hidden(markerLabel);

// A hidden target is a visual blueprint; it never appears as a second object.
circle(nodeBlueprint, (935, 885), 56);
color(nodeBlueprint, magenta); outlined(nodeBlueprint); stroke(nodeBlueprint, 7);
glow(nodeBlueprint, 1.2); hidden(nodeBlueprint);

// Supporting particles begin offstage, then arrive as an ordered ring.
rect(seedBox, (540, 610), 260, 180); hidden(seedBox);
particles(sparks, seedBox, 14, 7, 42, "random");
color(sparks, cyan); hidden(sparks);
circle(finalOrbit, (935, 885), 138); hidden(finalOrbit);

text(explain, (540, 1325), "The label follows because the relationship is authored.");
size(explain, 26); color(explain, dim); wrap(explain, 800); hidden(explain);

step("attach-and-travel") {
  seq {
    attach(markerLabel, marker, (0,-46));
    par {
      show(markerLabel, 0.35);
      show(explain, 0.40);
      draw(route, 2.8, smooth);
      travel(marker, route, 2.8, smooth);
    }
    wait(0.45);
  }
}

step("become-the-next-idea") {
  seq {
    par {
      become(marker, nodeBlueprint, 1.05, smooth);
      say(markerLabel, "same id · new visual state", 0.45);
      say(explain, "become preserves identity and settles on the exact blueprint.", 0.45);
    }
    wait(0.50);
  }
}

step("release-arrange-turn") {
  seq {
    attach(markerLabel, none);
    par {
      fade(markerLabel, 0.35);
      fade(route, 0.45);
      show(sparks, 0.30);
      arrange(sparks, finalOrbit, "ring", 1.15, smooth);
      say(explain, "Release the label. Arrange a system. Turn it around one pivot.", 0.45);
    }
    turn(sparks, marker, 24, 0.65, out);
    wait(1.40);
  }
}

Run it directly—no extra runtime option is required:

manic examples/motion-graphics-v2.manic

Advanced story — compose the whole motion language

The compact example above answers “what do the three new words do?” This advanced Reel answers the more important creator question: “how do they form a story with the motion vocabulary I already know?”

It keeps one visual world alive across three acts:

  1. A question travels while its label stays attached; a path pulse guides the eye and surrounding facts wander.
  2. The notation rewrites, the same question becomes a model, and the same facts arrange into a system. The model then spins locally.
  3. Labels and particles turn around one shared pivot while the equation reaches the story’s final meaning.

seq creates cause and effect, par groups changes that express one idea, and stagger prevents a dense scene from arriving as a visual shock.

// motion-graphics-v2-story.manic — one idea, one continuous motion story

title("The Journey of an Idea — Motion Graphics V2 Story");
canvas("9:16");
template("mono");
watermark(manicMark, (170, 178), "Made With Manic");

creator(me, "@anish2good name=Manic_Motion tagline=Move_the_idea yt=zarigatongy x=@anish2good web=8gwifi.org/manic accent=cyan secondary=magenta footer=social cta=Create_yours safe=reels");
socials(me);

// ---------------------------------------------------------------------------
// ONE PERSISTENT STAGE
// The scene never cuts away. Each act reuses what the previous act established.
// ---------------------------------------------------------------------------

text(kicker, (540, 184), "A MOTION GRAPHICS STORY");
size(kicker, 22); color(kicker, dim); bold(kicker); hidden(kicker);

text(headline, (540, 248), "How an idea learns to move");
size(headline, 42); bold(headline); hidden(headline);

text(chapter, (540, 316), "ACT 1 · FOLLOW ATTENTION");
size(chapter, 24); color(chapter, cyan); bold(chapter); hidden(chapter);

rect(stage, (540, 850), 900, 990);
color(stage, panel); outline(stage, dim); opacity(stage, 0.76);

text(narration, (540, 1400), "A question appears before the answer does.");
size(narration, 28); color(narration, fg); wrap(narration, 820); hidden(narration);

text(principle, (540, 1495), "Motion should guide the eye, preserve meaning, then settle.");
size(principle, 22); color(principle, dim); wrap(principle, 820); hidden(principle);

// ---------------------------------------------------------------------------
// ACT 1 — CURIOSITY TRAVELS; ITS CONTEXT FOLLOWS
// ---------------------------------------------------------------------------

spline(questionPath, (175, 1080), (290, 980), (385, 1060), (490, 900), (540, 790));
color(questionPath, cyan); stroke(questionPath, 5); dashed(questionPath, 18, 15);
glow(questionPath, 0.8); untraced(questionPath);

dot(idea, (175, 1080), 12);
color(idea, gold); glow(idea, 1.7); hidden(idea);

text(focusLabel, (175, 1024), "WHY?");
size(focusLabel, 29); color(focusLabel, gold); bold(focusLabel); hidden(focusLabel);

// Facts begin as visual noise. They persist through every later arrangement.
rect(noiseField, (540, 820), 690, 470); hidden(noiseField);
particles(facts, noiseField, 16, 7, 29, "random");
color(facts, cyan); glow(facts, 0.9); hidden(facts);

for i in 0..16 {
  tag(facts.p{i}, knowledgeSystem);
}

// ---------------------------------------------------------------------------
// ACT 2 — THE SAME QUESTION BECOMES A MODEL
// ---------------------------------------------------------------------------

equation(work, (540, 1195), `1+2+\cdots+n=?`, 43);
color(work, fg); hidden(work);

// Hidden entities are visual blueprints. `idea` adopts this exact final state.
rect(modelBlueprint, (540, 790), 116, 116);
outlined(modelBlueprint); outline(modelBlueprint, magenta); stroke(modelBlueprint, 7);
rot(modelBlueprint, 45); glow(modelBlueprint, 1.5); hidden(modelBlueprint);

circle(knowledgeOrbit, (540, 790), 250); hidden(knowledgeOrbit);

text(observe, (540, 492), "OBSERVE");
text(connect, (842, 790), "CONNECT");
text(testIdea, (540, 1088), "TEST");
text(explain, (238, 790), "EXPLAIN");

size(observe, 20); size(connect, 20); size(testIdea, 20); size(explain, 20);
color(observe, dim); color(connect, dim); color(testIdea, dim); color(explain, dim);
bold(observe); bold(connect); bold(testIdea); bold(explain);
hidden(observe); hidden(connect); hidden(testIdea); hidden(explain);
tag(observe, knowledgeSystem); tag(connect, knowledgeSystem);
tag(testIdea, knowledgeSystem); tag(explain, knowledgeSystem);

// ---------------------------------------------------------------------------
// ACT 3 — THE MODEL BECOMES A COORDINATED SYSTEM
// ---------------------------------------------------------------------------

text(systemLabel, (540, 648), "ONE IDEA · MANY RELATIONSHIPS");
size(systemLabel, 22); color(systemLabel, magenta); bold(systemLabel); hidden(systemLabel);

text(finalLine, (540, 1320), "Don't animate layers. Animate the relationship.");
size(finalLine, 30); color(finalLine, fg); bold(finalLine); wrap(finalLine, 820); hidden(finalLine);

// ---------------------------------------------------------------------------
// TIMELINE — seq tells the story; par and stagger control the viewing rhythm.
// ---------------------------------------------------------------------------

step("a-question-pulls-attention") {
  seq {
    par {
      show(kicker, 0.35);
      show(headline, 0.55);
      show(chapter, 0.40);
      show(narration, 0.45);
      show(principle, 0.45);
      show(idea, 0.30);
    }

    // Stagger turns a cloud into a readable arrival instead of one visual shock.
    stagger(0.035) {
      for i in 0..16 {
        show(facts.p{i}, 0.22);
      }
    }

    attach(focusLabel, idea, (0,-56));
    par {
      show(focusLabel, 0.30);
      draw(questionPath, 2.65, smooth);
      travel(idea, questionPath, 2.65, smooth);
      flow(questionPath, 1.45);
      wander(facts, 2.65);
    }

    par {
      to(idea, scale, 1.55, 0.45, out);
      say(narration, "Attention follows the question while the background stays alive.", 0.45);
    }
    wait(0.55);
  }
}

step("the-question-becomes-a-model") {
  seq {
    attach(focusLabel, none);
    par {
      fade(focusLabel, 0.30);
      fade(questionPath, 0.45);
      say(chapter, "ACT 2 · PRESERVE THE IDEA", 0.35);
      say(narration, "The question changes form, but it never loses its identity.", 0.45);
      show(work, 0.40);
    }

    // Existing notation moves only where the mathematical meaning changes.
    rewrite(work, `1+2+\cdots+n=\frac{n(n+1)}{2}`, 1.00, smooth);

    par {
      become(idea, modelBlueprint, 1.05, smooth);
      arrange(facts, knowledgeOrbit, "ring", 1.20, smooth);
      say(principle, "become keeps one id · arrange keeps every particle", 0.45);
    }

    // spin is local: the new model turns around its own centre.
    spin(idea, 90, 0.65, out);

    stagger(0.10) {
      show(observe, 0.30);
      show(connect, 0.30);
      show(testIdea, 0.30);
      show(explain, 0.30);
    }
    wait(0.55);
  }
}

step("relationships-move-as-one") {
  seq {
    attach(systemLabel, idea, (0,-142));
    par {
      show(systemLabel, 0.35);
      say(chapter, "ACT 3 · MOVE THE SYSTEM", 0.35);
      say(narration, "Now every part can move together without losing the structure.", 0.45);
      show(finalLine, 0.45);
    }

    // turn is relational: particles and labels share one pivot and one motion.
    par {
      turn(knowledgeSystem, idea, 45, 1.20, smooth);
      spin(idea, -135, 1.20, smooth);
      to(facts, color, lime, 1.20, smooth);
      rewrite(work, `\text{question}\;\longrightarrow\;\text{model}\;\longrightarrow\;\text{insight}`, 1.20, smooth);
    }

    par {
      pulse(idea, 0.70);
      say(principle, "FOLLOW · TRANSFORM · COORDINATE · SETTLE", 0.45);
    }
    wait(1.80);
  }
}

Run the advanced story through the same file-only production path:

manic examples/motion-graphics-v2-story.manic

The original essentials example remains useful when learning wander, travel, explicit morph, and particle arrangement:

// motion-graphics.manic — persistent objects, paths, particles, and a clean settle

title("Motion Graphics Essentials");
canvas("9:16");
template("mono");
watermark(manicMark, (150, 145), "Made With Manic");

let accent = 194;
let warm = 38;

text(kicker, (540, 170), "MOTION GRAPHICS");
size(kicker, 24); color(kicker, dim);
text(headline, (540, 245), "Keep the object. Change its state.");
size(headline, 42);

rect(chamber, (540, 535), 700, 300);
outline(chamber, dim); outlined(chamber); stroke(chamber, 3);
rect(startCell, (350, 535), 290, 270); hidden(startCell);
particles(dots, startCell, 36, 6, 23);
hue(dots, accent, 0.58, 0.72); glow(dots, 0.7);
text(note, (540, 835), "random layout · organic deterministic routes");
size(note, 25); color(note, dim);

plot(path, (245, 1120), 145, 175, "1-exp(-1.4*x)", (0, 3.8));
hue(path, warm, 0.60, 0.70); stroke(path, 5); untraced(path);
dot(marker, (245, 1120), 9); color(marker, fg); glow(marker, 1.0);
text(pathNote, (540, 1215), "travel moves the real marker — then it stays");
size(pathNote, 25); color(pathNote, dim);

line(connector, (245, 1120), (835, 850)); hidden(connector);
plot(pathMorph, (245, 1120), 145, 175, "1-exp(-1.4*x)", (0, 3.8));
hue(pathMorph, warm, 0.60, 0.70); stroke(pathMorph, 5);
morph(pathMorph, connector); hidden(pathMorph);

circle(orbit, (540, 535), 245); hidden(orbit);

step("scatter") {
  seq {
    par {
      seq { arrange(dots, chamber, "random", 1.25, out); wander(dots, 1.75); }
      seq { wait(0.25); say(note, "same dots · natural motion · repeatable render", 0.45); }
    }
  }
}

step("follow-a-path") {
  seq {
    par {
      draw(path, 2.4, smooth);
      travel(marker, path, 2.4, smooth);
    }
    wait(0.45);
  }
}

step("preserve-the-path") {
  seq {
    par {
      fade(path, 0.20);
      show(pathMorph, 0.10);
      to(pathMorph, morph, 1, 0.90, smooth);
      say(pathNote, "morph keeps an open path open", 0.45);
    }
    wait(0.45);
  }
}

step("arrive-and-settle") {
  seq {
    par {
      arrange(dots, orbit, "ring", 1.15, smooth);
      fade(chamber, 0.35);
      seq { wait(0.45); say(note, "ring arrival", 0.35); }
    }
    par {
      transform(dots, (540,535), 0.9659, -0.2588, 0.2588, 0.9659, 0.50, out);
      say(note, "brief motion → a deliberate stop", 0.35);
    }
    wait(1.20);
  }
}

Professional motion checklist

  • Author relationships and final states; avoid hand-keyframing intermediate coordinates.
  • Use par for changes that belong to one idea and seq for cause-and-effect.
  • Prefer smooth for explanatory transformations and out for a short settle.
  • Keep an important object id alive instead of fading it out and rebuilding it.
  • Leave a final wait so the audience can read the state motion created.
  • Preview by named step and scrub backwards: the same time must always produce the same frame.