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

Coming from Manim to Manic

If you’ve written Manim scenes, you already think the right way: a scene is a sequence of things that appear, move, and change over time. Manic keeps that mental model and drops most of the plumbing. This page is a translation guide — what your favourite Manim moves look like in Manic, and where the two languages genuinely differ.

Every scene mentioned here is a runnable file in the gallery under Coming from Manim. Read this page for the why; open those for the how.

Manic is not “Manim with different syntax.” It’s a small declarative language built for people who aren’t programmers. Where Manim hands you Python objects and asks you to wire them together, Manic gives you a builtin that already knows the intent. Most reactive rigs — a ValueTracker feeding an updater feeding an always_redraw — collapse to a single verb. The examples below are the same tutorial scenes you already know, written the Manic way.

The three big differences

1. No ValueTracker, no add_updater, no always_redraw. This is the headline. In Manim, “keep this line connected to those two dots as they move” is a hand-written updater. In Manic, a segment between two points is reactive by construction — it reflows to its endpoints every frame because the constraint pass runs for you. You rarely write the reactive glue; you state the relationship once and animate the inputs.

point(a, (200, 400)); point(b, (600, 300));
segment(l, a, b);            // stays connected — no updater
slidex(a, 840, 2);           // move one dot; the line follows

2. The timeline is deterministic and scrubbable. Animations in Manic resolve to a pure function of time t. A motion trail (trail) isn’t accumulated frame-by-frame like a Manim updater that appends points — it’s sampled from the resolved timeline, so scrubbing backwards shows exactly the trail up to that moment. You get correct reverse playback for free.

3. You say what happens, not how to redraw it. Manim’s self.play(Create(x)), Transform, Restore, MoveAlongPath are render instructions. Manic’s verbs — show, draw, move, to, turn, grow, fade — are intentions, sequenced with wait and grouped with par { }.

The translation table

ManimManicNotes
Axes(x_range, y_range, tips=False)coords(id, origin, xrange, yrange, sx, sy, 0)Arbitrary ranges, per-axis scale, tips/ticks/auto-numbers, and axis names — one call. Align a plot to the same origin+sx+sy.
ax.plot(lambda x: 4*x - x**2)plot(id, origin, sx, sy, "4*x - x*x", (0,4))Formula strings; x*x or x^2.
custom / non-numeric tick labelsxtick(id, coords, value, [text]) / ytick(…)Label a tick at an axis value — a number, a word ("Mon"), or a symbol ("π/2").
ax.get_vertical_line(ax.i2gp(2, curve))vline(id, curve, x, [color])Lands exactly on the graph; dotted/dashed/solid.
ax.get_riemann_rectangles(curve, …, dx)riemann(id, curve, a, b, [dx], [color])The discrete analogue of area.
ax.get_area(g, [a,b], bounded_graph=…)band(top, bottom, [color], (a,b))Fills only the x-slice between two curves.
ax.get_graph_label(graph, "\sin x", …)graphlabel(id, curve, `\sin x` , x, up)LaTeX label pinned to the curve, coloured to match.
Dot + add_updater(move_to(c2p(t, f(t))))mark(id, curve, x) then to(id, x, …)A dot that rides a curve — no updater.
always_redraw(Rectangle origin→(t, k/t))boxto(id, curve, x)Reactive inscribed rectangle; the constant-area demo in one line.
MathTex("a","+","b") (indexable)mathparts(id, (x,y), `a` , `+` , `b` )Auto-laid-out, baseline-aligned, addressable parts {id}.0, {id}.1, …
Circle() / Square() (a stroke-first ring)circle(id, (x,y), r); outlined(id)outlined drops the fill → a ring; filled reverts. Manic’s default is a filled disc, so opt into the ring with outlined.
circle.set_fill(PINK, opacity=0.5)color(id, pink); opacity(id, 0.5, fill)color sets the fill colour; the fill layer word gives it its own opacity, independent of the rim.
circle.set_stroke(BLUE, width=4, opacity=0.6)outline(id, blue); stroke(id, 4); opacity(id, 0.6, stroke)The stroke/outline layer word gives the rim its own opacity. On a hollow shape, colour the ring with outline, not color.
self.play(Create(circle))untraced(id); draw(id)The shape draws itself on — the outline traces while the (translucent) fill washes in behind it. Same base-setter+verb idiom as hidden+show. See examples/manim-vs/first-class-shapes.manic.
Write(text)untraced(e); draw(e)A left-to-right write-on wipe.
SurroundingRectangle(term, buff=.1)framebox(id, target, [buff])Outlined box fitted to a target’s bbox + pad.
ReplacementTransform(box1, box2)surround(box, newTarget)Glides + resizes the box to a new target — one verb, no second box.
SpiralInspiralin(group)Members spiral into place from a rotated, shrunken offset, staggered.
mob.save_state() / Restore(mob)savestate(id) then restore(id)Snapshot a transform, animate back to it later.
CyclicReplace / Swapcycle(a, b, c, …, [dur], [arc°])Each entity moves to the next’s position along an arc (default 90°).
Clockwise / CounterclockwiseTransformcycle(…, arc°) (signed arc)Points travel an oriented arc, set by the arc-degrees argument.
Line(...).rotate(theta) about a vertexturn(point, vertex, degrees, dur)Rotate a point about a pivot; attached arms/arcs follow.
Angle(l1, l2) + add_updateranglemark(id, a, b, c, ["θ"])Reactive arc; optional label rides the bisector as the angle opens.
Indicate / Flashpulse(id) / flash(id)Attention: a scale bump / a radial burst.
Wigglewiggle(id)Scale-pulse + oscillating rotation — the rotate cousin of shake.
Circumscribe / CircleIndicatecircumscribe(id, [color])A temporary outlined box drawn round the target, then faded — works on a group (tag) too.
FocusOnspotlight(id)A translucent disc converges onto the target, then fades.
Blinkblink(id)Blink out-and-in twice.
ShowPassingFlash / FlashAroundpassflash(id, [color])A luminous sliver travels the entity’s outline (closed shapes → light circles it).
x = ValueTracker(0) + set_x updaterslidex(id, x, dur) / slidey(id, y, dur)Move one coordinate to an absolute value, keeping the other.
VGroup(...).scale(1.4)groupscale(tag, 1.4)Scales about the group’s collective centroid.
group.animate.shift(dest − group[i])dock(tag, member, target)Slides the group so one member lands on the target — no delta maths.
VMobject + append-point updater (trace)trail(id, target, color, width)Exact, seekable motion trail sampled from the timeline.
self.camera.frame.animate.move_to(dot)followshot(id)The 2D camera tracks an entity; followshot(none) releases.
frame.animate.scale(0.5)zoom(2, …)⚠️ Inverted: Manim’s smaller frame = Manic’s zoom > 1.
ZoomedScene + activate_zooming()loupe(id, (sx,sy), w, h, (dx,dy), mag)A live magnifier: a frame over the scene + a panel showing that region enlarged.
Homotopy / ApplyWave / ComplexHomotopydeform(id, "u(x,y,t)", "v(x,y,t)")Continuous homotopy — remap a shape’s outline points by (u,v,t) each frame; the animated twin of warp. Write t=0 as the identity (u=x). A wave: "y + 30*sin(x*0.04 + t*tau)*sin(pi*t)".
self.play(a, b) (parallel)par { a; b; }Run verbs together.
self.wait(t)wait(t)Same.
self.play(FadeIn/FadeOut)show(id) / fade(id)See the note on hidden below.
GrowFromCenter / GrowFromEdge / GrowArrow / GrowFromPointhidden(id, from) then show(id)The reveal style lives on hidden (like untraced arms draw): from = center | up/down/left/right (a bbox edge) | tail (an arrow’s start) | a point (x,y). Scale-in from that anchor; omit from → fade-in.

