## Manim – Introduction

*Over the course of this year (2021/2022), I created a well-received “Introduction to Manim” series for KSP (Czech CS-oriented correspondence seminar), so it made sense to make it more accessible by translating it to English and publish it here.*

**27/12/2023 update:**

- fixed to be up-to-date with
**Manim v0.18.0** - the code snippets can be selected by
**triple clicking on the code window**

Animations are better than pictures, be it when presenting interesting ideas or visualizing algorithms.
That is why it’s useful to know how to create them, ideally programmatically.
This is the main motivation behind learning **Manim** – a Python library created by Grant Sanderson (3b1b), which this multipart series aims to cover.

### Preface

To follow the series, basic knowledge of Python is required. It is also useful to know the basics of $\TeX$, which we’ll use to typeset math and text, but it is not required. You will also need to install Manim if you wish to try the example code yourself.

Each part of the series contain a number of tasks for the reader to implement, which use the covered concepts. The author’s solutions are always provided (you are, however, highly encouraged to try to implement them first).

The classes/methods discussed in the series are accompanied by the links to their pages on the official Manim documentation, which contains a more comprehensive description and the source code.

### First animations

The basic building block of Manim are **scenes**, which are Python classes inheriting the `Scene`

class.
Each of the scenes must implement the `construct`

method, which contains information about how the scene looks like (creating shapes, moving them, changing, color and size, etc.).

Here is an example of a simple scene that creates a red `Square`

and then a blue `Circle`

.

```
from manim import *
class Intro(Scene):
def construct(self):
# create square and circle objects (and move them)
square = Square(color=RED).shift(LEFT * 2)
circle = Circle(color=BLUE).shift(RIGHT * 2)
# animate writing them on screen
self.play(Write(square), Write(circle))
# fading them from the scene
self.play(FadeOut(square), FadeOut(circle), run_time=2)
```

To render the video, we will use the `manim <file_name> -pqm`

command, where

`-p`

is the preview flag, telling Manim that we want to immediately view the result and`-qm`

sets the quality to`m`

edium (others being`l`

ow,`h`

igh and 4`k`

),

yielding the following video:

The method `self.play`

always expects a **non-zero** number of animations that it plays at the same time.
We’re calling it with the `Write`

and `FadeOut`

animations above, which create and hide objects passed to them.
The optional parameter `run_time`

sets the animation duration (in seconds), defaulting to $1$ if nothing is passed.

There are also a number of builtin constants used (`LEFT`

, `RIGHT`

, `RED`

, `BLUE`

).
These are constants that Manim uses to make the code more readable.
For a more comprehensive list, here are the colors and here are other constants.

After both of the objects are created, the `shift`

method is used to move them in the given direction, **also returning them**.
This is how the vast majority of functions on Manim objects are implemented, mainly to avoid having to use the following (valid but verbose) syntax:

```
# this
square = Square(color=RED)
square.shift(LEFT * 2)
# is the same as this
square = Square(color=RED).shift(LEFT * 2)
```

Since the direction constants are NumPy arrays, they can be added and multiplied by constants with ease – to move to the side and slightly up, we can simply do `obj.shift(LEFT + UP * 1.5)`

.

`animate`

syntax

Besides creating and hiding objects, we’d like to animate their attributes like color, position and orientation.
The `shift`

function does move the object, but it doesn’t do so visually.

This can be done in a number of ways, arguably the most elegant being the `animate`

syntax, which can be used via the magic `animate`

word after the object:

```
from manim import *
class Animate(Scene):
def construct(self):
square = Square(color=RED).shift(LEFT * 2)
circle = Circle(color=BLUE).shift(RIGHT * 2)
self.play(Write(square), Write(circle))
# moving objects
self.play(
square.animate.shift(UP * 0.5),
circle.animate.shift(DOWN * 0.5)
)
# rotating and filling the square (opacity 80%)
# scaling and filling the circle (opacity 80%)
self.play(
square.animate.rotate(PI / 2).set_fill(RED, 0.8),
circle.animate.scale(2).set_fill(BLUE, 0.8),
)
# change color
self.play(
square.animate.set_color(GREEN),
circle.animate.set_color(ORANGE),
)
self.play(FadeOut(square), FadeOut(circle))
```

