docs/source/tutorials/building_blocks.rst
####################### Manim's building blocks #######################
This document explains the building blocks of manim and will give you all the necessary tools to start producing your own videos.
Essentially, manim puts at your disposal three different concepts that you can
orchestrate together to produce mathematical animations: the
mathematical object (or mobject for short), the animation, and the
scene. As we will see in the following sections, each of these three
concepts is implemented in manim as a separate class: the :class:.Mobject,
:class:.Animation, and :class:.Scene classes.
.. note:: It is recommended that you read the tutorials :doc:quickstart and
:doc:output_and_config before reading this page.
Mobjects
Mobjects are the basic building blocks for all manim animations. Each class
that derives from :class:.Mobject represents an object that can be displayed
on the screen. For example, simple shapes such as :class:.Circle,
:class:.Arrow, and :class:.Rectangle are all mobjects. More complicated
constructs such as :class:.Axes, :class:.FunctionGraph, or
:class:.BarChart are mobjects as well.
If you try to display an instance of :class:.Mobject on the screen, you will only
see an empty frame. The reason is that the :class:.Mobject class is an
abstract base class of all other mobjects, i.e. it does not have any
pre-determined visual shape that can be displayed on the screen. It is only the
skeleton of a thing that could be displayed. Therefore, you will rarely need
to use plain instances of :class:.Mobject; instead, you will most likely
create instances of its derived classes. One of these derived classes is
:class:.VMobject. The V stands for Vectorized Mobject. In essence, a
vmobject is a mobject that uses vector graphics <https://en.wikipedia.org/wiki/Vector_graphics>_ to be displayed. Most of
the time, you will be dealing with vmobjects, though we will continue to use
the term "mobject" to refer to the class of shapes that can be displayed on the
screen, as it is more general.
.. note:: Any object that can be displayed on the screen is a mobject, even if
it is not necessarily mathematical in nature.
.. tip:: To see examples of classes derived from :class:.Mobject, see the
:mod:.geometry module. Most of these are in fact derived from
:class:.VMobject as well.
As explained in :doc:quickstart, usually all of the code in a manim
script is put inside the :meth:.construct method of a :class:.Scene class.
To display a mobject on the screen, call the :meth:~.Scene.add method of the
containing :class:.Scene. This is the principal way of displaying a mobject
on the screen when it is not being animated. To remove a mobject from the
screen, simply call the :meth:~.Scene.remove method from the containing
:class:.Scene.
.. manim:: CreatingMobjects
class CreatingMobjects(Scene):
def construct(self):
circle = Circle()
self.add(circle)
self.wait(1)
self.remove(circle)
self.wait(1)
Let's define a new :class:.Scene called Shapes and :meth:~.Scene.add
some mobjects to it. This script generates a static picture that displays a
circle, a square, and a triangle:
.. manim:: Shapes
class Shapes(Scene):
def construct(self):
circle = Circle()
square = Square()
triangle = Triangle()
circle.shift(LEFT)
square.shift(UP)
triangle.shift(RIGHT)
self.add(circle, square, triangle)
self.wait(1)
By default, mobjects are placed at the center of coordinates, or origin, when
they are first created. They are also given some default colors. Further, the
Shapes scene places the mobjects by using the :meth:.shift method. The
square is shifted one unit in the UP direction from the origin, while the
circle and triangle are shifted one unit LEFT and RIGHT, respectively.
.. attention:: Unlike other graphics software, manim places the center of
coordinates at the center of the screen. The positive vertical
direction is up, and the positive horizontal direction is right.
See also the constants ORIGIN, UP, DOWN, LEFT,
RIGHT, and others, defined in the :mod:.constants module.
There are many other possible ways to place mobjects on the screen, for example
:meth:.move_to, :meth:.next_to, and :meth:.align_to. The next scene
MobjectPlacement uses all three.
.. manim:: MobjectPlacement
class MobjectPlacement(Scene):
def construct(self):
circle = Circle()
square = Square()
triangle = Triangle()
# place the circle two units left from the origin
circle.move_to(LEFT * 2)
# place the square to the left of the circle
square.next_to(circle, LEFT)
# align the left border of the triangle to the left border of the circle
triangle.align_to(circle, LEFT)
self.add(circle, square, triangle)
self.wait(1)
The :meth:.move_to method uses absolute units (measured relative to the
ORIGIN), while :meth:.next_to uses relative units (measured from the
mobject passed as the first argument). :meth:align_to uses LEFT not as
measuring units but as a way to determine the border to use for alignment. The
coordinates of the borders of a mobject are determined using an imaginary
bounding box around it.
.. tip:: Many methods in manim can be chained together. For example the two lines
.. code-block:: python
square = Square()
square.shift(LEFT)
can be replaced by
.. code-block:: python
square = Square().shift(LEFT)
Technically, this is possible because most methods calls return the modified mobject.
The following scene changes the default aesthetics of the mobjects.
.. manim:: MobjectStyling
class MobjectStyling(Scene):
def construct(self):
circle = Circle().shift(LEFT)
square = Square().shift(UP)
triangle = Triangle().shift(RIGHT)
circle.set_stroke(color=GREEN, width=20)
square.set_fill(YELLOW, opacity=1.0)
triangle.set_fill(PINK, opacity=0.5)
self.add(circle, square, triangle)
self.wait(1)
This scene uses two of the main functions that change the visual style of a
mobject: :meth:.set_stroke and :meth:.set_fill. The former changes the
visual style of the mobject's border while the latter changes the style of the
interior. By default, most mobjects have a fully transparent interior so you
must specify the opacity parameter to display the color. An
opacity of 1.0 means fully opaque, while 0.0 means fully transparent.
Only instances of :class:.VMobject implement :meth:.set_stroke and
:meth:.set_fill. Instances of :class:.Mobject implement
:meth:.~Mobject.set_color instead. The vast majority of pre-defined classes
are derived from :class:.VMobject so it is usually safe to assume that you
have access to :meth:.set_stroke and :meth:.set_fill.
The next scene is exactly the same as the MobjectStyling scene from the
previous section, except for exactly one line.
.. manim:: MobjectZOrder
class MobjectZOrder(Scene):
def construct(self):
circle = Circle().shift(LEFT)
square = Square().shift(UP)
triangle = Triangle().shift(RIGHT)
circle.set_stroke(color=GREEN, width=20)
square.set_fill(YELLOW, opacity=1.0)
triangle.set_fill(PINK, opacity=0.5)
self.add(triangle, square, circle)
self.wait(1)
The only difference here (besides the scene name) is the order in which the
mobjects are added to the scene. In MobjectStyling, we added them as
add(circle, square, triangle), whereas in MobjectZOrder we add them as
add(triangle, square, circle).
As you can see, the order of the arguments of :meth:~.Scene.add determines
the order that the mobjects are displayed on the screen, with the left-most
arguments being put in the back.
Animations
At the heart of manim is animation. Generally, you can add an animation to
your scene by calling the :meth:~.Scene.play method.
.. manim:: SomeAnimations
class SomeAnimations(Scene):
def construct(self):
square = Square()
# some animations display mobjects, ...
self.play(FadeIn(square))
# ... some move or rotate mobjects around...
self.play(Rotate(square, PI/4))
# some animations remove mobjects from the screen
self.play(FadeOut(square))
self.wait(1)
Put simply, animations are procedures that interpolate between two mobjects.
For example, :code:FadeIn(square) starts with a fully transparent version of
:code:square and ends with a fully opaque version, interpolating between them
by gradually increasing the opacity. :class:.FadeOut works in the opposite
way: it interpolates from fully opaque to fully transparent. As another
example, :class:.Rotate starts with the mobject passed to it as argument, and
ends with the same object but rotated by a certain amount, this time
interpolating the mobject's angle instead of its opacity.
Any property of a mobject that can be changed can be animated. In fact, any
method that changes a mobject's property can be used as an animation, through
the use of :meth:.animate.
.. manim:: AnimateExample :ref_classes: Animation
class AnimateExample(Scene):
def construct(self):
square = Square().set_fill(RED, opacity=1.0)
self.add(square)
# animate the change of color
self.play(square.animate.set_fill(WHITE))
self.wait(1)
# animate the change of position and the rotation at the same time
self.play(square.animate.shift(UP).rotate(PI / 3))
self.wait(1)
:meth:.animate is a property of all mobjects that animates the methods that come
afterward. For example, :code:square.set_fill(WHITE) sets the fill color of
the square, while :code:square.animate.set_fill(WHITE) animates this action.
By default, any animation passed to :meth:play lasts for exactly one second.
Use the :code:run_time argument to control the duration.
.. manim:: RunTime
class RunTime(Scene):
def construct(self):
square = Square()
self.add(square)
self.play(square.animate.shift(UP), run_time=3)
self.wait(1)
Even though Manim has many built-in animations, you will find times when you need to smoothly animate from one state of a :class:~.Mobject to another.
If you find yourself in that situation, then you can define your own custom animation.
You start by extending the :class:~.Animation class and overriding its :meth:~.Animation.interpolate_mobject.
The :meth:~.Animation.interpolate_mobject method receives alpha as a parameter that starts at 0 and changes throughout the animation.
So, you just have to manipulate self.mobject inside Animation according to the alpha value in its interpolate_mobject method.
Then you get all the benefits of :class:~.Animation such as playing it for different run times or using different rate functions.
Let's say you start with a number and want to create a :class:~.Transform animation that transforms it to a target number.
You can do it using :class:~.FadeTransform, which will fade out the starting number and fade in the target number.
But when we think about transforming a number from one to another, an intuitive way of doing it is by incrementing or decrementing it smoothly.
Manim has a feature that allows you to customize this behavior by defining your own custom animation.
You can start by creating your own Count class that extends :class:~.Animation.
The class can have a constructor with three arguments, a :class:~.DecimalNumber Mobject, start, and end.
The constructor will pass the :class:~.DecimalNumber Mobject to the super constructor (in this case, the :class:~.Animation constructor) and will set start and end.
The only thing that you need to do is to define how you want it to look at every step of the animation.
Manim provides you with the alpha value in the :meth:~.Animation.interpolate_mobject method based on frame rate of video, rate function, and run time of animation played.
The alpha parameter holds a value between 0 and 1 representing the step of the currently playing animation.
For example, 0 means the beginning of the animation, 0.5 means halfway through the animation, and 1 means the end of the animation.
In the case of the Count animation, you just have to figure out a way to determine the number to display at the given alpha value and then set that value in the :meth:~.Animation.interpolate_mobject method of the Count animation.
Suppose you are starting at 50 and incrementing until the :class:~.DecimalNumber reaches 100 at the end of the animation.
Generally, you start with the starting number and add only some part of the value to be increment according to the alpha value.
So, the logic of calculating the number to display at each step will be 50 + alpha * (100 - 50).
Once you set the calculated value for the :class:~.DecimalNumber, you are done.
.. note::
If you're creating a custom animation and want to use a ``rate_func``, you must explicitly apply
``self.rate_func(alpha)`` to the parameter you're animating. For example, try switching the rate
function to ``rate_functions.there_and_back`` to observe how it affects the counting behavior.
Once you have defined your Count animation, you can play it in your :class:~.Scene for any duration you want for any :class:~.DecimalNumber with any rate function.
.. manim:: CountingScene :ref_classes: Animation DecimalNumber :ref_methods: Animation.interpolate_mobject Scene.play
class Count(Animation):
def __init__(self, number: DecimalNumber, start: float, end: float, **kwargs) -> None:
# Pass number as the mobject of the animation
super().__init__(number, **kwargs)
# Set start and end
self.start = start
self.end = end
def interpolate_mobject(self, alpha: float) -> None:
# Set value of DecimalNumber according to alpha
value = self.start + (self.rate_func(alpha) * (self.end - self.start))
self.mobject.set_value(value)
class CountingScene(Scene):
def construct(self):
# Create Decimal Number and add it to scene
number = DecimalNumber().set_color(WHITE).scale(5)
# Add an updater to keep the DecimalNumber centered as its value changes
number.add_updater(lambda number: number.move_to(ORIGIN))
self.add(number)
self.wait()
# Play the Count Animation to count from 0 to 100 in 4 seconds
self.play(Count(number, 0, 100), run_time=4, rate_func=linear)
self.wait()
Mobjects contain points that define their boundaries.
These points can be used to add other mobjects respectively to each other,
e.g. by methods like :meth:~.Mobject.get_center , :meth:~.Mobject.get_top
and :meth:~.Mobject.get_start. Here is an example of some important coordinates:
.. manim:: MobjectExample :save_last_frame:
class MobjectExample(Scene):
def construct(self):
p1 = [-1,-1, 0]
p2 = [ 1,-1, 0]
p3 = [ 1, 1, 0]
p4 = [-1, 1, 0]
a = Line(p1,p2).append_points(Line(p2,p3).points).append_points(Line(p3,p4).points)
point_start = a.get_start()
point_end = a.get_end()
point_center = a.get_center()
self.add(Text(f"a.get_start() = {np.round(point_start,2).tolist()}", font_size=24).to_edge(UR).set_color(YELLOW))
self.add(Text(f"a.get_end() = {np.round(point_end,2).tolist()}", font_size=24).next_to(self.mobjects[-1],DOWN).set_color(RED))
self.add(Text(f"a.get_center() = {np.round(point_center,2).tolist()}", font_size=24).next_to(self.mobjects[-1],DOWN).set_color(BLUE))
self.add(Dot(a.get_start()).set_color(YELLOW).scale(2))
self.add(Dot(a.get_end()).set_color(RED).scale(2))
self.add(Dot(a.get_top()).set_color(GREEN_A).scale(2))
self.add(Dot(a.get_bottom()).set_color(GREEN_D).scale(2))
self.add(Dot(a.get_center()).set_color(BLUE).scale(2))
self.add(Dot(a.point_from_proportion(0.5)).set_color(ORANGE).scale(2))
self.add(*[Dot(x) for x in a.points])
self.add(a)
It is also possible to transform a mobject into another mobject like this:
.. manim:: ExampleTransform
class ExampleTransform(Scene):
def construct(self):
self.camera.background_color = WHITE
m1 = Square().set_color(RED)
m2 = Rectangle().set_color(RED).rotate(0.2)
self.play(Transform(m1,m2))
The Transform function maps points of the previous mobject to the points of the
next mobject.
This might result in strange behaviour, e.g. when the dots of one mobject are
arranged clockwise and the other points are arranged counterclockwise.
Here it might help to use the flip function and reposition the points via the
roll <https://numpy.org/doc/stable/reference/generated/numpy.roll.html>_
function of numpy:
.. manim:: ExampleRotation
class ExampleRotation(Scene):
def construct(self):
self.camera.background_color = WHITE
m1a = Square().set_color(RED).shift(LEFT)
m1b = Circle().set_color(RED).shift(LEFT)
m2a = Square().set_color(BLUE).shift(RIGHT)
m2b = Circle().set_color(BLUE).shift(RIGHT)
points = m2a.points
points = np.roll(points, int(len(points)/4), axis=0)
m2a.points = points
self.play(Transform(m1a,m1b),Transform(m2a,m2b), run_time=1)
Scenes
The :class:.Scene class is the connective tissue of manim. Every mobject has
to be :meth:added <.Scene.add> to a scene to be displayed, or :meth:removed <.Scene.remove> from it to cease being displayed. Every animation has to be
:meth:played <.Scene.play> by a scene, and every time interval where no
animation occurs is determined by a call to :meth:~.Scene.wait. All of the
code of your video must be contained in the :meth:~.Scene.construct method of
a class that derives from :class:.Scene. Finally, a single file may contain
multiple :class:.Scene subclasses if multiple scenes are to be
rendered at the same time.