Animations

There are a wide range of possibilities to animate your mobjects that all work a bit differently. Here is a broad overview so that you can choose the animation stategy that fits best for your project. This chapter will cover ValueTrackers, Updaters, self.play Transformations the mobject.animate syntax and mobject.become syntax.

[1]:
from manim import *
Manim Community v0.12.0

[2]:
#ignore this cell, only for setup
param= "-v WARNING  --progress_bar None  -r  500,200  --disable_caching Example"

NO  = Cross(Square(), stroke_color = RED_D, stroke_width = 38).scale(0.9).to_edge(LEFT, buff=1)
YES = SVGMobject("good.svg").to_edge(LEFT, buff=1)
BEST = YES.copy()
BEST.add(Star(color= YELLOW, fill_opacity=1).scale(0.5).move_to(BEST).shift(0.5*DOWN+0.5*RIGHT));

Simple Replacements

[3]:
%%manim $param
class Example(Scene):
    def construct(self):
        dot= Dot(color= YELLOW, radius=0.5)
        self.add(dot)
        self.wait()
        dot.scale(2)
        self.wait()
        dot.scale(2)
        self.wait(2)
[4]:
%%manim $param
class Example(Scene):
    def construct(self):
        dot= Dot(color= YELLOW, radius=0.5)
        square= Square(side_length=4,color= BLUE, fill_opacity=1)
        triangle= Triangle(radius=3,color= ORANGE, fill_opacity=1).shift(DOWN*0.5)
        self.add(dot)
        self.wait()
        dot.become(square)
        self.wait()
        dot.become(triangle)
        self.wait()

Using .animate Syntax

[5]:
%%manim $param
class Example(Scene):
    def construct(self):
        dot= Dot(color= YELLOW, radius=0.5)
        self.play(dot.animate.scale(2))
[6]:
%%manim $param
class Example(Scene):
    def construct(self):
        dot= Dot(color= YELLOW, radius=0.5)
        self.play(dot.animate.shift(2*RIGHT))
[7]:
%%manim $param
class Example(Scene):
    def construct(self):
        dot= Dot(color= YELLOW, radius=0.5)
        self.play(dot.animate.set_color(BLUE))
[8]:
%%manim $param
class Example(Scene):
    def construct(self):
        dot= Dot(color= YELLOW, radius=0.5)
        self.play(dot.animate.shift(2*RIGHT).scale(2))
[9]:
%%manim $param
class Example(Scene):
    def construct(self):
        dot= Dot(color= YELLOW, radius=0.5)
        self.play(dot.animate.shift(2*RIGHT).scale(2).set_color(BLUE))

Updaters

They are very diverse! And they can be used with and without a “dt” parameter

[10]:
%%manim $param
class Example(Scene):
    def construct(self):
        dot = Dot(color= GREEN, radius=0.7)
        self.add(dot)
        def foo(mob,dt):
            mob.shift(2*RIGHT*dt)
        dot.add_updater(foo)
        self.wait(3)
[11]:
%%manim $param
class Example(Scene):
    def construct(self):
        dot = Dot(color= GREEN, radius=0.7)
        self.add(dot)
        dot.add_updater(lambda x,dt: x.shift(2*RIGHT*dt))
        self.wait(3)
[12]:
%%manim $param
class Example(Scene): # when there is no dt parameter, the updater does not work
    def construct(self):
        dot = Dot(color= GREEN, radius=0.7)
        self.add(dot,NO)
        dot.add_updater(lambda x : x.shift(2*RIGHT*0.1))
        self.wait(3)

Note: Not using the “dt” parameter will make your animation framerate dependent, but this can be solved using ValueTracker, which can be seen in the next section

Updaters + ValueTrackers

[13]:
%%manim $param
class Example(Scene):
    def construct(self):
        tracker= ValueTracker(0)
        dot = Dot(color= GREEN, radius=0.7)
        self.add(dot)
        def foo(mob):
            mob.move_to(RIGHT*tracker.get_value())
        dot.add_updater(foo)
        self.play(tracker.animate.set_value(2), rate_func= linear)

