| name | manim-scenes |
| description | Use when authoring or editing Manim Community Edition (manim-ce) scenes. Covers the Scene/Mobject model, the construct() lifecycle, the add/play distinction, coordinate system and direction constants, common Mobjects (Text, MathTex, geometric shapes), MathTex/LaTeX error triage, project workflow for multi-scene videos, and the gotchas that distinguish manim-ce from 3b1b's manimgl. |
Manim scenes
This skill is for Manim Community Edition (manim-ce, import manim), not 3Blue1Brown's manimgl. They have diverged: APIs that work in one often don't in the other. Check first which one the project uses.
Anatomy
from manim import *
class HelloWorld(Scene):
def construct(self):
title = Text("Hello, world!", color=BLUE)
self.play(Write(title))
self.wait()
self.play(FadeOut(title))
Scene is the base class. Each scene becomes one rendered video.
construct(self) is where the animation happens. Called once when rendering.
- Mobjects (mathematical objects) are the things you animate.
self.play(...) animates one or more changes over time.
self.add(...) instantly adds an Mobject without animating it.
self.wait(seconds=1.0) pauses (renders static frames).
The Mobject zoo
Geometric:
Square(side_length=2, color=RED)
Circle(radius=1, color=BLUE)
Rectangle(width=4, height=2)
Triangle()
Polygon(*[ [-1,0,0], [1,0,0], [0,1.5,0] ])
Line(start=LEFT, end=RIGHT)
Arrow(start=ORIGIN, end=UR)
Dot(point=ORIGIN, radius=0.08)
Text:
Text("plain text", font="Arial", weight=BOLD)
MathTex(r"e^{i\pi} + 1 = 0")
Tex(r"Inline \(\alpha\) text")
MarkupText("<b>bold</b> and <i>italic</i>")
MathTex parts are addressable: eq = MathTex("a","+","b","=","c"); eq[2] is the b. Use this for transformations.
Coordinate system and direction constants
Manim's frame is centered at the origin, x-right, y-up, z-out-of-screen. The visible frame is about ±7 in x and ±4 in y.
Standard direction vectors (numpy arrays):
ORIGIN = [0, 0, 0]
UP, DOWN, LEFT, RIGHT
UR (= UP+RIGHT), UL, DR, DL
IN (into screen), OUT
Positioning:
square.move_to(ORIGIN)
square.shift(2 * RIGHT)
square.next_to(circle, RIGHT, buff=0.5)
square.align_to(circle, LEFT)
square.to_edge(UP)
square.to_corner(UL)
These return the Mobject, so they chain: Square().shift(UP).set_color(RED).
add vs play vs animate
Three ways to change the scene:
| Form | When | Animated? |
|---|
self.add(m) | Appears instantly at start of next render frame | No |
self.play(Create(m)) | Animate construction over run_time seconds | Yes |
self.play(m.animate.shift(UP)) | Animate the result of m.shift(UP) | Yes |
The .animate syntactic sugar:
self.play(square.animate.shift(RIGHT).scale(2))
run_time controls duration: self.play(Write(t), run_time=2).
Groups
group = VGroup(square, circle, triangle)
group.arrange(RIGHT, buff=0.5)
self.play(FadeIn(group))
VGroup is for vector mobjects (most things). Group is for any mobjects mixed (e.g., images + vectors).
The self.camera.background_color trick
To match a slide deck or a particular theme:
self.camera.background_color = "#1f1f1f"
Or set globally in manim.cfg next to the script.
Common gotchas
- Modifying a Mobject after
self.play doesn't animate it. self.play records the animation; mutating the Mobject afterward changes its current state silently. Use .animate if you want an animation.
MathTex vs Tex. MathTex wraps in math mode; Tex doesn't. Mixing is a common source of "command not recognized" errors.
Text requires a TrueType font that Pango can find. Custom fonts: Text(..., font="MyFont") plus register_font() if not system-installed.
- Background isn't transparent by default for
mp4 output. Use -t (or --transparent) and .mov (ProRes) for transparent renders.
self.wait() at the end. If you skip it, the last frame is held for 0 seconds. Most exports look cut-off without a final wait.
MathTex error triage
LaTeX failures are the most common broken-render cause after API misuse. Triage in this order:
- Raw strings, always.
MathTex(r"\frac{a}{b}"). Without r, \f is a form feed and manim sends LaTeX garbage.
- f-strings double the braces.
MathTex(rf"x^{{{n}}}"), since LaTeX's {} collide with f-string interpolation.
- "LaTeX Error" in manim output means the
.tex compile failed. It's a LaTeX problem, not a Python one. Read the .log file the error points to (under media/Tex/); the offending line is quoted there. "Undefined control sequence" usually means a missing package, added via a template:
tpl = TexTemplate()
tpl.add_to_preamble(r"\usepackage{mathrsfs}")
MathTex(r"\mathscr{L}", tex_template=tpl)
- Build multi-part equations as substrings so animations have targets:
eq = MathTex("a^2", "+", "b^2", "=", "c^2")
eq[0]
SurroundingRectangle(eq[2])
A single-string MathTex is one blob, so eq[i], SurroundingRectangle, and TransformMatchingTex have nothing to grab. For finer splits without restructuring: MathTex(r"e^x = x^e", substrings_to_isolate="x").
- No LaTeX installed: every
MathTex/Tex render fails. manim needs a system LaTeX (texlive-full / MiKTeX) plus dvisvgm. Text() and MarkupText() do not.
Project workflow (multi-scene videos)
For anything longer than one scene:
- Write
plan.md first: the narrative arc, one line per scene, and the color palette (concept → color).
- One class per scene, numbered:
Scene1_Setup, Scene2_Theorem, Scene3_Derivative. Render order is explicit in the name.
- Render only the scenes you changed:
manim -ql video.py Scene3_Derivative.
- Assemble with ffmpeg concat:
printf "file '%s'\n" media/videos/video/480p15/Scene*.mp4 > concat.txt
ffmpeg -f concat -safe 0 -i concat.txt -c copy full.mp4
Globs sort lexically, so zero-pad (Scene01_…) past nine scenes.
Rendering CLI
manim -pql scene.py SceneName # quick preview (480p, 15fps)
manim -pqm scene.py SceneName # medium (720p, 30fps)
manim -pqh scene.py SceneName # high (1080p, 60fps)
manim -pqk scene.py SceneName # 4K
Flags:
-p = play after render
-q{l,m,h,k} = quality
-s = render only the last frame as PNG
-t = transparent background
Output goes to ./media/videos/<file>/<quality>/<SceneName>.mp4 by default.
Related: [[manim-animations]], [[manim-3d]].