The example shows a number of properties that each object has (position, orientation, color) and that can be animated. As you can see from the code, the animations can be chained to change a number of them at once – just make sure that they are not conflicting (moving both up and down at the same time, for example).

### Aligning objects

We’ve seen that the `shift`

function moves an object in the given direction.
Sometimes, however, it might be more convenient to move it in relation to other objects in the scene.

`next_to`

For moving one object next to another, we’ll use the `next_to`

function:

```
from manim import *
class NextTo(Scene):
def construct(self):
c1, c2, c3, c4 = [Circle(radius=0.5, color=WHITE)
for _ in range(4)]
rectangle = Rectangle(width=5, height=3)
# use Python's * syntax to write the objects
# does the following: f(*[1, 2, 3]) == f(1, 2, 3)
self.play(*[Write(o) for o in [c1, c2, c3, c4, rectangle]])
# move the circles such that they surround the rectangle
self.play(
c1.animate.next_to(rectangle, LEFT),
c2.animate.next_to(rectangle, UP),
c3.animate.next_to(rectangle, RIGHT),
c4.animate.next_to(rectangle, DOWN),
)
```

`move_to`

For moving one object on top of another, we’ll use the `move_to`

function:

```
from manim import *
class MoveTo(Scene):
def construct(self):
s1, s2, s3 = [Square() for _ in range(3)]
self.play(*[Write(o) for o in [s1, s2, s3]])
# align squares next to one another
self.play(
s1.animate.next_to(s2, LEFT),
s3.animate.next_to(s2, RIGHT),
)
# create numbers for each of them
# the Tex class will be discussed below
t1, t2, t3 = [Tex(f"${i}$").scale(3) for i in range(3)]
# move the numbers on top of the squares
t1.move_to(s1)
t2.move_to(s2)
t3.move_to(s3)
self.play(*[Write(o) for o in [t1, t2, t3]])
```

`align_to`

For moving one object on the “same level” as another, we’ll use the `align_to`

function:

```
from manim import *
class AlignTo(Scene):
def construct(self):
c1, c2, c3 = [Circle(radius=1.5 - i / 3, color=WHITE)
for i in range(3)]
self.play(*[Write(o) for o in [c1, c2, c3]])
# align such that c1 < c2 < c3
self.play(
c1.animate.next_to(c2, LEFT),
c3.animate.next_to(c2, RIGHT),
)
# align c1 and c2 such that their bottoms are the same as c2
self.play(
c1.animate.align_to(c2, DOWN),
c3.animate.align_to(c2, DOWN),
)
point = [0, 2.5, 0]
# align all circles such that their top touches a line going through the point
self.play(
c1.animate.align_to(point, UP),
c2.animate.align_to(point, UP),
c3.animate.align_to(point, UP),
)
```

### Typesetting text and math

Manim supports setting in $\TeX$ (including math).
To do this, we’ll be using the `Tex`

for setting text and `MathTex`

for setting math.
If you’re not familiar with setting math in $\TeX$, you can use one of many online editors (such as this one).

```
from manim import *
class TextAndMath(Scene):
def construct(self):
text = Tex("Hello Manim!").shift(LEFT * 2.5)
# note that we're using Python's r-strings for cleaner code
formula = MathTex(r"\sum_{i = 0}^\infty \frac{1}{2^i} = 2").shift(RIGHT * 2.5)
self.play(Write(formula), Write(text))
self.play(FadeOut(formula), FadeOut(text))
```

An alternative to using the `Tex`

class is the `Text`

class, which internally doesn’t use $\TeX$ and thus renders a(n arguably) worse-looking text, but is easier to work with when dealing with non-english characters.

### Tasks

#### Shuffle

Create an animation of random shuffling.

The `Swap`

animation may be used for swapping the position of two objects.
Its optional parameter `path_arc`

, which determines the angle under which they are swapped, might also be useful.

```
from manim import *
class Sort(Scene):
def construct(self):
c11 = Circle(color=WHITE).shift(UP * 1.5 + LEFT * 2)
c12 = Circle(color=WHITE).shift(UP * 1.5 + RIGHT * 2)
c21 = Circle(color=WHITE).shift(DOWN * 1.5 + LEFT * 2)
c22 = Circle(color=WHITE).shift(DOWN * 1.5 + RIGHT * 2)
self.play(Write(c11), Write(c12), Write(c21), Write(c22))
self.play(Swap(c11, c21))
self.play(Swap(c12, c22, path_arc=160 * DEGREES))
```

## Author's Solution

```
from manim import *
from random import *
class Shuffle(Scene):
def construct(self):
seed(0xDEADBEEF)
# number of values to shuffle
n = 5
circles = [
Circle(color=WHITE, fill_opacity=0.8, fill_color=WHITE).scale(0.6)
for _ in range(n)
]
# spacing between the circles
spacing = 2
for i, circle in enumerate(circles):
circle.shift(RIGHT * (i - (len(circles) - 1) / 2) * spacing)
self.play(*[Write(circle) for circle in circles])
# selected circle
selected = randint(0, n - 1)
self.play(circles[selected].animate.set_color(RED))
self.play(circles[selected].animate.set_color(WHITE))
# slowly increase speed when swapping
swaps = 13
speed_start = 1
speed_end = 0.2
for i in range(swaps):
speed = speed_start - abs(speed_start - speed_end) / swaps * i
# pick two random circles (ensuring a != b)
a, b = sample(range(n), 2)
# swap with a slightly larger arc angle
self.play(
Swap(circles[a], circles[b]), run_time=speed, path_arc=135 * DEGREES
)
# highlight the initial circle again
self.play(circles[selected].animate.set_color(RED))
self.play(circles[selected].animate.set_color(WHITE))
```

#### Sort

Create an animation of a sequence being sorted.

The `stretch_to_fit_height`

function (combined with the `animate`

syntax) may be useful for changing the height of the object, without scaling it proportionally.

```
from manim import *
class StretchToFitHeightExample(Scene):
def construct(self):
s1 = Square().shift(LEFT * 2.5)
s2 = Square().shift(RIGHT * 2.5)
self.play(Write(s1), Write(s2))
self.play(
s1.animate.stretch_to_fit_height(3.5),
s2.animate.set_height(3.5),
)
```

## Author's Solution

```
from manim import *
from random import *
class Sort(Scene):
def construct(self):
seed(0xDEADBEEF)
n = 20
value_min, value_max = 1, 20
values = [randint(value_min, value_max) for _ in range(n)]
# width of rectangles and the height of a single unit
rectangle_width = 0.2
unit_height = 0.2
rectangle_spacing = 2.5
rectangles = [
Rectangle(
width=rectangle_width,
height=unit_height * v,
fill_color=WHITE,
fill_opacity=1,
)
for v in values
]
# calculate the point at which to align all of the rectangles (so they're all centered)
alignment_point = None
max_value = 0
for i, v in enumerate(values):
if max_value < v:
max_value = v
alignment_point = Point().shift(DOWN * rectangles[i].height / 2)
for i, rect in enumerate(rectangles):
rect.shift(
RIGHT
* (i - (len(rectangles) - 1) / 2)
* rectangle_width
* rectangle_spacing
).align_to(alignment_point, DOWN)
self.play(*[Write(r) for r in rectangles])
def animate_at(a, b, duration):
"""Animate that we're looking at the positions a and b."""
self.play(
*[
r.animate.set_color(WHITE if i not in (a, b) else YELLOW)
for i, r in enumerate(rectangles)
],
run_time=duration,
)
def animate_swap(a, b, duration):
"""Animate the swap of positions a and b."""
self.play(
rectangles[a]
.animate.stretch_to_fit_height(values[a] * unit_height)
.align_to(alignment_point, DOWN),
rectangles[b]
.animate.stretch_to_fit_height(values[b] * unit_height)
.align_to(alignment_point, DOWN),
run_time=duration,
)
# the first pass is slower
speed_slow = 0.6
speed_fast = 0.07
for i in range(n):
speed = speed_slow if i == 0 else speed_fast
swapped = False
for j in range(n - i - 1):
animate_at(j, j + 1, speed)
if values[j] > values[j + 1]:
values[j], values[j + 1] = values[j + 1], values[j]
animate_swap(j, j + 1, speed)
swapped = True
# if the sequence is sorted, stop
if not swapped:
break
self.play(*[FadeOut(r) for r in rectangles])
```

