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 byScene.play. It converts linear timealpha(0 to 1) through the rate function, then callsinterpolate_mobject.interpolate_mobject(alpha): Iterates over submobject pairs and callsinterpolate_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:
submobject: The current submobject being animated.starting_submobject: The copy of the submobject as it existed at the start.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
Animationfrommanimlib/animation/animation.pyto create custom visual effects. - Override
interpolate_submobjectto define how individual submobjects transform from their starting state to target state using the easedalphaparameter. - Use rate functions from
manimlib/utils/rate_functions.pyor define custom callables to control easing, acceleration, and deceleration. - Leverage
lag_ratioto create staggered animations where submobjects animate sequentially rather than simultaneously. - Store initial state in
__init__orbegin()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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →