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 *
config.media_embed = True
Manim Community v0.17.3

[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)

and this is how to make a custom rate function, e.g. a for a low frame rate within a high frame rate video:

[15]:
%%manim $param

def low_frame_rate(t):
    return np.floor(t*10)/10

class Example(Scene):
    def construct(self):
        self.camera.background_color = "#ece6e2"
        banner = ManimBanner(dark_theme=False).scale(0.7)
        banner2 = banner.copy()
        Group(banner, banner2).arrange(DOWN)
        self.play(banner.expand(), rate_func = low_frame_rate)
        self.play(banner2.expand())
        self.wait()
[16]:
%%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)

It’s also possible to use different rate functions simultaneously:

[17]:
%%manim $param
class Example(Scene):
    def construct(self):
        line1 = Line(3*LEFT, 3*RIGHT, stroke_width=10).set_color(RED)
        line2 = Line(3*LEFT, 3*RIGHT, stroke_width=10).set_color(GREEN)
        Group(line1,line2).arrange(DOWN, buff=2)

        d1 = Dot(point = line1.get_left(), radius=0.5)
        d2 = Dot(point = line2.get_left(), radius=0.5)

        label1 = Tex("smooth").scale(2).next_to(line1, buff= 1)
        label2 = Tex("linear").scale(2).next_to(line2, buff= 1)

        tr1=ValueTracker(-3)
        tr2=ValueTracker(-3)

        d1.add_updater(lambda z: z.set_x(tr1.get_value()))
        d2.add_updater(lambda z: z.set_x(tr2.get_value()))

        self.add(line1,line2,d1,d2,label1,label2 )

        self.play(tr1.animate(rate_func=smooth).set_value(3), tr2.animate(rate_func=linear).set_value(3))
        self.wait()
[18]:
%%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#

[19]:
%%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.

[20]:
%%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 = 60.0fps
[21]:
param5fps = "-v WARNING  --progress_bar None  --frame_rate=5 -r  500,200  --disable_caching Example"
[22]:
%%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.

[23]:
%%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))
[24]:
%%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))
[25]:
%%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))

[26]:
#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))
[27]:
%%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))
[28]:
%%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#

[29]:
%%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)
[30]:
%%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#

[31]:
%%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)))
[32]:
%%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)))