# How to Leverage Manim’s Internal _AnimationBuilder for Chained Animations

> Master Manim's internal _AnimationBuilder to create powerful chained animations. Learn how this pattern sequences transformations for smoother, complex visual effects in your scenes.

- Repository: [Grant Sanderson/manim](https://github.com/3b1b/manim)
- Tags: internals
- Published: 2026-02-28

---

**Manim’s private `_AnimationBuilder` class transforms any `Mobject` into a fluent interface for sequencing transformations, capturing method calls on a hidden target copy and compiling them into a single `_MethodAnimation` when passed to `Scene.play()`.**

The `3b1b/manim` library provides a powerful internal mechanism for creating fluent, chained animations through the `_AnimationBuilder` pattern. By accessing the `.animate` property on any `Mobject`, developers can leverage the internal _AnimationBuilder pattern for creating chained animations in Manim that compile multiple transformations into a single interpolated sequence.

## Understanding the _AnimationBuilder Architecture

The animation builder lives in [`manimlib/mobject/mobject.py`](https://github.com/3b1b/manim/blob/main/manimlib/mobject/mobject.py) as the private class **`_AnimationBuilder`**. When you access the `animate` property of any `Mobject`, Manim creates an instance of this builder and prepares it to record transformations.

### Construction and Target Generation

Upon instantiation, the builder immediately creates a copy of the original object that will serve as the transformation target:

```python

# Internal mechanism in manimlib/mobject/mobject.py

self.mobject.generate_target()

```

The `generate_target()` method makes a deep copy of the `Mobject` and stores it as `self.mobject.target`. This target object receives all the transformations while the original remains unchanged until animation playback begins.

### Method Chaining and Attribute Capture

Every attribute accessed on the builder (e.g., `.shift`, `.scale`, `.set_fill`) is resolved on the *target* object rather than the original:

- The underlying method is stored in `self.methods`
- The builder returns itself (`return self`), enabling fluent chaining
- Attribute access is intercepted and redirected to `self.mobject.target`

This design allows you to write expressive chains like `square.animate.shift(RIGHT).scale(2).set_fill(RED)` where each method call is recorded for later execution.

### Animation Argument Handling

Calling the builder as a function (e.g., `animate(run_time=2)`) stores animation-wide keyword arguments in `self.anim_args` via `set_anim_args`:

```python

# Example usage

square.animate(run_time=3, rate_func=linear).shift(RIGHT)

```

These arguments can only be set once per builder instance, preventing ambiguous configuration. When the animation is finally built, these parameters are passed to the generated `Animation` object.

## From Builder to Animation: The Execution Pipeline

When you pass the builder to `Scene.play()`, Manim must convert the recorded method chain into a runnable animation object.

### The prepare_animation Bridge

In [`manimlib/animation/animation.py`](https://github.com/3b1b/manim/blob/main/manimlib/animation/animation.py), the `prepare_animation` function handles the conversion:

```python
def prepare_animation(anim):
    if isinstance(anim, _AnimationBuilder):
        return anim.build()
    # ... handle other animation types

```

This bridge allows `Scene.play` to accept either concrete `Animation` instances or `_AnimationBuilder` objects seamlessly.

### Building the _MethodAnimation

The `_AnimationBuilder.build()` method creates a **`_MethodAnimation`**, which is a subclass of `MoveToTarget` defined in [`manimlib/animation/transform.py`](https://github.com/3b1b/manim/blob/main/manimlib/animation/transform.py):

- The animation iterates over the stored methods in `self.methods`
- It applies each method to the target copy
- During playback, it interpolates the original `Mobject` toward the transformed target state
- Animation-wide arguments (`run_time`, `rate_func`, etc.) are applied to the final animation object

This pipeline ensures that complex chains like `.shift().scale().rotate()` execute as a single coherent animation rather than separate sequential steps.

## Practical Implementation Examples

### Basic Chained Transformations

The most common use case involves chaining multiple transformations on a single mobject:

```python
from manim import *

class ChainDemo(Scene):
    def construct(self):
        square = Square()
        self.add(square)
        # Shift right → scale up → change fill colour, all in one animation

        self.play(
            square.animate.shift(RIGHT).scale(2).set_fill(RED, opacity=0.8),
            run_time=3,
        )

```

In this example, `square.animate` instantiates the `_AnimationBuilder`. Each subsequent method call (`shift`, `scale`, `set_fill`) is recorded and applied to the target copy. When `self.play` receives the builder, it compiles these into a single `_MethodAnimation` that executes over three seconds.

### Configuring Animation Parameters

You can specify global animation parameters either through `Scene.play` or directly on the builder:

```python
self.play(
    circle.animate.move_to(UP).rotate(PI/2).set_stroke(BLUE, width=5),
    run_time=2,
    rate_func=slow_into,
)

```

Alternatively, use `set_anim_args` (invoked when calling the builder as a function):

```python
self.play(
    triangle.animate.set_anim_args(lag_ratio=0.2).rotate(2*PI),
)

```

Note that `set_anim_args` can only be called once per builder instance, ensuring unambiguous configuration of animation-wide properties like `lag_ratio`, `run_time`, or `rate_func`.

### Custom Animations via @override_animate

For methods requiring non-standard animation behavior, Manim provides the `@override_animate` decorator. When the builder detects this marker, it stores the custom animation instead of generating a generic `_MethodAnimation`:

```python
from manim import *

class MyMobject(VMobject):
    @override_animate
    def wiggle(self, amplitude=0.2):
        # Return a custom Animation

        return ApplyMethod(self.shift, amplitude * RIGHT)

class OverrideDemo(Scene):
    def construct(self):
        obj = MyMobject().set_points_as_corners([[-1, -1, 0], [1, 1, 0]])
        self.add(obj)
        # The builder detects the overridden method and uses the returned Animation

        self.play(obj.animate.wiggle(amplitude=0.5))

```

**Important limitation:** Chaining is **not** supported after an overridden method. Attempting to call additional methods after `.wiggle()` raises `NotImplementedError`, as the builder cannot determine how to merge custom animations with subsequent generic transformations.

## Summary

- **`_AnimationBuilder`** in [`manimlib/mobject/mobject.py`](https://github.com/3b1b/manim/blob/main/manimlib/mobject/mobject.py) provides the fluent interface accessed via `.animate`.
- **Target generation** via `generate_target()` creates a copy of the mobject that receives all transformations while the original remains untouched until playback.
- **Method chaining** captures attribute access on the target, storing methods in a list for later execution by `_MethodAnimation`.
- **Animation compilation** occurs in `prepare_animation` ([`manimlib/animation/animation.py`](https://github.com/3b1b/manim/blob/main/manimlib/animation/animation.py)), which invokes `build()` to create a `_MethodAnimation` from the stored method chain.
- **Global parameters** can be set via `Scene.play` arguments or `set_anim_args` on the builder, but only once per builder instance.
- **Custom animations** use `@override_animate` to bypass the generic method animation, though this disables further chaining.

## Frequently Asked Questions

### What is the _AnimationBuilder pattern in Manim?

The `_AnimationBuilder` pattern is an internal implementation in `3b1b/manim` that enables fluent, chained animation syntax. When you access the `.animate` property on any `Mobject`, Manim instantiates this private builder class, which records every subsequent method call (like `shift()` or `rotate()`) on a hidden target copy. When the scene plays the animation, the builder compiles these recorded methods into a single `_MethodAnimation` that interpolates the original object toward the transformed state.

### How does method chaining work with the animate property?

Method chaining works through attribute interception. When you write `square.animate.shift(RIGHT).scale(2)`, the builder resolves `shift` and `scale` on the target copy (`self.mobject.target`) rather than the original object. It stores the method references in an internal list (`self.methods`) and returns `self`, allowing the next method call to continue the chain. This deferred execution model ensures all transformations are applied simultaneously during a single animation frame rather than sequentially as separate animations.

### Can I configure global animation parameters using _AnimationBuilder?

Yes, you can configure animation-wide parameters such as `run_time`, `rate_func`, or `lag_ratio` in two ways. First, pass them directly to `Scene.play()` alongside the builder instance. Second, invoke the builder as a function with keyword arguments (e.g., `square.animate(run_time=2).shift(RIGHT)`), which internally calls `set_anim_args`. Note that these arguments can only be set once per builder instance; attempting to configure them multiple times will raise an error to prevent ambiguous animation settings.

### Why does chaining fail after using @override_animate?

Chaining fails after `@override_animate` because the builder switches from recording generic method calls to executing a custom animation factory. When the builder detects the `_override_animate` marker on a method, it expects that method to return a fully constructed `Animation` object (such as `ApplyMethod` or a custom subclass). Since this returned animation encapsulates the transformation logic internally, the builder cannot determine how to merge subsequent method calls into the existing custom animation. Attempting to chain further methods raises `NotImplementedError` to prevent undefined behavior.