# ManimGL (3b1b/manim) User Guide

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:**

```bash
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`):**

```python
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:

```bash
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](https://www.tug.org/texlive/) (Linux), [MacTeX](https://tug.org/mactex/)
> (macOS), or [MiKTeX](https://miktex.org/) (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"
    ```bash
    pip install manimgl
    ```

=== "Source (development)"
    ```bash
    git clone https://github.com/3b1b/manim.git
    cd manim
    pip install -e .
    ```

=== "pipx"
    ```bash
    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

```bash
manimgl --version
```

```text
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:

```bash
manimgl
```

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

```python
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`

```yaml
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`:

```python
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.

```python
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:

```python
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

```python
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

```python
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:

```python
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:

```python
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=[...]`:

```python
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 `DecimalNumber`s along the axes. |

```python
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 `VMobject`s (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 `StringMobject`s 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:

```python
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=`:

```python
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:

```python
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:

```python
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`.

```bash
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

```bash
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

```bash
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

```bash
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

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

### 5.6 Render with a Transparent Background

```bash
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

```bash
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

```bash
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

```bash
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

```bash
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

```python
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

```python
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:

```python
self.play(ChangeDecimalToValue(value, 9.0))
```

### 5.14 Build an Interactive Control Panel

```python
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:

```bash
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:**

```bash
# 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

```python
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:**

```python
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 `VMobject`s.

**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:

```python
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:

```python
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`:

```yaml
camera:
  background_color: "#101010"
```

Or per-run with `-c`:

```bash
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`?**

```yaml
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`:

```python
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?**

```bash
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`:

```python
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?**

```python
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.

---

_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. Unofficial; not affiliated with or reviewed by the project._