Two things that trip people up

hidden is a base state, not an animation. hidden(x) means “x starts invisible from t=0” — it’s declared once, up top, alongside the constructors. To hide something mid-scene, use the timeline verb fade(x). (Symmetrically, show(x) reveals it.) Constructors run at build time, so anything you don’t want on screen at the start should be declared and hidden before the timeline begins.

zoom is inverted from Manim’s camera scale. Manim shrinks the camera frame to magnify (frame.scale(0.5)); Manic zooms the view directly, so zoom(2) magnifies and zoom(1) is the wide shot.

Porting a scene, step by step

A comfortable way to move a Manim scene across:

  1. Set the frame. Replace the whole Axes(...) block with one coords(...) — ranges, per-axis scale, tips, ticks, numbers, and axis names all live there. Point your plots at the same origin, sx, sy and they line up.
  2. Delete the reactive rig. Every ValueTracker, add_updater, and always_redraw has a direct verb (slidex, mark, boxto, anglemark, segment, trail, …). Find the builtin that already encodes the relationship instead of re-wiring it — the table above is your lookup.
  3. Sequence with wait and par. self.play(a, b) becomes par { a; b; }; self.wait(t) becomes wait(t). Reveals are show/draw; hides are fade.
  4. Declare-and-hidden up top. Anything that shouldn’t be on screen at the start is constructed and hidden before the timeline, then shown when its moment comes.

If a Manim move isn’t in the table, check the API reference — Manic’s vocabulary is broad, and there’s usually a builtin that says it in one line.

Beyond the core: domain kits

Here’s the biggest difference the table above doesn’t show. Manim is a general animation library: for a neural network you hand-build a NetworkMobject out of Circles and Lines and fake the activations; for a slider you subclass NumberLine; for a gradient surface you wire up a Surface and animate the vectors yourself. (That is literally how the 3Blue1Brown neural-network, high-dimensions, and gradient-descent videos are made.)

Manic ships kits that compute the object for you — the maths is real, not drawn — and because every kit object is an ordinary tagged entity, the core rhetoric (framebox, brace, arrow, say, equation, pulse) dresses it directly.

You’d hand-build in ManimA Manic kit gives you
a NetworkMobject of circles + edges, activations fakednetwork + forward / loss / backward / update — a real feed-forward net with computed activations and gradients (large layers auto-abbreviate with )
an image Mobject + hand-wired lines to the input neuronsfeed(net, digit) — flattens a tensor/digit into the inputs and traces the lit strokes in
a Surface + hand-animated gradient arrowssurface3 + slice3 (the partial-derivative cross-section) + gradient3 + descend3 (a ball rolling down −∇f, with its trail)
a rack of NumberLines for a high-dimensional pointsliders + setsliders — N coordinate dials with a live Σxᵢ²
a physics sim animated by handthe physics kit’s integrated sims (spring, pendulum, orbits, …) run on a real solver

Because they are just entities, you mix core and kit freely — this is the whole point of the language:

network(net, (600, 360), "4 6 3", "relu softmax");
framebox(fb, net.hidden, 14);      // core box around a whole KIT layer (a group)
brace(br, (720, 165), (720, 555), 26, right);
surround(fb, net.output, 1.0);     // glide the box across, layer by layer
pulse(net.output);                 // flash the prediction

The kit computes the object; the core gives you the explanation. See the ML kit, the 3D chapter, and the kits overview.

Where to go next