How to Create Custom Manim Animations by Subclassing the Animation Class and Defining Rate Functions

Subclass the Animation class from manimlib.animation.animation, override interpolate_submobject to define frame-by-frame transformations, and pass a custom rate function to control easing and timing.

To create custom Manim animations that go beyond built-in effects like FadeIn or Transform, you need to subclass the Animation class and define how each submobject changes over time. This approach, implemented in the 3b1b/manim repository, gives you precise control over interpolation logic and timing through custom rate functions.

Understanding the Animation Base Class

The Animation class in manimlib/animation/animation.py provides the lifecycle for every animation. It handles initialization, the start of the animation, frame-by-frame updates, and cleanup. When you subclass Animation, you inherit this machinery and only need to specify how the visual state changes.

Core Lifecycle Methods

The base class defines several key methods that orchestrate the animation:

  • begin(): Captures the starting state of the mobject by creating copies of submobjects.
  • interpolate(alpha): The main entry point called by Scene.play. It converts linear time alpha (0 to 1) through the rate function, then calls interpolate_mobject.
  • interpolate_mobject(alpha): Iterates over submobject pairs and calls interpolate_submobject.
  • interpolate_submobject(submobject, starting_submobject, alpha): This is the method you must override. It receives the eased alpha value and defines how the submobject transforms from its starting state.

The Role of Rate Functions

Rate functions control the pacing of the animation. They are callables that map linear time t ∈ [0, 1] to eased progress. The Animation class stores the rate function and applies it before passing the value to interpolation methods.

Built-in rate functions live in manimlib/utils/rate_functions.py and include:

  • linear: Constant velocity.
  • smooth: S-curve ease-in-out (default).
  • overshoot: Moves past the target and settles back.
  • there_and_back: Moves to target and returns to start.
  • wiggle: Oscillates around the target.

You can pass these to your animation via the rate_func parameter or define custom lambdas like lambda t: t**2 for quadratic ease-in.

How to Subclass Animation for Custom Effects

To create a custom animation, you create a class inheriting from Animation and implement the transformation logic in interpolate_submobject.

Override interpolate_submobject

This method receives three arguments:

  1. submobject: The current submobject being animated.
  2. starting_submobject: The copy of the submobject as it existed at the start.
  3. alpha: The eased time value (already processed by the rate function).

You use alpha to interpolate between the starting state and target state. For example, to fade in, you might set submobject.set_opacity(alpha).

Working with lag_ratio for Staggered Effects

The lag_ratio parameter (default 0) controls the delay between submobjects. When lag_ratio > 0, each submobject starts animating after the previous one has progressed by that fraction. This is handled automatically by get_sub_alpha in the base class, which adjusts the alpha for each submobject based on its index.

Defining Custom Rate Functions

Custom rate functions are plain Python callables that take a float t from 0 to 1 and return a float. You can combine existing functions or write mathematical transformations.

For example, to create a bounce effect:

def bounce(t):
    return t * (1 - t) * 4  # Parabolic arc peaking at t=0.5

Pass this to your animation: MyAnimation(mobj, rate_func=bounce).

Complete Code Examples

Example 1: Pulse Animation with Overshoot

This custom animation scales a mobject up and back down, using the overshoot rate function to create a bouncing effect.

from manimlib.animation.animation import Animation
from manimlib.utils.rate_functions import overshoot

class Pulse(Animation):
    """Scale a mobject up then back to its original size."""
    def __init__(self, mobject, scale_factor=1.5, **kwargs):
        super().__init__(mobject, **kwargs)
        self.scale_factor = scale_factor
        # Store the original dimensions for reference

        self.original_width = mobject.width
        self.original_height = mobject.height

    def interpolate_submobject(self, mob, start_mob, alpha):
        # alpha is already eased by the chosen rate function

        # Interpolate between 1 and the target scale_factor

        new_scale = 1 + (self.scale_factor - 1) * alpha
        mob.scale_to_fit_width(self.original_width * new_scale)
        mob.scale_to_fit_height(self.original_height * new_scale)

# Usage in a scene

class Demo(Scene):
    def construct(self):
        circle = Circle()
        self.play(Pulse(circle, rate_func=overshoot, run_time=2))

