Jesse A. Green / notebook

Live demo: the DocCompiler guide library as a static, read-only snapshot (October 2026). No backend, no accounts, nothing to submit.

ManimGL (3b1b/manim) User Guide

Generated April 2026 from 3b1b/manim at c5e23d9

AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it.

ManimGL is the OpenGL-based mathematical animation engine written and maintained by Grant Sanderson (3Blue1Brown). It is the same library used to render the videos on the 3Blue1Brown YouTube channel — a Python framework for programming precise, expressive animations of mathematical objects, equations, graphs, and 3D surfaces, then writing them out as mp4/gif/png files or previewing them live in an interactive OpenGL window.

Important

This is the original 3b1b/manim (also called ManimGL). It is not the same project as manim-community/manim (ManimCE). The two libraries share a common ancestor and a similar high-level API, but they have diverged significantly. Code, default behavior, available classes, CLI flags, configuration files, and renderer implementations are different. If you arrived here looking for the community-maintained edition, install manim (the package on PyPI) instead of manimgl.

1. Quick Start

ManimGL turns Python Scene classes into rendered animations. You write a class that inherits from Scene, implement a construct method, populate it with mobjects (mathematical objects) and play calls, then run the manimgl command on your file.

Key features at a glance:

  • OpenGL renderer with shader-based vector graphics for sharp output at any resolution.
  • Live interactive window — preview animations as you write them, drop into an IPython shell mid-scene.
  • LaTeX integration via the Tex and TexText mobjects with 100+ built-in TeX templates.
  • Rich mobject library — geometry (Circle, Polygon, Arrow, ...), text (Text, Tex), 3D surfaces (Sphere, Torus, Cube, ...), coordinate systems (Axes, NumberPlane, ComplexPlane), value trackers, vector fields, stream lines, and matrices.
  • Animation library — fades, transforms, creation effects, indications, motion along paths, decimal counters, rotations, lagged-start groups, and string-matching transforms (TransformMatchingStrings, TransformMatchingTex).
  • Updaters and the .animate syntax — every Mobject method can be animated by chaining .animate.<method>(...) or by attaching frame-by-frame updater functions.
  • CLI quality presets — -l (480p), -m (720p), --hd (1080p), --uhd (4K) — plus arbitrary -r WxH and --fps.
  • Hot-reload during development via the embedded IPython shell (reload_scene()), and resume-mid-scene with -e <line>.

Prerequisites: Python 3.8+, FFmpeg on PATH, and a working LaTeX distribution (TeX Live, MacTeX, or MiKTeX) if you intend to render Tex / TexText. Most modern systems already meet the OpenGL/GLSL 3.30 requirement.

Install and run a one-liner:

pip install manimgl
manimgl example_scenes.py OpeningManimExample

The first command pulls ManimGL plus its Python dependencies. The second opens an OpenGL window that plays the OpeningManimExample scene from the example_scenes.py file shipped with this repo.

Tip

Skip the file argument entirely (manimgl) and ManimGL drops you straight into a blank interactive scene with from manimlib import * already executed in the IPython namespace — perfect for live experimentation.

Minimal scene file (hello.py):

from manimlib import *

class HelloManim(Scene):
    def construct(self):
        circle = Circle()
        circle.set_fill(BLUE, opacity=0.5)
        square = Square()

        self.play(ShowCreation(square))
        self.play(ReplacementTransform(square, circle))
        self.wait()

Run it:

manimgl hello.py HelloManim

Expected: an OpenGL window opens, draws a square, morphs it into a blue filled circle, and pauses for one second.

What to try next:

  • Add -w to write the result to an .mp4 instead of previewing.
  • Replace Square() with Tex(R"e^{\pi i} = -1") for a LaTeX equation.
  • Browse the bundled example_scenes.py — it ships every major capability as a runnable demo.

2. Installation

2.1 System Requirements

Requirement Detail
Python 3.8 or newer (depends on ipython>=8.18.0 and modern type-hint syntax).
OpenGL A driver and GPU supporting GLSL 3.30 core profile (covers virtually every desktop machine made since 2010).
FFmpeg Must be on PATH. Used to encode .mp4/.gif/.mov files and to mux audio.
LaTeX A latex (or xelatex / lualatex) binary on PATH plus dvisvgm. Required only if you use Tex or TexText.
Operating system Linux, macOS, and Windows are all supported.

Important

