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 intent | Manic | Result |
|---|---|---|
| Keep one object beside another | attach(child, target, [(dx,dy)]) | the child follows every resolved target position |
| Stop following | attach(child, none) | the child releases at the settled position without a second verb |
| Change visual identity | become(source, blueprint, [dur], [ease]) | the source keeps its id and settles exactly on the blueprint |
| Turn a whole arrangement | turn(id_or_tag, pivot, degrees, [dur], [ease]) | every member follows the same circular pivot motion |
| Move a real object on a path | travel(object, path, dur, ease) | the object arrives and remains at the endpoint |
| Send temporary path emphasis | flow(path, dur) | a pulse passes without moving an authored object |
| Add contained ambient life | wander(particles, dur) | seeded, repeatable motion within the container |
| Reorganize the same particles | `arrange(particles, region, “random | grid |
| Change an ordinary property | to(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:
- A question travels while its label stays attached; a path pulse guides the eye and surrounding facts wander.
- The notation rewrites, the same question becomes a model, and the same facts arrange into a system. The model then spins locally.
- 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
parfor changes that belong to one idea andseqfor cause-and-effect. - Prefer
smoothfor explanatory transformations andoutfor a short settle. - Keep an important object id alive instead of fading it out and rebuilding it.
- Leave a final
waitso the audience can read the state motion created. - Preview by named
stepand scrub backwards: the same time must always produce the same frame.