#### Search

Create an animation of binary searching a random sorted sequence.

The `Arrow`

object is very useful for creating the position indicators.

```
from manim import *
class ArrowExample(Scene):
def construct(self):
a1 = Arrow(start=UP, end=DOWN).shift(LEFT * 2)
a2 = Arrow(start=DOWN, end=UP).shift(RIGHT * 2)
self.play(Write(a1), Write(a2))
```

## Author's Solution

```
from manim import *
from random import *
class Search(Scene):
def construct(self):
seed(0xDEADBEEF1) # prettier input
n = 10
value_min, value_max = 1, n
values = sorted([randint(value_min, value_max) for _ in range(n)])
square_side_length = 0.75
square_spacing = 1.3
squares = [Square(side_length=square_side_length) for v in values]
numbers = [Tex(f"${v}$") for v in values]
# move rectangles such that they are centered
for i, rect in enumerate(squares):
rect.shift(
RIGHT
* (i - (len(squares) - 1) / 2)
* square_side_length
* square_spacing
)
# label positions
for i, number in enumerate(numbers):
number.move_to(squares[i])
pointer_length = 0.4
l_pointer = Arrow(start=DOWN * pointer_length, end=UP).next_to(squares[0], DOWN)
r_pointer = Arrow(start=DOWN * pointer_length, end=UP).next_to(squares[-1], DOWN)
self.play(*[Write(s) for s in squares], *[Write(n) for n in numbers])
# print the number we're looking for
target = randint(value_min, value_max)
text = Tex(f"Target: ${target}$").shift(UP * 1.5)
self.play(Write(text))
self.play(Write(l_pointer), Write(r_pointer))
lo, hi = 0, len(values) - 1
def color_in_range(objects, color, range):
"""Return the animation of coloring the objects in the sequence."""
return [
o.animate.set_color(color) for i, o in enumerate(objects) if i in range
]
while lo < hi:
avg = (lo + hi) // 2
current_arrow = (
Arrow(start=DOWN * pointer_length, end=UP)
.next_to(squares[avg], DOWN)
.set_color(ORANGE)
)
self.play(Write(current_arrow))
if values[avg] < target:
# move left pointer
self.play(
FadeOut(current_arrow),
l_pointer.animate.next_to(squares[avg + 1], DOWN),
*color_in_range(squares, DARK_GRAY, range(lo, avg + 1)),
*color_in_range(numbers, DARK_GRAY, range(lo, avg + 1)),
)
lo = avg + 1
elif values[avg] >= target:
# move right pointer
self.play(
FadeOut(current_arrow),
r_pointer.animate.next_to(squares[avg], DOWN),
*color_in_range(squares, DARK_GRAY, range(avg + 1, hi + 1)),
*color_in_range(numbers, DARK_GRAY, range(avg + 1, hi + 1)),
)
hi = avg
# the desired value has been found
if values[hi] == target:
self.play(
*color_in_range(squares, DARK_GRAY, range(hi)),
*color_in_range(squares, DARK_GRAY, range(hi + 1, n)),
*color_in_range(numbers, DARK_GRAY, range(hi)),
*color_in_range(numbers, DARK_GRAY, range(hi + 1, n)),
numbers[hi].animate.set_color(GREEN),
squares[hi].animate.set_color(GREEN),
FadeOut(l_pointer),
)
break
self.play(*[FadeOut(r) for r in numbers + squares + [r_pointer, text]])
```