Source references: Animation class – manimlib/animation/animation.py; overshoot rate function – manimlib/utils/rate_functions.py.

Example 2: Wiggle Animation with Custom Rate Function

This example defines a custom rate function that combines there_and_back with a sine wave to create a wiggling motion.

import numpy as np
from manimlib.animation.animation import Animation
from manimlib.utils.rate_functions import there_and_back

def wiggle_rate(t, wiggles=3):
    """Wiggle back-and-forth `wiggles` times over the interval."""
    return there_and_back(t) * np.sin(wiggles * np.pi * t)

class Wiggle(Animation):
    def __init__(self, mobject, amplitude=0.2, **kwargs):
        super().__init__(mobject, **kwargs)
        self.amplitude = amplitude

    def interpolate_submobject(self, mob, start_mob, alpha):
        # Apply a sinusoidal horizontal shift based on the eased alpha

        shift = self.amplitude * np.sin(alpha * np.pi * 2)
        mob.shift(shift * RIGHT)

# In a scene

class WiggleDemo(Scene):
    def construct(self):
        square = Square()
        self.play(Wiggle(square, rate_func=lambda t: wiggle_rate(t, wiggles=5),
                         run_time=3))

Example 3: Staggered Fade-In with Lag Ratio

This animation demonstrates how to use lag_ratio to create a staggered entrance effect where submobjects fade in sequentially.

class StaggeredFadeIn(Animation):
    def __init__(self, mobject, **kwargs):
        super().__init__(mobject, lag_ratio=0.2, **kwargs)

    def interpolate_submobject(self, mob, start_mob, alpha):
        mob.set_opacity(alpha)   # Fade each sub-mobject according to its lagged alpha

# Use on a VGroup

class StaggerDemo(Scene):
    def construct(self):
        dots = VGroup(*[Dot() for _ in range(8)]).arrange(RIGHT, buff=0.5)
        self.play(StaggeredFadeIn(dots, run_time=2))

Summary

  • Subclass Animation from manimlib/animation/animation.py to create custom visual effects.
  • Override interpolate_submobject to define how individual submobjects transform from their starting state to target state using the eased alpha parameter.
  • Use rate functions from manimlib/utils/rate_functions.py or define custom callables to control easing, acceleration, and deceleration.
  • Leverage lag_ratio to create staggered animations where submobjects animate sequentially rather than simultaneously.
  • Store initial state in __init__ or begin() to ensure smooth interpolation during the animation lifecycle.

Frequently Asked Questions

What is the difference between interpolate and interpolate_submobject in Manim?

The interpolate method is the high-level entry point called by Scene.play with a linear time value. It applies the rate function to convert that time to an eased alpha value, then calls interpolate_mobject. The interpolate_submobject method is the specific hook you override to define how a single submobject changes given that eased alpha. According to the source in manimlib/animation/animation.py, interpolate_submobject receives the current submobject, its starting copy, and the eased alpha value.

How do I make a custom animation work with VGroups and submobjects?

When animating a VGroup or any compound mobject, Manim automatically iterates over submobjects and calls your interpolate_submobject for each one. To handle this properly, ensure your interpolation logic operates on the individual mob parameter rather than assuming a single mobject. If you need to access the original group structure, store references in __init__ before the base class initialization copies the starting mobject. The lag_ratio parameter in manimlib/animation/animation.py controls the delay between submobjects, creating staggered effects automatically.

Can I combine multiple custom animations to play simultaneously?

Yes, you can play multiple custom animations at once by passing them as separate arguments to self.play() in your scene, or by using AnimationGroup from manimlib/animation/composition.py. When passing multiple animations to Scene.play, Manim synchronizes their timing so they start together. If you need more complex coordination, wrap your custom animations in an AnimationGroup with lag_ratio to stagger them, or use Succession to play them sequentially. Each animation maintains its own rate function and duration independently within the group.

Where are the built-in rate functions defined in the Manim source code?

The built-in rate functions are defined in manimlib/utils/rate_functions.py. This module contains standard easing functions including linear, smooth, overshoot, there_and_back, and wiggle. Each function accepts a float t in the range [0, 1] and returns an eased float. You can import these directly or use them as templates for creating custom rate functions. The smooth function is the default used by the Animation class when no rate_func is specified.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →