Manim: Creating Dynamic Visual Animations

0
2

Learn about how Manim, a Python library, converts code into captivating animations, and why it has become the preferred choice for educators, developers, and content creators worldwide.

Modern educational videos in mathematics and science often feature stunning animations that turn complex ideas into visual masterpieces. Equations flow dynamically, geometric figures evolve seamlessly, and algorithms come alive through elegant motion—making learning both engaging and intuitive.

Traditionally, educators depended on costly proprietary tools like Adobe After Effects or specialised mathematical visualisers with steep learning curves and high licensing fees. These barriers often left creative educators confined to static presentations and textbook-based explanations.

Today, the open source revolution has transformed this space with Manim—a Python library that makes high-quality educational animations accessible to all. Originally built by Grant Sanderson for his 3Blue1Brown channel, Manim has grown into a powerful engine enabling anyone with basic programming skills to create professional grade visualisations.

Understanding Manim’s architecture

Before creating animations, it’s important to understand Manim’s core components.

  • Scenes: The canvas and timing controller for animations.
  • Mobjects: Mathematical objects representing visual elements.
  • Animations: Transformations defining how objects change over time.
  • Rendering pipeline: Converts code into high-quality video files.

Scene structure

Manim operates on a scene-based architecture, where every animation is defined as a Python class inheriting from Scene.

Its key characteristics are:

  • Must contain a construct() method.
  • Acts as both a spatial canvas and a timing controller.
  • Provides specialised scene types for different contexts.

Here’s an example scene structure:

class BasicDemo(Scene):
def construct(self):
# Scene initialization
title = Text(“Manim Architecture”)
# Animation sequence
self.play(Write(title))
self.wait(2)

You can refer to Figure 1 for the output.

 Manim architecture
Figure 1: Manim architecture

Mobjects (Mathematical objects)

Mobjects represent all the visual elements in Manim animations.

Basic primitives are:

  • Geometric shapes → Circle(), Square(), Arrow(), Polygon()
  • Text renderingText(), Tex() (with LaTeX support)
  • Graphs and plotsAxes(), FunctionGraph(), NumberPlane()

Advanced constructs are:

  • 3D objectsThreeDAxes(), Surface(), ParametricSurface()
  • Custom graphicsSVGMobject(), ImageMobject()

Grouping mechanisms are:

  • Vertical grouping for synchronised animationsVGroup()
  • General grouping for treating multiple objects as oneGroup()

Animation system:

Animations define high-level transformations, not frame-by-frame changes.

General animations are:

  • Create() Object creation animation
  • Transform() Morph one object into another
  • FadeIn() / FadeOut() Opacity transitions
  • Write()Text-specific writing animation

Timing control is as follows:

  • self.play() Executes animation sequences
  • self.wait() → Adds pause or delay
  • run_time Controls animation speed

Setting up Manim on Linux

To install and run Manim, we need Python 3.7 or higher, FFmpeg (for video encoding), LaTeX (for mathematical typesetting) and Cairo (for graphics rendering).

Let us start with dependency installation.

For Ubuntu/Debian:

sudo apt update
sudo apt install python3 python3-pip python3-venv
sudo apt install ffmpeg sox libcairo2-dev pkg-config
sudo apt install texlive-full pandoc

For Arch Linux:

sudo pacman -S python python-pip ffmpeg
sudo pacman -S texlive-most cairo

For Fedora:

sudo dnf install python3 python3-pip ffmpeg
sudo dnf install texlive cairo-devel

For a virtual environment setup, create an isolated environment:

python3 -m venv manim-env
source manim-env/bin/activate
pip install --upgrade pip setuptools wheel

To install Manim, type:

pip install manim

For installation verification, use the following code:

manim --version
manim checkhealth

Common solutions are:

  • Missing LaTeX → Install texlive-full package
  • Cairo errors → Install libcairo2-dev development headers
  • FFmpeg issues → Verify PATH and installation

For directory structure setup, organise project directories:

mkdir -p ~/manim-projects/{scripts,media,assets}

cd ~/manim-projects/scripts

Creating your first animation

For basic scene creation, create a file first_animation.py:

from manim import *
class BasicShapes(Scene):
def construct(self):
# Object creation
welcome_text = Text(“Welcome to Manim!”)
square = Square(side_length=2, color=BLUE)
circle = Circle(radius=1.2, color=GREEN)
# Positioning
welcome_text.to_edge(UP, buff=0.5)
square.shift(LEFT * 2.5)
circle.shift(RIGHT * 2.5)
# Animation sequence
self.play(Write(welcome_text))
self.play(Create(square), Create(circle))
self.play(square.animate.rotate(PI/4))
self.play(circle.animate.set_color(YELLOW))
self.play(Transform(square, circle))
self.wait(2)

The output of the above code can be seen Figure 2.

Basic scene creation
Figure 2: Basic scene creation

For basic rendering, use:

manim -pql first_animation.py BasicShapes

The flag meanings are:

-p — Preview (auto-open video)

-q — Quality (l=low, m=medium, h=high)

-l — Low quality (480p, fast rendering)

Quality options are:

-ql — 480p (development)

-qm — 720p (standard)

-qh — 1080p (production)

-qk – 4K (high-end)

Essential positioning is as follows:

  • to_edge(UP/DOWN/LEFT/RIGHT) — Align to
    screen edges
  • next_to(object, direction) — Position relative to other objects
  • shift(vector) — Move by specified amount
  • move_to(position) — Move to exact coordinates

Coordinate system is:

  • Origin (0,0) at screen centre
  • Positive X extends right
  • Positive Y extends up
  • Standard vectors: UP, DOWN, LEFT, RIGHT, ORIGIN

Algorithm and data structure visualisation

For horizontal distance, use the following code:

from manim import *

class BraceAnnotation(Scene):

def construct(self):

dot = Dot([-2, -1, 0])

dot2 = Dot([2, 1, 0])

line = Line(dot.get_center(), dot2.get_center()).set_color(ORANGE)

b1 = Brace(line)

b1text = b1.get_text(“Horizontal distance”)

b2 = Brace(line, direction=line.copy().rotate(PI / 2).get_unit_vector())

b2text = b2.get_tex(“x-x_1”)

self.add(line, dot, dot2, b1, b2, b1text, b2text)

You can refer to Figure 3 for the output of the above code.
For vector arrow, use:

Horizontal distance
Figure 3: Horizontal distance
from manim import *


class VectorArrow(Scene):

def construct(self):

dot = Dot(ORIGIN)

arrow = Arrow(ORIGIN, [2, 2, 0], buff=0)

numberplane = NumberPlane()

origin_text = Text(‘(0, 0)’).next_to(dot, DOWN)

tip_text = Text(‘(2, 2)’).next_to(arrow.get_end(), RIGHT)

self.add(numberplane, dot, arrow, origin_text, tip_text)

The output of the above code is given in Figure 4.

Vector arrow
Figure 4: Vector arrow

The code for Boolean operations is:

from manim import *

class BooleanOperations(Scene):

def construct(self):

ellipse1 = Ellipse(

width=4.0, height=5.0, fill_opacity=0.5, color=BLUE, stroke_width=10

).move_to(LEFT)

ellipse2 = ellipse1.copy().set_color(color=RED).move_to(RIGHT)

bool_ops_text = MarkupText(“<u>Boolean Operation</u>”).next_to(ellipse1, UP * 3)

ellipse_group = Group(bool_ops_text, ellipse1, ellipse2).move_to(LEFT * 3)

self.play(FadeIn(ellipse_group))

i = Intersection(ellipse1, ellipse2, color=GREEN, fill_opacity=0.5)

self.play(i.animate.scale(0.25).move_to(RIGHT * 5 + UP * 2.5))

intersection_text = Text(“Intersection”, font_size=23).next_to(i, UP)

self.play(FadeIn(intersection_text))

u = Union(ellipse1, ellipse2, color=ORANGE, fill_opacity=0.5)

union_text = Text(“Union”, font_size=23)

self.play(u.animate.scale(0.3).next_to(i, DOWN, buff=union_text.height * 3))

union_text.next_to(u, UP)

self.play(FadeIn(union_text))

e = Exclusion(ellipse1, ellipse2, color=YELLOW, fill_opacity=0.5)

exclusion_text = Text(“Exclusion”, font_size=23)

self.play(e.animate.scale(0.3).next_to(u, DOWN, buff=exclusion_text.height * 3.5))

exclusion_text.next_to(e, UP)

self.play(FadeIn(exclusion_text))

d = Difference(ellipse1, ellipse2, color=PINK, fill_opacity=0.5)

difference_text = Text(“Difference”, font_size=23)

self.play(d.animate.scale(0.3).next_to(u, LEFT, buff=difference_text.height * 3.5))

difference_text.next_to(d, UP)

self.play(FadeIn(difference_text))

The output of Boolean operations can be seen in Figure 5.

Boolean operations
Figure 5: Boolean operations

Best practices and optimisation

For performance optimisation, follow this development workflow:

  • Use –ql flag for rapid iteration.
  • Cache scenes to avoid re-rendering.
  • Break large projects into smaller scenes.

Organise code as follows:

# config.py
from manim import *

CUSTOM_BLUE = “#1E3A8A”

CUSTOM_GREEN = “#10B981”

TITLE_SCALE = 0.8

# main_animation.py

from manim import *

from config import *

For collaborative development, the version control setup should be:

  • Use -ql flag for rapid iteration.
  • Cache scenes to avoid re-rendering.
  • Break large projects into smaller scenes.

Code organisation should be:

git init manim-project

echo “media/” >> .gitignore

echo “__pycache__/” >> .gitignore

echo “*.pyc” >> .gitignore

An optimised project structure is:

manim-project/

├── scenes/

│ ├── intro.py

│ ├── algorithms.py

│ └── conclusion.py

├── assets/

│ ├── images/

│ └── sounds/

├── config.py

└── README.md

Manim transforms the challenging task of creating professional animations into an accessible, code-driven workflow that aligns perfectly with open source principles. By leveraging Python’s ecosystem and running natively on Linux systems, it provides animation capabilities that extend far beyond mathematical content into the programming, science, engineering, business, and creative domains.

 

LEAVE A REPLY

Please enter your comment!
Please enter your name here