| name | manim-educational-animation |
| description | Creating clean, non-overlapping Manim animations for educational explainers (Gymnasium/school level). Avoids text overlap bugs, guides pacing, and structures single-scene vs multi-scene approaches. |
| version | 1.0.0 |
Manim Educational Animation — Best Practices
When to Use
Creating clean, non-overlapping Manim animations for educational explainers (Gymnasium/school level). Avoids text overlap bugs, guides pacing, and structures single-scene vs multi-scene approaches.
Overview
This skill contains a reusable operational workflow. Follow the existing task-specific steps and examples in the sections below.
Pitfalls
- Do not hardcode credentials, tokens, or personal secrets.
- Verify external service URLs, paths, and permissions before making changes.
- Keep generated outputs reproducible and record input assumptions.
Verification
- Confirm required inputs and credentials are available.
- Run the smallest safe command or example before scaling up.
- Check produced files, API responses, or plots before reporting success.
Core Lesson Learned (from trial & error)
The #1 bug in educational Manim animations: old text stays visible while new text is written over it.
The root cause: Manim renders animations in parallel. Write and FadeOut in the same self.play() overlap.
The Fix: Step Group Pattern
s1_lbl = Text("Schritt 1: Variablen trennen", ...)
s1_eq = MathTex(r"\frac{dT}{T-20} = -k\,dt", ...)
self.play(Write(s1_lbl), Write(s1_eq), run_time=2.0)
self.wait(3.0)
s1_grp = VGroup(s1_lbl, s1_eq)
self.play(FadeOut(s1_grp), run_time=1.2)
self.wait(0.5)
s2_lbl = Text("Schritt 2: Integrieren", ...)
s2_eq = MathTex(r"\ln|T-20| = -kt + C", ...)
self.play(Write(s2_lbl), Write(s2_eq), run_time=2.0)
Why wait(0.5): Manim's animation queue needs a frame gap to clear the old mobject.
Without it, the next Write starts before FadeOut finishes.
Why not ReplacementTransform: It can leave LaTeX artifacts and bounding-box ghosts.
FadeOut of a VGroup is reliable.
Consistent Positioning
All derivation steps should use the same anchor position — otherwise they drift:
s1_lbl = Text("Schritt 1: ...", ...).to_edge(LEFT, buff=0.8).shift(DOWN * 2.0)
s2_lbl = Text("Schritt 2: ...", ...).to_edge(LEFT, buff=0.8).shift(DOWN * 2.0)
eq.next_to(s1_lbl, DOWN, buff=0.5)
Single-Scene vs Multi-Scene
Single-scene (one class with _teilN() methods)
- Best for: focused topic, 1-2 minute explainers, school material
- Pros: no stitching needed, easier to avoid overlap, simpler code
- Cons: long render time for one scene
- Target: ~60 animations, ~2 min for a complete topic
Multi-scene (stitched)
- Best for: 5+ min videos, modular content, different visual layouts per section
- Pros: scenes render in parallel, each scene has its own visual identity
- Cons: more complex pipeline, overlap bugs harder to track across scenes
- Target: ~15 animations per scene, ~15 sec/scene
Pacing for Educational Content
| Audience | Target length | Animations | Per-step wait |
|---|
| Gymnasium/School (Klasse 11) | 1.5–2.5 min | ~60–80 | 2–3 sec |
| University | 3–5 min | ~80–120 | 1.5–2 sec |
| General public | 5–8 min | ~120–200 | 1–1.5 sec |
Color Palette (Dark Background)
BG = "#0D1117"
BLUE = "#58C4DD"
GREEN = "#83C167"
YELLOW = "#FFFF00"
WHITE = "#EAEAEA"
DIM = "#7A8599"
RED = "#FF6B6B"
PURPLE = "#C792EA"
MONO = "DejaVu Sans Mono"
Common Bugs
| Bug | Cause | Fix |
|---|
| Text overlaps | FadeOut + Write in same play() | Add wait(0.5) after every FadeOut |
| Steps drift vertically | No consistent anchor position | All steps use same .shift(DOWN * 2.0) |
| LaTeX artifacts | ReplacementTransform on MathTex | Use FadeOut + Write instead |
| Row-by-row reveals too slow | 6+ reveals per field × 0.2s each | Use Create(pfeile) for the whole group at once |
| 9-part video too long | Too many scenes, too much explanation | Target 5 parts max, 2 min total |
| 199 animations for 2 min | Too many wait() and small reveals | Combine into fewer, bigger animations |
log(x, base) not defined | Manim has no built-in log function | Use np.log(x) / np.log(base) with import numpy as np |
NameError: name 'log' not defined | Same as above — log from math module not available | Same fix: np.log(x) / np.log(base) |
Rendering
manim -ql script.py SceneName
manim -qh script.py SceneName
manim -ql --format=png -s script.py SceneName
ffmpeg -y -f concat -safe 0 -i concat.txt -c copy final.mp4