Chapter 3 · manim-slides
The payoff chapter: your animations become presentations. The decks below are real interactive presentations — click one open, then click / press → inside it to advance, exactly like your audience would.
Guess: what's the minimum change to turn an animation into a presentation?
Slide instead of Scene and mark pauses with next_slide(). That's genuinely all — Topic 1.A talk venue has no WiFi. What could go wrong with an HTML deck?
--offline flag bundles everything — Topic 2.1From Scene to Slide — one import, one call
Change Scene to Slide (from manim_slides) and call self.next_slide() wherever the presentation should pause and wait for you. Everything you know from Chapter 2 still works — a Slide is a Scene.
from manim import *
from manim_slides import Slide
class FirstDeck(Slide):
def construct(self):
title = Text("My first deck", font_size=60)
self.play(Write(title))
self.next_slide() # ⏸ waits for your click
self.play(title.animate.scale(0.5).to_edge(UP))
body = Text("Click / press → to advance",
font_size=36, color=GREY)
self.play(FadeIn(body))
self.next_slide() # ⏸ waits again
self.play(FadeOut(title), FadeOut(body))Render + convert: manim-slides render deck.py FirstDeck then manim-slides convert --to html --offline FirstDeck out.html — or press ▶ with the VS Code extension and it's all automatic.
class TwoPoints(Slide):
def construct(self):
head = Text("Why animate slides?", font_size=48).to_edge(UP)
p1 = Text("1. Motion guides attention",
font_size=34, color=TEAL).shift(UP * 0.5)
p2 = Text("2. Steps appear when YOU decide",
font_size=34, color=YELLOW).next_to(p1, DOWN, buff=0.6)
self.play(Write(head))
self.next_slide()
self.play(FadeIn(p1, shift=RIGHT))
self.next_slide()
self.play(FadeIn(p2, shift=RIGHT))
self.next_slide()
self.play(FadeOut(head), FadeOut(p1), FadeOut(p2))The pattern for every talk: reveal → pause → reveal → pause.
class LoopingLogo(Slide):
def construct(self):
logo = RegularPolygon(6, color=TEAL,
fill_opacity=0.4).scale(1.5)
label = Text("loop=True keeps this spinning",
font_size=30).to_edge(DOWN)
self.play(Create(logo), FadeIn(label))
self.next_slide(loop=True) # ⟳ loops until you advance
self.play(Rotate(logo, TAU, run_time=3,
rate_func=linear))
self.next_slide()
self.play(FadeOut(logo), FadeOut(label))loop=True replays that segment forever — perfect for title slides while the audience settles.
class MathLecture(Slide):
def construct(self):
title = Text("The derivative", font_size=54)
self.play(Write(title))
self.next_slide()
self.play(title.animate.scale(0.55).to_edge(UP))
definition = MathTex(
r"f'(x) = \lim_{h \to 0} \frac{f(x+h) - f(x)}{h}",
font_size=54)
self.play(Write(definition))
self.next_slide()
self.play(definition.animate.scale(0.7).shift(UP * 1.6))
axes = Axes(x_range=[-2.5, 2.5], y_range=[-0.5, 4],
x_length=8, y_length=3.2).shift(DOWN * 1.2)
curve = axes.plot(lambda x: 0.55 * x**2 + 0.3,
color=YELLOW)
self.play(Create(axes), Create(curve))
self.next_slide()
x = ValueTracker(-1.8)
tangent = always_redraw(lambda: TangentLine(
curve, alpha=(x.get_value() + 2.5) / 5,
length=3.5, color=RED))
self.add(tangent)
self.play(x.animate.set_value(1.8), run_time=3,
rate_func=linear)
self.next_slide()
self.play(*[FadeOut(m) for m in
[title, definition, axes, curve, tangent]])All three chapters in one deck: Chapter-1 LaTeX, Chapter-2 graphs & updaters, Chapter-3 pauses.
Slide from manim_slides, subclass it as MyTalk, play one animation, then call next_slide().2Rendering, presenting, exporting
Three commands cover the whole lifecycle — and if you use the VS Code extension, the first two happen automatically every time you save.
# 1. RENDER — runs Manim + records where the pauses are
manim-slides render lecture.py MathLecture
# 2. PRESENT — three ways to show it:
manim-slides present MathLecture # native window (PySide6) — best in-person
manim-slides convert --to html --offline MathLecture out.html # browser deck — best to share
# ...or the VS Code extension's live preview — best while authoring
# 3. EXPORT — hand it to PowerPoint people:
manim-slides convert --to pptx MathLecture lecture.pptx | Target | Best for | Offline? |
|---|---|---|
Native GUI (present) | Presenting in person — instant start, presenter hotkeys | always |
HTML (convert --to html --offline) | Sharing a link/file; works on any machine with a browser | with --offline |
PowerPoint (--to pptx) | Venues that demand .pptx; embeds auto-playing videos | always |
MyTalk from talk.py with manim-slides, then convert it to offline HTML named talk.html.3Slide craft — patterns that make decks feel professional
Four battle-tested patterns. None require new API — just discipline in how you use next_slide() and FadeOut.
Pattern 1 — Clean between sections
self.next_slide()
self.play(*[FadeOut(m) for m in self.mobjects]) # wipe everything
# ...start the next section on a clean canvas Pattern 2 — Title that shrinks into a header
title = Text("Big opening title", font_size=64)
self.play(Write(title))
self.next_slide()
self.play(title.animate.scale(0.5).to_edge(UP)) # becomes the header Pattern 3 — The loop-while-talking title slide
self.next_slide(loop=True) # ambient motion, zero pressure
self.play(Rotate(star, TAU, run_time=4, rate_func=linear))
self.next_slide() # advances when YOU are ready Pattern 4 — Highlight, then un-highlight
self.play(formula[2].animate.set_color(YELLOW)) # spotlight one term
self.next_slide()
self.play(formula[2].animate.set_color(WHITE)) # release attention next_slide(). Pauses are free — confusion isn't.next_slide with loop=True, play a Rotate animation, then call next_slide() again to close the loop.4Canvas, wipes, zooms & speaker notes — the pro deck
Four upgrades in one deck (open it — it's real): a canvas header that survives slide wipes, wipe() and zoom() transitions, and speaker notes only you see in presenter view.
class ProDeck(Slide):
def construct(self):
header = Text("Advanced deck patterns",
font_size=32).to_edge(UP)
self.add_to_canvas(header=header) # survives wipes
self.play(FadeIn(header))
self.next_slide(
notes="Canvas keeps the header everywhere.")
p1 = Text("wipe() slides content sideways",
font_size=30, color=TEAL)
self.play(FadeIn(p1))
self.next_slide(notes="Demonstrate wipe.")
p2 = Text("like a real slide change",
font_size=30, color=YELLOW)
self.wipe(self.mobjects_without_canvas, p2)
self.next_slide(notes="Demonstrate zoom.")
p3 = Text("zoom() scales the next idea in",
font_size=30, color=GREEN)
self.zoom(self.mobjects_without_canvas, p3)
self.next_slide(notes="Wrap up.")
self.play(*[FadeOut(m) for m in self.mobjects])add_to_canvas pins mobjects across transitions; mobjects_without_canvas is everything else. notes= appears in presenter view (press S in HTML decks).
self.add_to_canvas(title=…), then wipe to the next content excluding the canvas.5Every export target that matters
One rendered deck, four audiences: a browser tab for you, a single HTML file for email, a PowerPoint for the conference laptop, a PDF for the handout.
# 1. live presenting (opens a window, arrow keys advance)
manim-slides present MyTalk
# 2. one self-contained HTML file — email it, open anywhere, no install
manim-slides convert --to html --offline --one-file MyTalk talk.html
# 3. PowerPoint — each slide segment becomes an auto-playing video
manim-slides convert --to pptx MyTalk talk.pptx
# 4. PDF handout — one frame per slide
manim-slides convert --to pdf MyTalk talk.pdf--offline bundles reveal.js locally (folder + assets dir); adding --one-file inlines everything into a single .html — biggest file, zero dependencies, survives email attachments. That single-file trick is exactly how the decks embedded in this site were made portable.6The real-world workflow, start to finish
How the pieces fit on an actual working day — the exact loop the Manim Slides Preview VS Code extension automates for you (▶ renders + converts; Ctrl+S keeps the .pptx fresh).
# the loop you will actually live in:
# 1. edit talk.py (VS Code)
# 2. render draft manim render -ql talk.py MyTalk
# 3. preview instantly manim-slides present MyTalk
# 4. repeat 1-3 until happy
# 5. final quality manim render -qh talk.py MyTalk
# 6. ship both formats manim-slides convert --to pptx MyTalk talk.pptx
# manim-slides convert --to html --offline --one-file MyTalk talk.html1️⃣ Renaming the scene class and wondering where the old slides went — slide data is stored per class name; re-render after renaming.
2️⃣ Forgetting to re-render before convert — convert uses the last rendered videos, not your latest code.
3️⃣ Paths with spaces: always quote —
"d:\My Talks\talk.py" — or the CLI sees two arguments. (Your project folder d:\Alamin Maruf\... qualifies!)Self-examination
show solution
from manim import *
from manim_slides import Slide
class SineDeck(Slide):
def construct(self):
title = Text("The sine wave", font_size=56)
self.play(Write(title))
self.next_slide()
self.play(title.animate.scale(0.5).to_edge(UP))
axes = Axes(x_range=[-4, 4], y_range=[-2, 2],
x_length=10, y_length=5)
curve = axes.plot(lambda x: np.sin(x), color=YELLOW)
self.play(Create(axes))
self.play(Create(curve), run_time=2)
self.next_slide()
self.play(*[FadeOut(m) for m in self.mobjects])show solution
class PulseTitle(Slide):
def construct(self):
c = Circle(radius=1.5, color=TEAL, fill_opacity=0.4)
title = Text("Ready?", font_size=48).next_to(c, DOWN)
self.play(Create(c), Write(title))
self.next_slide(loop=True)
self.play(c.animate.scale(1.25), rate_func=there_and_back,
run_time=1.6)
self.next_slide()
self.play(FadeOut(c), FadeOut(title))show solution
class QuadraticTalk(Slide):
def construct(self):
# 1 — looping title
title = Text("The Quadratic Formula", font_size=54)
self.play(Write(title))
self.next_slide(loop=True)
self.play(title.animate.set_color(TEAL),
rate_func=there_and_back, run_time=2)
self.next_slide()
self.play(title.animate.scale(0.5).to_edge(UP))
# 2 — the formula
f = MathTex(r"x = \frac{-b \pm \sqrt{b^2-4ac}}{2a}",
font_size=64)
self.play(Write(f))
self.next_slide()
# 3 — parabola with roots
self.play(f.animate.scale(0.6).shift(UP * 1.8))
ax = Axes(x_range=[-4, 2], y_range=[-3, 5],
x_length=8, y_length=3.4).shift(DOWN * 1.2)
curve = ax.plot(lambda x: x**2 + 2*x - 2, color=YELLOW)
roots = VGroup(*[Dot(ax.c2p(r, 0), color=RED)
for r in (-1 - 3**0.5, -1 + 3**0.5)])
self.play(Create(ax), Create(curve))
self.play(FadeIn(roots, scale=3))
self.next_slide()
# 4 — highlight the discriminant b²-4ac
self.play(Indicate(f, color=YELLOW))
self.next_slide()
# 5 — clean exit
self.play(*[FadeOut(m) for m in self.mobjects])
# terminal:
# manim-slides render talk.py QuadraticTalk
# manim-slides convert --to html --offline QuadraticTalk talk.html
# manim-slides convert --to pptx QuadraticTalk talk.pptx