Note: now you can also use rate functions:

[14]:
%%manim $param
class Example(Scene):
    def construct(self):
        tracker= ValueTracker(0)
        dot = Dot(color= GREEN, radius=0.7)
        self.add(dot)
        def foo(mob):
            mob.move_to(RIGHT*tracker.get_value())
        dot.add_updater(foo)
        self.play(tracker.animate.set_value(2), rate_func= smooth)
[15]:
%%manim $param
class Example(Scene):
    def construct(self):
        tracker= ValueTracker(0.5)
        dot = Dot(color= GREEN, radius=0.7)
        self.add(dot)
        def foo(mob):
            mob.move_to(RIGHT*tracker.get_value())
        dot.add_updater(foo)
        self.play(tracker.animate.set_value(2.2), rate_func= smooth)
        self.play(tracker.animate.increment_value(1), rate_func= smooth)
        self.play(tracker.animate.increment_value(-1), rate_func= smooth)
        self.play(tracker.animate.set_value(0.5), rate_func= linear)

[16]:
%%manim $param
#one can now also add additional properties to mobjects, in this case a counter.
class Example(Scene):
    def construct(self):
        tracker= ValueTracker(0)
        dot = Dot(color= GREEN, radius=0.7)
        self.add(dot)
        dot.counter=0
        def foo(mob):
            mob.move_to(RIGHT*tracker.get_value())
            if mob.counter == 20:
                mob.set_color(random_bright_color())
                mob.counter = 0
            mob.counter += 1
        dot.add_updater(foo)
        self.play(tracker.animate.set_value(2), rate_func= linear, run_time=3)

Transformations

[17]:
%%manim $param
class Example(Scene):
    def construct(self):
        d= Dot(color= YELLOW, radius=0.5)
        d2= d.copy().shift(2*RIGHT)
        self.play(Transform(d, d2))

Does and Donts

Note that when you choose to work with updaters, your script might depend on the frame rate.

[18]:
%%manim $param
class Example(Scene):
    def construct(self):
        print(f"{config.frame_rate = }fps")
        dotred= Dot(color= RED, radius=0.5).shift(UP)
        dotgreen = Dot(color= GREEN, radius=0.5)
        dotgreen.next_to(dotred,DOWN)
        self.add(dotgreen,dotred)
        DIR= 2*RIGHT
        dotgreen.add_updater(lambda x,dt: x.shift(DIR*dt))
        dotred.add_updater(lambda x,dt: x.shift(DIR*1/60))
        self.wait(3)
config.frame_rate = 60fps
[19]:
param5fps = "-v WARNING  --progress_bar None  --frame_rate=5 -r  500,200  --disable_caching Example"
[20]:
%%manim $param5fps
class Example(Scene):
    def construct(self):
        print(f"{config.frame_rate = }fps")
        dotred= Dot(color= RED, radius=0.5).shift(UP)
        dotgreen = Dot(color= GREEN, radius=0.5)
        dotgreen.next_to(dotred,DOWN)
        self.add(dotgreen,dotred)
        DIR= 2*RIGHT
        dotgreen.add_updater(lambda x,dt: x.shift(DIR*dt))
        dotred.add_updater(lambda x,dt: x.shift(DIR*1/60))
        self.wait(3)
config.frame_rate = 5.0fps

Rotation animation

There are multiple ways to rotate a square, but not all will result in that animation that you might have expected.

[21]:
%%manim $param

class Example(Scene):
    def construct(self, **kwargs):
        s1= Square().set_color(YELLOW)
        self.add(s1, BEST)
        self.play(Rotate(s1, angle=PI/2))
[22]:
%%manim $param
class Example(Scene):
    def construct(self, **kwargs):
        s2= Square().set_color(PURPLE)
        self.add(s2, NO)
        self.play(s2.animate.rotate(PI/2))
[23]:
%%manim $param

