EXPLAINERS & GUIDES

Manim Tutorial: Render Your First Math Animation

Build one complete Python scene: change the slope of a line, track a point, and export a video you can check. This example was rendered with Manim Community 0.21.0.

A silent, 12-second Manim render made for this lesson. The blue line is y = mx; the orange point keeps x = 2. As m moves through 0, 1, 2, and −1, the point reaches y = 0, 2, 4, and −2. The axes use different screen scales; read the labels rather than the angle.

Download Python sourceDownload MP4

What you will build

This beginner Manim tutorial turns y = mx into a 12-second video. You will create axes, a line, a point, and a changing parameter; then render the scene to an MP4. The complete source below produces the video above. It is a standalone Manim example made for this guide, not a demonstration of browser generation in LemmaReel.

You need basic Python familiarity and a working Manim Community installation. This lesson uses ordinary Text labels, so its scene does not require LaTeX. Other Manim examples that use MathTex may need a separate LaTeX installation.

1. Prepare a small, separate project

Follow the official Manim installation guide for your operating system and its native dependencies. Keep the tutorial environment separate from existing projects. With Python 3.12 and uv available, the commands for this example are:

mkdir slope-lesson
cd slope-lesson
uv venv --python 3.12
uv pip install "manim==0.21.0"
uv run manim --version

The page's output was verified with Python 3.12 and Manim Community 0.21.0 on macOS. The commands above are not a claim that every operating system has been tested. Check the official setup instructions if a native dependency fails to install.

Download the Python source above and save it as slope_scene.py inside this folder. Keep its class name SlopeLesson: the render command uses that name to select the scene.

2. Read the scene in four parts

Scene and construct. SlopeLesson inherits from Scene. Manim runs construct to assemble objects and execute the animation in order. self.add places the static objects and the starting state on screen.

Mathematical coordinates. Axes defines the coordinate system. axes.c2p(x, y) converts a mathematical point to a position on the screen. This scene uses different horizontal and vertical scales to fit the graph, so the line's apparent angle is not a direct measurement of its slope.

A changing value. ValueTracker(0) stores m. Both line endpoints are computed from y = mx. The orange dot always uses (2, 2*m), so its x coordinate cannot drift when m changes.

Objects that follow the value. always_redraw rebuilds the line, dot, and numeric readout as m changes. self.play(m.animate.set_value(target)) moves to the next value; self.wait(2) holds that result long enough to inspect it. The loop defines a three-second transition-and-hold for each new target.

The official quickstart introduces the general scene workflow; the Axes reference documents its coordinate helpers.

3. Use the complete source

This code is loaded from the same file offered by the download link, so the displayed lesson and downloadable source stay together.

"""A self-contained Manim Community 0.21.0 lesson; no LaTeX required.
Render: manim -qm --disable_caching slope_scene.py SlopeLesson
"""
from manim import (
    BLUE, ORANGE, WHITE, Axes, Dot, Line, Scene, Text,
    ValueTracker, always_redraw, linear,
)


class SlopeLesson(Scene):
    def construct(self):
        self.camera.background_color = "#111827"
        axes = Axes(
            x_range=[-3, 3, 1], y_range=[-5, 5, 1],
            x_length=9, y_length=4.5,
            axis_config={"include_tip": False, "color": WHITE},
        ).shift([0, -0.1, 0])
        title = Text("Changing slope: y = m x", font_size=34).move_to([0, 3.25, 0])
        scale_note = Text("Read the values: the x and y screen scales differ.", font_size=20).move_to([0, 2.65, 0])
        labels = [Text("x", font_size=23).next_to(axes.x_axis.get_end(), [1, 0, 0]),
                  Text("y", font_size=23).next_to(axes.y_axis.get_end(), [0, 1, 0])]
        for x in [-2, -1, 1, 2]:
            labels.append(Text(str(x), font_size=19).move_to(axes.c2p(x, 0) + [0, -0.28, 0]))
        for y in [-4, -2, 2, 4]:
            labels.append(Text(str(y), font_size=19).move_to(axes.c2p(0, y) + [-0.3, 0, 0]))
        m = ValueTracker(0)
        graph = always_redraw(lambda: Line(
            axes.c2p(-2, -2 * m.get_value()),
            axes.c2p(2, 2 * m.get_value()), color=BLUE, stroke_width=5,
        ))
        point = always_redraw(lambda: Dot(axes.c2p(2, 2 * m.get_value()), color=ORANGE, radius=0.09))
        readout = always_redraw(lambda: Text(
            f"m = {m.get_value():.1f}     x = 2     y = {2 * m.get_value():.1f}",
            font_size=27, color=ORANGE,
        ).move_to([0, -2.9, 0]))
        self.add(axes, title, scale_note, *labels, graph, point, readout)
        self.wait(2)
        for target in [1, 2, -1]:
            self.play(m.animate.set_value(target), run_time=1, rate_func=linear)
            self.wait(2)
        self.wait(1)

4. Render and find the MP4

Run this from the directory containing slope_scene.py:

uv run manim -qm --disable_caching slope_scene.py SlopeLesson

For the tested version, -qm renders at 1280 × 720 and 30 frames per second. The finished file is:

media/videos/slope_scene/720p30/SlopeLesson.mp4

Open that file in a video player. This scene is silent: the information appears in its labels and numeric readout. Narration is a separate production step. You can start with the narration outline in the math animation workflow guide.

5. Check the result before changing it

Held sectionSlope mFixed xExpected y
0–2 seconds020
3–5 seconds122
6–8 seconds224
9–12 seconds−12−2

The intervals between those holds are transitions, not additional fixed targets. Check that the blue line always passes through the origin, the orange point stays at x = 2, and the readout agrees with the point. The final frame should show a downward-sloping line and y = −2.

Try changing the last target from −1 to −2. Before rendering, predict the final point: (2, −4). This still fits the configured axes. If you use a larger magnitude, expand the y range or shorten the line segment so the graph stays visible. Re-render and check the numeric values rather than judging only whether the motion looks smooth.

If the render does not work

  • No module named manim: run through the environment where you installed Manim. Confirm uv run manim --version before retrying.
  • Scene not found: use the exact file and class names in the command, including capitalization.
  • A Cairo or Pango import error: check your operating system's native dependency setup. In our macOS validation, an older Intel Homebrew configuration was selected when building Cairo for an ARM Python environment. Rebuilding against the matching ARM libraries fixed that environment; it is not a universal command to copy onto every machine.
  • The picture looks mathematically wrong: inspect the formulas passed to c2p, the axis ranges, and the held values in the table before changing styling or timing.

Choose the next lesson

Browse Manim animation examples to see other visual explanations. If you want a broader production process, use how to make math animations. If Python scene code is not the right fit, compare Manim alternatives and workflows.

LemmaReel's current rendering build and this standalone Python tutorial are separate workflows. Browser generation in LemmaReel is planned; this page does not offer an online compiler. See the current setup guide for the available product workflow.