If Tex(...) raises LatexError: LaTeX compilation failed, the issue is almost always your TeX distribution rather than ManimGL. Install TeX Live (Linux), MacTeX (macOS), or MiKTeX (Windows) and re-run. This is the single most-reported issue on the project tracker (issues #2446, #2440, #2314, #2390, etc.).

2.2 Install Methods

pip

pip install manimgl

Source (development)

git clone https://github.com/3b1b/manim.git
cd manim
pip install -e .

pipx

pipx install manimgl

After install, the manimgl console script is on your PATH, and the importable Python package is manimlib (from manimlib import *).

Warning

pip install manim installs the wrong project. manim on PyPI is ManimCE (manim-community/manim). manimgl on PyPI is this project.

2.3 Runtime Dependencies

ManimGL pulls in the following Python packages (from requirements.txt):

Group Packages
Rendering moderngl, moderngl_window, PyOpenGL, pyglet (transitively via moderngl_window)
Math / shapes numpy, scipy, sympy, mapbox-earcut, skia-pathops, isosurfaces, trimesh, pywavefront
Text & SVG manimpango>=0.6.0, svgelements>=1.8.1, fontTools, Pillow
Color colour, matplotlib (colormaps only)
Audio pydub
Interactive ipython>=8.18.0, pyperclip, screeninfo
Caching & config addict, appdirs, diskcache, pyyaml, validators, tqdm, rich
Misc pygments, setuptools, audioop-lts (Python 3.13+ only)

pip install manimgl installs all of these automatically.

2.4 Verifying the Install

manimgl --version
ManimGL v1.7.x

If the binary prints a green ManimGL vX.Y.Z, the install succeeded. If you also see a Python traceback after the version line, you have a missing runtime dependency (most often FFmpeg or LaTeX) — see Troubleshooting.

A fast smoke test that exercises the renderer end-to-end:

manimgl

This launches a BlankScene with an OpenGL window and an embedded IPython shell. From the shell:

play(ShowCreation(Circle()))

A blue ring should be drawn into the window over one second.

3. Configuration

ManimGL reads configuration from layered sources, merged in this order (later sources override earlier ones):

  1. manimlib/default_config.yml shipped inside the package.
  2. custom_config.yml in the directory you run manimgl from.
  3. The file passed via --config_file /path/to/file.yml.
  4. CLI flags such as -l, --fps, --vcodec.

Note

Most users only need a custom_config.yml next to their scene files. The Admin Guide's Configuration Reference documents every key in the schema.

3.1 Minimal custom_config.yml

directories:
  base: "./manim_output"
  subdirs:
    output: "videos"
    raster_images: "images"
camera:
  background_color: "#0e0e1a"
  fps: 60
tex:
  template: "default"
text:
  font: "Consolas"
  alignment: "LEFT"

What each block does:

  • directories: where rendered videos, images, sounds, and downloaded assets go.
  • camera: default frame size, frame rate, and background color for every scene unless overridden.
  • tex: which TeX template (a preamble bundle) to use for Tex / TexText.
  • text: default font/alignment for Text(...) mobjects.

3.2 Common Settings

Goal Setting Example
Default to 1080p camera.resolution resolution: (1920, 1080)
Slow-mo 60fps camera.fps fps: 60
Different background camera.background_color background_color: "#101010"
Open window in upper-right of screen window.position_string position_string: UR
Write videos under a project root directories.base base: "/Users/me/manim_files"
Choose multi-monitor screen window.monitor_index monitor_index: 1
Enable verbose logs log_level log_level: "DEBUG"
Use Chinese-capable LaTeX tex.template template: "ctex"

3.3 CLI Flags Used at Render Time

Flag Purpose
-w / --write_file Render to .mp4 instead of previewing.
-s / --skip_animations Skip animations and write only the last frame as a .png.
-l / --low_quality 480p (854x480).
-m / --medium_quality 720p (1280x720).
--hd 1080p (1920x1080).
--uhd 4K (3840x2160).
-r WxH / --resolution WxH Arbitrary resolution, e.g. -r 1440x900.
--fps INT Override frame rate.
-c COLOR / --color COLOR Override background color (any name, hex, or rgb() string).
-t / --transparent Render to .mov with alpha (uses prores_ks codec).
-i / --gif Save output as .gif instead of .mp4.
-o / --open Open the rendered file when done. Implies -w.
--finder Reveal the rendered file in Finder/Explorer when done. Implies -w.
-f / --full_screen Show preview window in full screen.
-p / --presenter_mode Pause on every wait() until you press space or right-arrow.
-a / --write_all Render every Scene subclass in the file.
-n N[,M] / --start_at_animation_number Skip to animation N (and optionally stop at M).
-e LINE / --embed Insert self.embed() at the given line and drop into IPython there.
--vcodec STR Override FFmpeg video codec (default libx264).
--pix_fmt STR Override FFmpeg pixel format (default yuv420p).
--file_name NAME Override output file name.
--video_dir PATH Override output directory.
--config_file PATH Use a non-default custom config file.
--log-level LEVEL DEBUG / INFO / WARNING / ERROR / CRITICAL.
--leave_progress_bars Keep tqdm bars visible after the scene finishes.
--show_animation_progress Print a progress bar for each individual animation.
--prerun Run the scene once with skipping enabled to compute the total frame count first (drives the global progress bar).
--subdivide Write each animation to its own movie file under a folder.
--clear-cache Erase the LaTeX/Text mobject cache and exit.
--autoreload Re-import the scene module on every IPython cell so edits in your editor take effect without leaving the shell.
-q / --quiet Suppress most stdout.
-v / --version Print version and exit.

Tip

If you don't pass any of -w, -o, --finder, the renderer assumes "preview only" and opens an interactive window without writing files.

4. Core Usage

A ManimGL session is built from three pieces — mobjects (the things on screen), animations (changes over time), and a scene (the runtime that plays them). This section walks through each layer.

4.1 Writing a Scene

Subclass Scene (or one of its variants) and implement construct:

from manimlib import *

class FirstScene(Scene):
    def construct(self):
        text = Text("Hello, ManimGL", font_size=72)
        self.play(Write(text))
        self.wait()
        self.play(text.animate.shift(2 * UP).set_color(YELLOW))
        self.wait()

Run it: manimgl first.py FirstScene.

The Scene subclasses you can pick from:

Class When to use
Scene The default. Camera frame faces the XY plane.
ThreeDScene Subclass of Scene that adds a 3D-aware add(). Use when working with Sphere, Torus, Cube, etc.
InteractiveScene Adds drag-to-select, color palette, crosshair overlay, undo/redo, and a Cmd+C / Cmd+V workflow for live scene authoring.

Note

Scene.__init__ reads camera, file-writer, and runtime config from the global manim_config dict (built from your YAML files plus CLI overrides), then layers in any default_camera_config / default_file_writer_config you set on your subclass. Per-instance overrides win.

4.2 The .animate Syntax

Every Mobject method can be turned into an animation by inserting .animate between the mobject and the method call. ManimGL captures the call, runs the real method on a copy, and interpolates from the original to the result.

self.play(circle.animate.shift(2 * RIGHT))
self.play(grid.animate.set_color(YELLOW))
self.play(grid.animate.apply_complex_function(lambda z: z**2), run_time=6)
self.play(grid.animate.set_height(TAU - MED_SMALL_BUFF))

Chaining works:

self.play(circle.animate.shift(2 * RIGHT).scale(0.25))

4.3 Common Mobjects

These are imported by from manimlib import *. The full list lives in the Admin Guide; the table below covers the ones you reach for most often.

Category Mobject
Geometry Circle, Dot, SmallDot, Ellipse, Square, Rectangle, RoundedRectangle, Polygon, RegularPolygon, Triangle, Line, DashedLine, Arrow, Vector, Arc, ArcBetweenPoints, CurvedArrow, Annulus, AnnularSector, Sector, Elbow, CubicBezier
Text & math Text, Tex, TexText, MarkupText, Code, BulletedList
Numbers DecimalNumber, Integer, NumberLine, UnitInterval, Slider
Coordinate systems Axes, ThreeDAxes, NumberPlane, ComplexPlane
Functions ParametricCurve, FunctionGraph, ImplicitFunction
3D Sphere, Torus, Cylinder, Cone, Cube, Prism, Disk3D, Square3D, Line3D, Dodecahedron, SurfaceMesh, TexturedSurface
Containers Group, VGroup, VGroup3D, SGroup
Probability SampleSpace, BarChart
Decoration SurroundingRectangle, BackgroundRectangle, Cross, Underline, Brace
Tracking ValueTracker, ExponentialValueTracker, ComplexValueTracker, TracedPath, TracingTail, AnimatedBoundary
Vector fields VectorField, TimeVaryingVectorField, StreamLines, AnimatedStreamLines
Matrices Matrix, IntegerMatrix, DecimalMatrix, TexMatrix, MobjectMatrix
Boolean ops Union, Difference, Intersection, Exclusion
Interactive widgets Button, Checkbox, LinearNumberSlider, ColorSliders, Textbox, ControlPanel, MotionMobject

Building shapes

circle = Circle(radius=1.5, color=BLUE).set_fill(BLUE_E, opacity=0.5)
square = Square(side_length=2, fill_color=RED, fill_opacity=0.3)
arrow = Arrow(LEFT, 2 * RIGHT, buff=0)
poly  = RegularPolygon(n=6).scale(1.2)

Text and equations

title = Text("Pythagorean Theorem", font="Arial", font_size=72, t2c={"theorem": YELLOW})
eq    = Tex(r"a^2 + b^2 = c^2", font_size=96)
prose = TexText(r"Therefore $\triangle ABC$ is right-angled.")

Text is layered on Pango/HarfBuzz via manimpango and supports per-token style maps:

Text(
    "And the same as slant and weight",
    font="Consolas",
    t2s={"slant": ITALIC},
    t2w={"weight": BOLD},
    t2c={"slant": ORANGE, "weight": RED},
)

The t2c / t2s / t2w / t2f dicts (text-to-color, text-to-slant, text-to-weight, text-to-font) take literal substrings and apply the value to every match.

Indexing into Tex

Tex mobjects are indexable by substring:

equation = Tex(R"e^{\pi i} = -1", font_size=144)
self.play(FlashAround(equation["e"]))
self.play(Indicate(equation[R"\pi"]))
self.play(equation[re.compile(r"\w\^2")][0].animate.set_color(RED))

If a substring's symbols aren't drawn in source order (common with \sum, \frac, \infty), pre-declare it with isolate=[...]:

Tex(R"\sum_{n=1}^\infty {1 \over n^2}", isolate=[R"\infty"])

Coordinate systems

Axes, NumberPlane, ComplexPlane, and ThreeDAxes all inherit from CoordinateSystem, which exposes:

Method Shorthand What it does
coords_to_point(x, y[, z]) c2p Convert a point in axis units to scene coordinates.
point_to_coords(point) p2c Inverse of c2p.
get_graph(func, **kw) — Render a ParametricCurve of func.
get_graph_label(graph, label) — Place a label next to a graph.
input_to_graph_point(x, graph) i2gp The point on a graph at a given x.
add_coordinate_labels(...) — Draw DecimalNumbers along the axes.
axes = Axes(
    x_range=(-1, 10),
    y_range=(-2, 2, 0.5),
    height=6,
    width=10,
    axis_config=dict(stroke_color=GREY_A, numbers_to_exclude=[0]),
)
axes.add_coordinate_labels(font_size=20)

graph = axes.get_graph(lambda x: 2 * np.sin(x), color=BLUE)
label = axes.get_graph_label(graph, R"\sin(x)")
self.play(ShowCreation(graph), FadeIn(label, RIGHT))

4.4 Animations

Every animation is a class that takes one or more mobjects plus optional run_time, rate_func, lag_ratio, and time_span. Pass instances to self.play(...).

Creation / destruction:

Animation Effect
ShowCreation(m) Draws m along its path.
Uncreate(m) Reverses ShowCreation.
Write(m) Like ShowCreation but tuned for text/equations.
DrawBorderThenFill(m) Outline first, then fill.
ShowIncreasingSubsets(m) Reveal submobjects one by one.
ShowSubmobjectsOneByOne(m) Show then hide each submobject in turn.
AddTextWordByWord(m) Append words sequentially.

Fades:

Animation Effect
FadeIn(m) Opacity 0 → 1.
FadeOut(m) Opacity 1 → 0 and remove from scene.
FadeInFromPoint(m, point) / FadeOutToPoint(m, point) Fade with translation from/to a point.
FadeTransform(a, b) Cross-fade a into b.
FadeTransformPieces(a, b) Per-submobject cross-fade.
VFadeIn(m) / VFadeOut(m) / VFadeInThenOut(m) Stroke/fill-only fade for VMobjects (avoids point-count changes).

Growing:

Animation Effect
GrowFromPoint(m, point) / GrowFromCenter(m) / GrowFromEdge(m, edge) Scale up from origin.
GrowArrow(arrow) Draw an arrow growing along its length.

Indication:

Animation Effect
Indicate(m) Briefly scales and color-pulses.
FocusOn(point) Highlight a region with a fade-in dot.
Flash(point) Radiating lines from a point.
CircleIndicate(m) Surround m with an animated circle.
ShowPassingFlash(m) / VShowPassingFlash(m) A bright stroke moves along m.
FlashAround(m) / FlashUnder(m) Trace a flash around or below m.
ShowCreationThenDestruction(m) / ShowCreationThenFadeOut(m) Compose creation with destruction/fade.
ApplyWave(m) Sinusoidal wobble.
WiggleOutThenIn(m) Quick wiggle.
TurnInsideOut(m) Reverse normal vector — useful for surfaces.
FlashyFadeIn(m) Big-bang reveal.

Movement and transforms:

Animation Effect
Transform(a, b) Morph a into b, leaving a mutated.
ReplacementTransform(a, b) Like Transform but replaces a with b in scene.
TransformFromCopy(a, b) Copy of a becomes b; a stays put.
MoveToTarget(m) Animate m to its m.target (set via m.generate_target()).
ApplyMethod(m.method, *args) Animate the result of calling method on m. (Often replaced by m.animate.method(...).)
ApplyFunction(func, m) / ApplyPointwiseFunction(func, m) / ApplyComplexFunction(func, m) Apply a function to every point of m.
ApplyMatrix(matrix, m) Apply a 2x2 or 3x3 matrix.
Restore(m) Animate back to the most recent m.save_state().
FadeToColor(m, color), ScaleInPlace(m, factor), ShrinkToCenter(m) Convenience subclasses.
CyclicReplace(*ms) / Swap(a, b) Permute mobjects in place.
MoveAlongPath(m, path) Slide m along a VMobject path.
Rotating(m) / Rotate(m, angle) Continuous rotation / fixed-angle rotation.
Homotopy(func, m), SmoothedVectorizedHomotopy(...), ComplexHomotopy(...), PhaseFlow(...) Animate by a continuous function of position and time.

Numbers:

Animation Effect
ChangingDecimal(decimal, func_of_time) Set a DecimalNumber's value each frame from a callback.
ChangeDecimalToValue(decimal, target) Counter sweeps to target.
CountInFrom(decimal, start) Counter sweeps from start to current.

String matching:

Animation Effect
TransformMatchingStrings(a, b, matched_keys=..., key_map=...) Match common substrings in two StringMobjects and morph piece by piece.
TransformMatchingTex(a, b) / TransformMatchingShapes(a, b) Variants for Tex and shape-based matching.

Updaters:

Animation Effect
UpdateFromFunc(m, func) Run func(m) every frame for the animation's duration.
UpdateFromAlphaFunc(m, func) Run func(m, alpha) where alpha ramps 0->1.
MaintainPositionRelativeTo(m, ref) Pin m to ref while another animation runs.

Composition:

Animation Effect
AnimationGroup(*anims) Play multiple animations in parallel.
Succession(*anims) Play sequentially; total time = sum of run times.
LaggedStart(*anims, lag_ratio=...) Stagger overlapping starts.
LaggedStartMap(Anim, group, **kw) Apply Anim to each item in a group with stagger.
Broadcast(m) Concentric expanding copies.

Tip

Pass time_span=(start, end) to any animation to delay its start or stretch it across part of the play call. Useful for choreographing multiple animations inside a single play.

4.5 Updaters and Always-Redraw

ManimGL's updater system is a per-frame callback attached to a mobject:

square = Square()
brace  = always_redraw(Brace, square, UP)        # Re-runs Brace(square, UP) every frame
label  = TexText("Width = 0.00")
number = label.make_number_changeable("0.00")

label.always.next_to(brace, UP)                  # Pin label above the brace
number.f_always.set_value(square.get_width)      # Read width each frame

self.add(square, brace, label)
self.play(square.animate.set_width(5, stretch=True))

Four flavors:

  • mobject.add_updater(lambda m: ...) — explicit, always available. Optional second argument is dt.
  • mobject.always.<method>(*args) — calls method(*args) every frame.
  • mobject.f_always.<method>(*funcs) — like always but each arg is a no-arg callable evaluated every frame.
  • always_redraw(constructor, *args, **kwargs) — call constructor(...) every frame and replace the mobject's data with the result.

Note

Updaters fire on every frame the mobject is in the scene, including during animations. They run after the animation's interpolate(alpha) method, so an updater that sets a property will override the animation's effect on that property. Use mobject.suspend_updating() / resume_updating() if you need to pause updaters temporarily.

4.6 Rate Functions

Rate functions reshape time within an animation. ManimGL ships these (and you can pass any Callable[[float], float] taking values in [0, 1]):

Function Curve
linear Constant velocity
smooth Default — ease in then out
rush_into / rush_from Start fast / end fast
slow_into Decelerate to a stop
double_smooth Double-S curve
there_and_back Out and back
there_and_back_with_pause Out, pause at peak, back
running_start Pulls back, then forward
overshoot Past target then settles
not_quite_there Stops short of target
wiggle N wiggles
lingering Most of run_time at the end
exponential_decay Approaches 1 asymptotically
squish_rate_func(func, a, b) Compress an existing curve into [a, b]

Pass them via rate_func=:

self.play(square.animate.scale(2), rate_func=there_and_back, run_time=2)

4.7 Camera Frame and Three Dimensions

The active camera is self.camera, and its frame mobject is self.frame (also reachable as self.camera.frame). The frame is itself a Mobject — animate it the same way as anything else:

self.play(self.frame.animate.scale(0.75).to_corner(UL))
self.frame.add_updater(lambda m, dt: m.increment_theta(-0.1 * dt))

For 3D scenes, subclass ThreeDScene and use Sphere, Torus, Cube, Cylinder, TexturedSurface, etc. Reorient the camera with self.frame.reorient(theta_deg, phi_deg, gamma_deg) or self.frame.set_phi(...) / set_theta(...).

Mobject.fix_in_frame() glues a mobject to the camera frame so it stays in place even as the camera moves — perfect for HUD overlays like titles.

4.8 Live Interactive Development

Calling self.embed() inside construct drops you into an IPython shell with the scene paused and the local namespace (self, mobjects, play, add, remove, clear, wait, save_state, restore) ready:

class InteractiveDevelopment(Scene):
    def construct(self):
        circle = Circle()
        square = Square()
        self.play(ShowCreation(square))
        self.embed()
        # The shell now has `self`, `square`, `circle`, `play`, `add`...

Tips while embedded:

  • touch() — give the OpenGL window keyboard/mouse focus.
  • Hold d while moving the mouse to rotate the camera.
  • Hold f to pan, z + scroll to zoom.
  • Press r to reset the camera.
  • Press q to release window focus and return to the shell.
  • Type reload_scene() to pick up source edits without restarting.
  • Type exit() or hit Ctrl+D to end the scene.

Tip

Run manimgl your_file.py YourScene -e 42 to inject self.embed() at line 42 automatically. No need to edit the source.

5. Common Tasks

5.1 Render a Scene to an MP4

Goal: turn MyScene in myfile.py into MyScene.mp4.

manimgl myfile.py MyScene -w

Verify: look in videos/ (or wherever directories.output points) for MyScene.mp4.

If it fails: ensure FFmpeg is on PATH (ffmpeg -version), and check the log for codec errors. Pass --vcodec libx264 to force the default.

5.2 Render Every Scene in a File

manimgl myfile.py -a

ManimGL discovers every Scene subclass and renders them in order. If the file defines SCENES_IN_ORDER = [...], that list takes precedence.

5.3 Save the Final Frame as a Still Image

manimgl myfile.py MyScene -s -w

Without -s, the renderer writes a video. With -s, it skips animations entirely and writes only the last frame as a .png. Adding -w forces file output instead of preview.

5.4 Quick Iteration at Low Quality

manimgl myfile.py MyScene -l

-l switches to 480p (854x480) and renders far faster. Combine with -w to write or omit it to preview.

5.5 Render a 4K Final Master

manimgl myfile.py MyScene --uhd -w --fps 60

5.6 Render with a Transparent Background

manimgl myfile.py MyScene -t -w

Output is a .mov using the prores_ks codec with an alpha channel — drop it directly into a video editor.

Warning

Transparency on Windows has known issues at certain resolutions (issue #2110). If the output looks corrupt, try the same scene with a different --vcodec such as qtrle, or render on Linux/macOS.

5.7 Save a Scene as a GIF

manimgl myfile.py MyScene -i -w

The -i flag changes the file extension to .gif and clears the vcodec so FFmpeg picks an appropriate one.

5.8 Skip to the N-th Animation

manimgl myfile.py MyScene -n 5

Skips animations 0–4 and starts rendering from animation 5. Use -n 5,12 to render animations 5 through 12 inclusive.

5.9 Drop into IPython at a Specific Line

manimgl myfile.py -e 42

Inserts self.embed() after line 42 of the file and starts the scene whose nearest preceding class line is the one to use. No source edits required.

5.10 Convert Live-Authored Selection Back to Code

While running an InteractiveScene with a window, hold Ctrl and drag to highlight a region (or just tap Ctrl to select the mobject under the cursor). Then:

Keystroke Effect
Cmd/Ctrl + C Copy IDs of selection to clipboard.
Cmd/Ctrl + V Paste — interpreted as a copied mobject, copied LaTeX, or copied text.
Cmd/Ctrl + Z Undo the last operation.
Cmd/Ctrl + Shift + Z Redo.
Cmd/Ctrl + S Save selection to file.
g (hold) Grab and drag selection.
h / v / z (hold) Constrain drag to X / Y / Z axis.
t (hold) Resize selection.
Cmd + t Toggle between selecting top-level mobjects and inner submobjects.

5.11 Use presenter_mode for Talks

manimgl talk.py SlideOne -p

Every self.wait() becomes a hard pause that resumes only when you press Space or ->. Combine with -w only if you want to render the talk-paced version to file.

5.12 Add Sound to a Scene

class WithSound(Scene):
    def construct(self):
        self.add_sound("intro.wav", time_offset=0)
        self.play(Write(Text("Hello"), run_time=2))
        self.wait()

Sounds are pulled from directories.sounds (default sounds/ next to your scene file). Supported formats are anything pydub can decode (WAV, MP3, M4A, etc.).

5.13 Make Numbers in Tex Animate

tex = Tex("x^2 + y^2 = 4.00")
value = tex.make_number_changeable("4.00")
value.add_updater(lambda v: v.set_value(circle.get_radius() ** 2))
self.add(tex)

Or animate it explicitly:

self.play(ChangeDecimalToValue(value, 9.0))

5.14 Build an Interactive Control Panel

class ControlsExample(Scene):
    drag_to_pan = False

    def setup(self):
        self.textbox      = Textbox()
        self.checkbox     = Checkbox()
        self.color_picker = ColorSliders()
        self.panel = ControlPanel(
            Text("Text", font_size=24), self.textbox, Line(),
            Text("Show/Hide", font_size=24), self.checkbox, Line(),
            Text("Color", font_size=24), self.color_picker,
        )
        self.add(self.panel)

    def construct(self):
        text = Text("text", font_size=96)
        # ...attach an updater that reads from self.textbox.get_value()...

5.15 Clear the LaTeX/Text Cache

ManimGL caches compiled SVGs for Tex and Text mobjects in appdirs.user_cache_dir("manim") (or wherever directories.cache points). To wipe it:

manimgl --clear-cache

This is what to try first if a Tex mobject keeps rendering as it did before you fixed the source string.

6. Troubleshooting

Diagnostic Decision Tree

Did `manimgl --version` print without errors?
+- No  -> Your install is broken. Reinstall: `pip install --force-reinstall manimgl`.
+- Yes ->
    Did the scene fail before any rendering?
    +- Yes -> Python import error -> check Tex/Text imports & dependencies.
    +- No  ->
        Did Tex/TexText fail?
        +- Yes -> LaTeX issue -> see below.
        +- No  ->
            Did FFmpeg fail?
            +- Yes -> FFmpeg not on PATH or unsupported codec -> see below.
            +- No  -> Rendering visual issue -> file a GitHub issue.

Error: LatexError: LaTeX compilation failed

Cause: A Tex(...) or TexText(...) call failed because either (a) your LaTeX distribution is missing, (b) it can't find a required package, or (c) the source string has a syntax error LaTeX rejects.

Solution:

  1. Verify a LaTeX binary is on PATH: latex --version.
  2. If installed but missing packages, install one of: TeX Live (sudo apt install texlive-full or download from TUG), MacTeX, MiKTeX.
  3. The default template loads amsmath, amssymb, physics, mathrsfs, dsfont, xcolor, etc. If a custom tex.template you reference is missing packages, switch to template: "default" or template: "basic" in custom_config.yml.
  4. Read the LaTeX log printed below the error — it points to the exact line.

Prevention: test your Tex strings in a standalone .tex file first. Issues #2446, #2440, #2314, and #2390 all stem from missing LaTeX installs.

Error: Couldn't find ffmpeg or avconv - defaulting to ffmpeg

Cause: FFmpeg is not on PATH (issue #2417, frequent on Chromebook Crostini and minimal Docker images).

Solution:

# macOS
brew install ffmpeg

# Debian/Ubuntu
sudo apt install ffmpeg

# Windows (with Chocolatey)
choco install ffmpeg

Then reopen your shell and re-run manimgl.

Prevention: set file_writer.ffmpeg_bin: "/full/path/to/ffmpeg" in custom_config.yml if you can't put FFmpeg on the system PATH.

Error: IndexError: list index out of range on UpdatersExample

Cause: Updater pipeline regression visible on Windows + Python 3.10 + manimgl 1.7.2 (issues #2456, #2457).

Solution:

  1. Update to the latest manimgl: pip install --upgrade manimgl.
  2. If the error persists, replace mobject.always.<method>(...) with the long form mobject.add_updater(lambda m: m.<method>(...)).

Prevention: monitor the releases page on the GitHub repo.

Error: TypeError: 'Animation only works for Mobjects.'

Cause: You passed a non-Mobject to an animation (e.g. FadeIn(text_string) instead of FadeIn(Text(text_string))).

Solution: wrap the value in the appropriate Mobject constructor first.

Error: Exception: 'Whoops, looks like you accidentally invoked the method you want to animate'

Cause: You called m.method() directly inside self.play(...) instead of using m.animate.method() or ApplyMethod(m.method, ...).

Solution: rewrite

self.play(circle.shift(2 * RIGHT))            # WRONG
self.play(circle.animate.shift(2 * RIGHT))    # RIGHT

Error: Exception: 'Trying to restore without having saved'

Cause: Restore(m) or m.restore() called before m.save_state().

Solution: call m.save_state() once before the first Restore in the animation.

Error: Exception: 'MoveToTarget called on mobject without attribute target'

Cause: MoveToTarget(m) requires m.target to be set first via m.generate_target() then mutating m.target.

Solution:

m.generate_target()
m.target.shift(2 * UP).set_color(RED)
self.play(MoveToTarget(m))

Error: ValueError: At least 2 mobjects needed for Union/Intersection/Exclusion.

Cause: Boolean operations require two or more VMobjects.

Solution: pass at least two: Union(circle, square).

Error: Exception: 'Functions passed to ApplyFunction must return object of type Mobject'

Cause: The function passed to ApplyFunction returned something other than a Mobject — possibly None because it mutated in place.

Solution: make the function return its argument:

self.play(ApplyFunction(lambda m: m.shift(UP) and m, target))

Or just use ApplyMethod / .animate.shift(UP) instead.

Error: IOError: <file_name> not Found

Cause: ManimGL is looking in directories.raster_images, directories.vector_images, directories.three_d_models, or directories.sounds for the file, and not finding it.

Solution: drop the asset into the corresponding subdirectory of directories.base, or pass an absolute path.

Error: Exception: 'Only xz and xy are valid floor planes'

Cause: Passed an unsupported argument to Scene.set_floor_plane. Only "xy" (default) and "xz" are accepted.

Solution: call self.set_floor_plane("xz") for camera setups where the ground is the X-Z plane, or omit the call to keep the X-Y default.

Visual issue: black spots on surface singularities

Cause: Numerical instabilities at the singular points of parametric surfaces (issue #2452 — e.g. the poles of a Sphere).

Solution: subtly perturb the singular points or increase resolution=(theta_steps, phi_steps) on the surface mobject.

Visual issue: axis numbers disappear when indicating multiple ones at once

Cause: Issue #2352 — playing several Indicate animations on axis labels can cause earlier ones to vanish.

Solution: wrap them in a LaggedStartMap(Indicate, axes_numbers) instead of multiple parallel Indicate calls.

Visual issue: Tex substring access fails to color the right symbol

Cause: LaTeX draws the symbol in a different order than it appears in the source string (common with \sum, \frac, \infty).

Solution: declare the substring up front:

Tex(R"\sum_{n=1}^\infty {1 \over n^2}", isolate=[R"\infty"])

Visual issue: Ellipse draws a wrong curve

Cause: Known issue #2436 — when the ellipse is highly eccentric, its quadratic-bezier approximation has visible cusp artifacts.

Solution: use ParametricCurve with a parametric ellipse function for high-precision rendering, or stretch a Circle non-uniformly.

Visual issue: TexText.make_number_changeable doesn't work

Cause: Issue #2206 — under some templates the spacing math around the number's bounding box drifts after replacement.

Solution: use Tex (math mode) instead of TexText (text mode) when the number is the only thing that changes.

Visual issue: TexText does not display

Cause: Issue #2358 — TexText with empty content or with characters outside the loaded font.

Solution: ensure the source string is non-empty and that your TeX template loads a font that covers the characters you use (try tex.template: "default").

Performance: scene runs slowly even with -l

Causes: too many submobjects, expensive updaters, or repeatedly creating fresh mobjects per frame in always_redraw.

Solutions:

  1. Group static mobjects in a VGroup so they're rendered as one batch.
  2. Replace always_redraw(Constructor, *args) with a manual updater that reuses an existing mobject in place.
  3. Use -s to render a single frame and confirm the scene's static cost.

Misuse: manim (lowercase) installs the wrong project

Cause: Pip-installing manim gets the community edition (ManimCE), not this project (issue #2441).

Solution: pip uninstall manim; pip install manimgl.

Caution

Mixing manimgl and manim (ManimCE) in the same Python environment is not supported. Their import paths conflict (manimlib vs manim) and some shared dependency versions disagree. Use one or the other per virtual environment.

7. FAQ

Q: How is this different from manim-community/manim (ManimCE)?

They share a common ancestor (Grant Sanderson's original manim) but have diverged in 2020+. ManimGL is what 3Blue1Brown uses to make videos and emphasizes a fast OpenGL preview, live IPython workflow, and Grant's preferred mobject API. ManimCE prioritizes broad documentation, stability, and pluggable renderers (Cairo + OpenGL). The CLI binaries are different (manimgl vs manim), the import path is different (manimlib vs manim), and the class APIs have small but real disagreements. Pick one and stick with it for a project.

Q: Can I render with the GPU instead of OpenGL on the CPU?

OpenGL is the GPU pipeline — every Mobject is drawn through moderngl and shaders. There is no separate "CPU renderer". Rendering speed is dominated by how many submobjects you have and how often you regenerate them, not by GPU saturation. (See discussion in issue #775.)

Q: Where does the rendered video go?

Into directories.output (default videos/) under directories.base from your custom_config.yml. If directories.mirror_module_path: True, the output path mirrors the source file's path so multi-scene projects stay organized.

Q: How do I change the background color?

In custom_config.yml:

camera:
  background_color: "#101010"

Or per-run with -c:

manimgl myfile.py MyScene -c "#101010"

Or per-scene by setting default_camera_config = dict(background_color="#101010") on the Scene subclass.

Q: How do I use a different default font for Text?

text:
  font: "Inter"
  alignment: "LEFT"

Or per-mobject: Text("Hello", font="Inter"). The font must be installed on the system; manimpango looks it up via Pango/HarfBuzz.

Q: How do I use Chinese (or any CJK) text in Tex?

Set tex.template: "ctex" in custom_config.yml. The bundled ctex template includes \usepackage[UTF8]{ctex} and uses xelatex so it can typeset Chinese characters and mixes of Chinese plus Latin scripts.

Q: How do I extend manim_config in code?

Import from manimlib.config:

from manimlib.config import manim_config
manim_config.camera.fps = 60

Modifications you make before scene construction take effect for that run.

Q: Does ManimGL support hot reload while editing scenes?

Yes. From inside an embedded shell, type reload_scene() and the module will be re-imported and the scene restarted from the same embed_line. To get cell-level hot reload (every line you type re-imports the file first), add --autoreload to the manimgl command.

Q: How do I ensure my scenes are deterministic?

Scene.random_seed = <int> (default 0) on your subclass. ManimGL seeds Python's random and NumPy's RNG with this value at scene start. Set it to None to disable seeding.

Q: How do I run multiple scenes from the same file?

manimgl myfile.py SceneA SceneB SceneC

Or render every scene: manimgl myfile.py -a. If you need an explicit order, define SCENES_IN_ORDER = [SceneA, SceneB, SceneC] at module level.

Q: How do I make the camera follow a moving object?

Attach an updater to self.frame:

self.frame.add_updater(lambda m: m.move_to(target_mobject.get_center()))

Q: How do I report a bug?

Open an issue at https://github.com/3b1b/manim/issues. Include the exact manimgl --version output, your Python version, OS, the pip freeze of your environment, the minimal scene that reproduces, and the full Python traceback.

Q: Is the project actively maintained?

ManimGL is maintained by Grant Sanderson at the pace required to ship 3Blue1Brown videos. Pull requests are reviewed but throughput is limited. For a more "community-driven" cadence, look at ManimCE.

Q: Where can I see real-world ManimGL code?

Read the example_scenes.py file shipped with this repo for a tour. For full-length productions, https://github.com/3b1b/videos is the public mirror of every animation 3Blue1Brown has shipped — thousands of scenes.

Q: How do I save and restore scene state?

self.save_state()
# ...play animations...
self.restore_state(self.undo_stack[-1])  # or self.undo()

Scene.max_num_saved_states = 50 controls how deep the undo stack goes. InteractiveScene exposes Cmd/Ctrl+Z and Cmd/Ctrl+Shift+Z for the same operations interactively.

Q: Why does --version exit without rendering anything?

-v short-circuits the run loop after printing if no file argument was supplied. Drop the flag (or pass a file) to render.

Q: Can I write my own animation classes?

Yes — subclass Animation and implement interpolate_submobject(self, sub, sub_starting, alpha) (per-submobject interpolation) or interpolate_mobject(self, alpha) (whole-mobject interpolation). See the Admin Guide's developer section for the exact contract.

Q: How do I disable the click-and-drag-to-pan behavior?

Set drag_to_pan = False on your Scene subclass (it is True by default).

Q: Why is the window opening on the wrong monitor?

Set window.monitor_index: <n> in custom_config.yml to pick a specific display, or window.position_string: UR (upper-right) / UL / DL / DR / UO (upper middle) / etc. for placement on the chosen monitor.