class Example(Scene):
    def construct(self, **kwargs):
        theta_track= ValueTracker(0)
        s3= Square().set_color(ORANGE)
        self.add(s3, YES)
        s3.previous_angle=0
        def pref(x):
            x.previous_angle=theta_track.get_value()
        s3.add_updater(lambda x: x.rotate(theta_track.get_value()-s3.previous_angle))
        s3.add_updater(pref)
        self.play(theta_track.animate.increment_value(PI/2))

[24]:
#not yet implemented
#class Example(Scene):
#    def construct(self, **kwargs):
        #s3b= Square().set_color(YELLOW)
        #self.add(s3b)
        #theta_track= DeltaValueTracker(0)
        #s3b.add_updater(lambda x: x.rotate(theta_track.get_delta_value()))
        #self.play(theta_track.animate.set_value(90*DEGREES))
[25]:
%%manim $param
# NOT WORKING!, BAD PRACTICE.
class Example(Scene):
    def construct(self, **kwargs):
        s4= Square().set_color(GREEN)
        self.add(s4, NO)
        theta_track= ValueTracker(0)
        s4.add_updater(lambda x: x.rotate(theta_track.get_value()))
        self.play(theta_track.animate.increment_value(PI/2))
[26]:
%%manim $param
class Example(Scene):
    def construct(self, **kwargs):
        s6= Square().set_color(PINK)
        self.add(s6, YES)
        s6.add_updater(lambda x, dt: x.rotate(dt*PI/2))
        self.wait(1)

Known bugs

Bug with updaters that do not have a dt

[27]:
%%manim $param
class Example(Scene):
    def construct(self):
        dot = Dot(color= GREEN, radius=0.7)
        self.add(dot,NO)

        #dot.add_updater(lambda x,dt : x)

        dot.add_updater(lambda x : x.shift(2*RIGHT*1/config.frame_rate))
        self.wait(3)
[28]:
%%manim $param
class Example(Scene):
    def construct(self):
        dot = Dot(color= GREEN, radius=0.7)
        self.add(dot,YES)

        dot.add_updater(lambda x,dt : x) #adding this line will make the updater continiously watch

        dot.add_updater(lambda x : x.shift(2*RIGHT*1/config.frame_rate))
        self.wait(3)

Bugs with updater in ZoomedScene

[29]:
%%manim $param
class Example(ZoomedScene):
    def __init__(self, **kwargs):
        ZoomedScene.__init__(
            self,
            zoom_factor=0.3,
            zoomed_display_height=4,
            zoomed_display_width=4,
            image_frame_stroke_width=20,
            zoomed_camera_config={
                "default_frame_stroke_width": 3,
            },
            **kwargs
        )
    def construct(self):
        d= Dot()
        self.add(d)
        imgo =Square().scale(0.3).set_color(RED)
        self.add(imgo)
        #imgo.add_updater(lambda x: x) # COMMENTED OUT
        self.activate_zooming(animate=True)
        self.play(self.zoomed_camera.frame.animate.shift(0.5 * (LEFT+UP*0.8)))
        self.play(self.zoomed_camera.frame.animate.shift(0.5 * (RIGHT+DOWN*2.8)))
[30]:
%%manim $param
class Example(ZoomedScene):
    def __init__(self, **kwargs):
        ZoomedScene.__init__(
            self,
            zoom_factor=0.3,
            zoomed_display_height=4,
            zoomed_display_width=4,
            image_frame_stroke_width=20,
            zoomed_camera_config={
                "default_frame_stroke_width": 3,
            },
            **kwargs
        )
    def construct(self):
        d= Dot()
        self.add(d)
        imgo =Square().scale(0.3).set_color(RED)
        self.add(imgo)
        imgo.add_updater(lambda x: x) # INCLUDED
        self.activate_zooming(animate=True)
        self.play(self.zoomed_camera.frame.animate.shift(0.5 * (LEFT+UP*0.8)))
        self.play(self.zoomed_camera.frame.animate.shift(0.5 * (RIGHT+DOWN*2.8)))
[ ]: