Core Objects
- - Circle, Square, Rectangle, Line, Arrow, Dot, Polygon, RegularPolygon
- - Text, Tex, MathTex
- - ImageMobject, SVGMobject
- - Group and VGroup
Runnable examples for the currently supported feature set. Previews are lazy and only compile when you click Render preview, so opening this page does not trigger a flood of compile requests.
Browse examples by topic, search through supported patterns, and render any scene directly in the browser.
Slow down an animation with run_time.
scene = Scene(fps=30, width=640, height=360)square = Square()scene.add(square)scene.play(square.animate.shift(UP), run_time=3)scene.wait(1)
Change stroke and fill on multiple shapes.
scene = Scene(fps=30, width=640, height=360)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)scene.add(circle, square, triangle)scene.wait(1)
Order of add() controls stacking.
scene = Scene(fps=30, width=640, height=360)circle = Circle().shift(LEFT).set_stroke(color=GREEN, width=20)square = Square().shift(UP).set_fill(YELLOW, opacity=1.0)triangle = Triangle().shift(RIGHT).set_fill(PINK, opacity=0.5)scene.add(triangle, square, circle)scene.wait(1.5)
Square to Rectangle with rotation.
scene = Scene(fps=30, width=640, height=360, backgroundColor=WHITE)m1 = Square().set_color(RED)m2 = Rectangle(width=3, height=2).set_color(RED).rotate(0.2)scene.add(m1, m2)scene.play(Transform(m1, m2), run_time=1.5)scene.wait(1)
Pair of squares morphing into circles simultaneously.
scene = Scene(fps=60, width=960, height=540, backgroundColor=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)scene.play(Transform(m1a, m1b), Transform(m2a, m2b), run_time=1.2)scene.wait(0.8)
Compares normal and rolled point-order transforms in parallel.
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) scene = ExampleRotation()scene.construct()
Morph an equilateral triangle into a square.
scene = Scene(fps=60, width=960, height=540, backgroundColor=WHITE)tri = Triangle().set_color(ORANGE).shift(LEFT*1.5)sqr = Square().set_color(ORANGE).shift(LEFT*1.5)scene.add(tri)scene.play(Transform(tri, sqr), run_time=1.2)scene.wait(0.8)
Wide rectangle easing into a circle.
scene = Scene(fps=60, width=960, height=540, backgroundColor=WHITE)rect = Rectangle(width=4, height=2).set_color(TEAL).shift(RIGHT*1.5)circ = Circle().set_color(TEAL).shift(RIGHT*1.5)scene.add(rect)scene.play(Transform(rect, circ), run_time=1.2)scene.wait(0.8)
Pan and zoom the frame.
scene = Scene(fps=30, width=640, height=360, backgroundColor=BLACK)grid = NumberPlane()scene.add(grid)scene.move_camera(position=LEFT * 2, zoom=1.3, run_time=1.5, rate_func=smooth)scene.wait(0.5)scene.move_camera(position=RIGHT * 2, zoom=0.9, run_time=1.5, rate_func=smooth)scene.wait(0.5)
Use the 2D camera frame animate proxy to pan and zoom around a layout.
scene = Scene(fps=30, width=960, height=540, backgroundColor="#020617") plane = NumberPlane().set_stroke(color=GREY_D, width=1)left = Circle(color=BLUE).scale(1.2).shift(LEFT * 3)right = Square(color=YELLOW).scale(1.1).shift(RIGHT * 3)label = Text("Frame animate", font_size=36, color=WHITE).to_edge(UP) scene.add(plane, left, right, label)scene.play(scene.camera.frame.animate.move_to(LEFT * 3).set(zoom=1.5), run_time=1.4)scene.play(scene.camera.frame.animate.move_to(RIGHT * 3).set(zoom=1.3), run_time=1.4)scene.play(scene.camera.frame.animate.move_to(ORIGIN).set(zoom=1.0), run_time=1.2)scene.wait(0.4)
Animate property setters and transforms.
scene = Scene(fps=30, width=640, height=360)square = Square().set_fill(RED, opacity=1.0)scene.add(square)scene.play(square.animate.set_fill(WHITE), run_time=1)scene.play(square.animate.shift(UP).rotate(PI / 3), run_time=1.5)scene.wait(1)
Core Animation examples.
scene = Scene(fps=30, width=640, height=360)square = Square()scene.play(FadeIn(square))scene.play(Rotate(square, PI / 4))scene.play(FadeOut(square))scene.wait(1)
Three shapes move with a small lag.
scene = Scene(fps=30, width=640, height=360)circle = Circle().shift(LEFT * 2)square = Square().shift(ORIGIN)triangle = Triangle().shift(RIGHT * 2)scene.add(circle, square, triangle)scene.play( LaggedStart( circle.animate.shift(UP * 1.5), square.animate.shift(DOWN * 1.0), triangle.animate.shift(UP * 1.5), lag_ratio=0.2 ), run_time=2, rate_func=there_and_back)scene.wait(0.5)
Two objects move at the same time with lag_ratio=0.
scene = Scene(fps=30, width=640, height=360)car = Dot(point=LEFT * 4 + UP * 1, color=RED)bus = Dot(point=LEFT * 4 + DOWN * 1, color=BLUE)scene.add(car, bus) car_end_pos = RIGHT * 4 + UP * 1gpu_end_pos_latency = LEFT * 0.5 + DOWN * 1bus_latency_time = 3 scene.play( AnimationGroup( car.animate.move_to(car_end_pos), bus.animate.move_to(gpu_end_pos_latency), lag_ratio=0 ), run_time=bus_latency_time, rate_func=linear)scene.wait(0.5)Plot a sine wave on axes.
scene = Scene(fps=30, width=640, height=360)axes = Axes(x_range=(-3, 3, 1), y_range=(-1.5, 1.5, 0.5))graph = FunctionGraph(lambda x: sin(x), color=BLUE)scene.add(axes)scene.play(Create(graph), run_time=2)scene.wait(1)
Circle, Square, Triangle shifted in different directions.
scene = Scene(fps=30, width=640, height=360)circle = Circle().shift(LEFT)square = Square().shift(UP)triangle = Triangle().shift(RIGHT)scene.add(circle, square, triangle)scene.wait(1)
Add and remove a circle with waits.
scene = Scene(fps=30, width=640, height=360)circle = Circle()scene.add(circle)scene.wait(1)scene.remove(circle)scene.wait(1)
Render `Code` and `Table` side by side to probe text fitting and layout stability.
scene = Scene(fps=30, width=960, height=540, backgroundColor="#0f172a") code = Code("total = 0\nfor value in range(4):\n total += value")table = Table([ ["Metric", "Value"], ["Frames", "120"], ["FPS", "30"], ["Status", "Ready"],]) code.to_edge(LEFT, buff=0.8)table.to_edge(RIGHT, buff=0.8)title = Text("Text-backed layout objects", font_size=34, color=WHITE).to_edge(UP) scene.play(FadeIn(title), FadeIn(code), FadeIn(table), run_time=1.2)scene.wait(0.8)
Demonstrate Write animation on Text.
scene = Scene(fps=30, width=640, height=360)hello = Text("Hello, Manim!", font_size=48)scene.play(Write(hello), run_time=2)scene.wait(1)
A blue circle animates from the left edge to the right edge over 5 seconds, tracing a smooth full-cycle sine wave at half the scene’s height while maintaining its stroke width.
scene = Scene(fps=60) dot = Circle(radius=1, color=BLUE, strokeWidth=8)scene.add(dot) start_x = -scene.frame_width / 2end_x = scene.frame_width / 2travel_time = scene.durationvx = (end_x - start_x) / travel_timeamplitude = scene.frame_height / 4 # half the scene height def drift(mob, dt): # accumulate elapsed time on the mob t = getattr(mob, "_t", 0) + dt mob._t = t x = start_x + vx * t y = amplitude * sin((t / travel_time) * TAU) # full sine wave over the run mob.position = (x, y, 0) dot.add_updater(drift)scene.wait(travel_time)dot.remove_updater(drift)
Animate a number changing smoothly.
scene = Scene(fps=30, width=640, height=360)tracker = ValueTracker(0)number = DecimalNumber(tracker.get_value()).scale(3)number.add_updater(lambda m, dt: m.set_value(tracker.get_value()).move_to(ORIGIN))scene.add(number)scene.play(tracker.animate.set_value(100), run_time=3)scene.wait(0.5)A ValueTracker updates both a dot position and a label via updaters.
scene = Scene(fps=30, width=640, height=360) tracker = ValueTracker(0)dot = Circle(radius=0.3, color=BLUE)label = DecimalNumber(tracker.get_value()).scale(1.6) def update_dot(mob, dt): x = -3 + (tracker.get_value() / 100) * 6 # map 0→100 to -3→+3 mob.move_to((x, 0, 0)) def update_label(mob, dt): mob.set_value(tracker.get_value()) mob.next_to(dot, UP, buff=0.3) dot.add_updater(update_dot)label.add_updater(update_label) scene.add(dot, label)scene.play(tracker.animate.set_value(100), run_time=3)scene.wait(0.5)ValueTracker animates a line and moving dot on Axes via always_redraw.
scene = Scene(fps=60, width=800, height=450) axes = Axes( x_range=[0, 4, 1], y_range=[0, 3, 1], x_length=7, y_length=4,).to_edge(DOWN, buff=0.6) def func(x): return 0.5 + 0.5 * x + 0.6 * sin(x * PI / 2) tracker = ValueTracker(0) line = always_redraw(lambda: axes.plot(func, x_range=[0, tracker.get_value()], color=BLUE, stroke_width=5))dot = always_redraw(lambda: Dot(axes.c2p(tracker.get_value(), func(tracker.get_value())), color=YELLOW).scale(1.1)) scene.add(axes, line, dot)scene.play(tracker.animate.set_value(4), run_time=3, rate_func=linear)scene.wait(0.5)A tracker sweeps a timeline while always_redraw grows a plot and tip dot.
scene = Scene(fps=30, width=640, height=360) axes = Axes( x_range=[1970, 2030, 10], y_range=[0, 9, 1], x_length=9, y_length=5, axis_config={"include_tip": False, "stroke_width": 2, "color": GREY_C}, x_axis_config={"numbers_to_include": [1970, 1990, 2010, 2030]},).move_to(ORIGIN) START_VISIBLE_YEAR = 1971.2year_tracker = ValueTracker(1970) # --- Data series ---def get_transistor_y(year): return 0.5 + (year - 1970) * (8.0 / 55.0) def get_clock_y(year): if year <= 2005: return 0.3 + (year - 1970) * (3.9 / 35.0) else: return 4.2 + (year - 2005) * (0.3 / 20.0) def get_cores_y(year): if year <= 2005: return 0 else: return (year - 2005) * (5.0 / 20.0) # --- Helpers ---def get_line_graph(func, color, stroke=3): return always_redraw( lambda: VGroup() if year_tracker.get_value() < START_VISIBLE_YEAR else axes.plot( func, x_range=[1970, year_tracker.get_value()], color=color, stroke_width=stroke ) ) def get_tip_dot(func, color): return always_redraw( lambda: VGroup() if year_tracker.get_value() < START_VISIBLE_YEAR else Dot( point=axes.c2p( year_tracker.get_value(), func(year_tracker.get_value()) ), color=color ).scale(0.8) ) # --- Series visuals ---line_trans = get_line_graph(get_transistor_y, TEAL, stroke=5)line_clock = get_line_graph(get_clock_y, MAROON, stroke=5)line_cores = get_line_graph(get_cores_y, GOLD, stroke=4) dot_trans = get_tip_dot(get_transistor_y, TEAL)dot_clock = get_tip_dot(get_clock_y, MAROON)dot_cores = get_tip_dot(get_cores_y, GOLD) x_label = Tex("Year", font_size=22, color=GREY_B).next_to(axes.x_axis, DOWN, buff=0.25)y_label = Tex("Performance (arb.)", font_size=22, color=GREY_B).rotate(-90 * DEGREES).next_to(axes.y_axis, LEFT, buff=0.3) scene.add(axes, line_trans, line_clock, line_cores, dot_trans, dot_clock, dot_cores, x_label, y_label)scene.play(year_tracker.animate.set_value(START_VISIBLE_YEAR), run_time=0.5, rate_func=linear)scene.play(year_tracker.animate.set_value(2005), run_time=2, rate_func=linear)scene.play(year_tracker.animate.set_value(2025), run_time=1.5, rate_func=linear)scene.wait(0.5)
Relative and absolute placement helpers.
scene = Scene(fps=30, width=640, height=360)circle = Circle()square = Square()triangle = Triangle()circle.move_to(LEFT * 2)square.next_to(circle, LEFT)triangle.align_to(circle, LEFT)scene.add(circle, square, triangle)scene.wait(1)
Build a polyline via append_points and inspect sampled anchor points.
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) scene = MobjectExample()scene.construct()
Animate a dot around a circular guide path while leaving the guide visible.
scene = Scene(fps=30, width=960, height=540, backgroundColor="#020617") orbit = Circle(radius=2.2, color=GREY_C)planet = Dot(color=YELLOW).move_to(orbit.point_from_proportion(0))sun = Dot(color=ORANGE).scale(1.8)title = Text("MoveAlongPath", font_size=34, color=WHITE).to_edge(UP) scene.add(orbit, sun, planet, title)scene.play(MoveAlongPath(planet, orbit), run_time=3.2, rate_func=linear)scene.wait(0.4)
Three dots traverse the same distance with linear, smooth, and there_and_back timing.
scene = Scene(fps=30, width=960, height=540, backgroundColor="#020617") base = Line(LEFT * 4, RIGHT * 4, color=GREY_D)labels = VGroup( Text("linear", font_size=22, color=BLUE), Text("smooth", font_size=22, color=GREEN), Text("there_and_back", font_size=22, color=YELLOW),).arrange(DOWN, aligned_edge=LEFT, buff=0.9).move_to(LEFT * 4.8) dots = VGroup( Dot(color=BLUE).move_to(LEFT * 4 + UP * 1.2), Dot(color=GREEN).move_to(LEFT * 4), Dot(color=YELLOW).move_to(LEFT * 4 + DOWN * 1.2),) scene.add( base.copy().shift(UP * 1.2), base, base.copy().shift(DOWN * 1.2), labels, dots,)scene.play(dots[0].animate.move_to(RIGHT * 4 + UP * 1.2), run_time=2.4, rate_func=linear)scene.play(dots[1].animate.move_to(RIGHT * 4), run_time=2.4, rate_func=smooth)scene.play(dots[2].animate.move_to(RIGHT * 4 + DOWN * 1.2), run_time=2.4, rate_func=there_and_back)scene.wait(0.4)
Use VoiceoverThreeDScene to keep captions and a 3D camera move aligned on the same timeline.
from manim import * class NarratedOrbitScene(VoiceoverThreeDScene): def construct(self): self.add_voiceover( "intro", "The camera reveals a cube while the caption timeline stays aligned.", 1.8, ) self.set_camera_orientation(phi=70 * DEGREES, theta=-40 * DEGREES, distance=7.5) header = self.create_header( "VoiceoverThreeDScene", "Reusable narration on top of ThreeDScene", ) self.add_fixed_in_frame_mobjects(header) axes = ThreeDAxes() cube = Cube().set_color(BLUE) cube.rotate(18 * DEGREES, axis=RIGHT) cube.rotate(24 * DEGREES, axis=UP) self.add(axes) with self.play_voiceover_block("intro"): self.play(FadeIn(cube), run_time=0.6) self.move_camera( phi=62 * DEGREES, theta=-18 * DEGREES, distance=6.9, run_time=1.2, ) self.wait(0.3) scene = NarratedOrbitScene()scene.construct()
Set an initial 3D camera orientation, reveal a cube, and animate the camera to a new view.
from manim import * class SimpleCubeScene(ThreeDScene): def construct(self): self.set_camera_orientation( phi=72 * DEGREES, theta=-42 * DEGREES, distance=7.5, ) cube = Cube() self.add(cube) self.play(FadeIn(cube, scale=0.92), run_time=0.8) self.move_camera( phi=64 * DEGREES, theta=-28 * DEGREES, distance=7.1, run_time=1.6, ) self.wait(1.2) scene = SimpleCubeScene()scene.construct()
Combine a 3D object with a title that stays fixed in screen space.
from manim import * class FixedOverlayScene(ThreeDScene): def construct(self): self.set_camera_orientation(phi=68 * DEGREES, theta=-35 * DEGREES, distance=7.2) title = Text("Fixed overlay", font_size=34, color=YELLOW).to_edge(UP) subtitle = Text("HUD stays flat while the 3D camera moves", font_size=20, color=WHITE).next_to(title, DOWN, buff=0.12) self.add_fixed_in_frame_mobjects(title, subtitle) self.add(title, subtitle) axes = ThreeDAxes() cube = Cube().set_color(BLUE) cube.rotate(20 * DEGREES, axis=RIGHT) cube.rotate(25 * DEGREES, axis=UP) self.add(axes, cube) self.begin_ambient_camera_rotation(rate=0.18) self.wait(2.5) self.stop_ambient_camera_rotation() self.wait(0.3) scene = FixedOverlayScene()scene.construct()
Stage several 3D primitives on axes and orbit the camera around them.
from manim import * class ThreeDGalleryScene(ThreeDScene): def construct(self): self.set_camera_orientation(phi=70 * DEGREES, theta=-40 * DEGREES, distance=10) axes = ThreeDAxes(x_length=6, y_length=4, z_length=4) cube = Cube().set_color(BLUE).shift(LEFT * 2) sphere = Sphere(radius=0.8).set_color(GREEN) cylinder = Cylinder(radius=0.45, height=1.8).set_color(YELLOW).shift(RIGHT * 2) label = Text("Cube Sphere Cylinder", font_size=22, color=WHITE).to_edge(DOWN) self.add_fixed_in_frame_mobjects(label) self.add(axes, cube, sphere, cylinder, label) self.play(FadeIn(cube), FadeIn(sphere), FadeIn(cylinder), run_time=1.0) self.begin_ambient_camera_rotation(rate=0.12) self.wait(2.4) self.stop_ambient_camera_rotation() self.wait(0.4) scene = ThreeDGalleryScene()scene.construct()
Plot a parametric surface and rotate the camera to read its shape.
from manim import *import numpy as np class SurfaceWaveScene(ThreeDScene): def construct(self): self.set_camera_orientation(phi=72 * DEGREES, theta=-35 * DEGREES, distance=8.5) axes = ThreeDAxes(x_length=5, y_length=5, z_length=3) surface = Surface( lambda u, v: [u, v, 0.35 * np.sin(2 * u) * np.cos(2 * v)], u_range=[-1.8, 1.8], v_range=[-1.8, 1.8], resolution=(16, 16), color=BLUE, ) mesh = SurfaceMesh(surface, color=WHITE, stroke_width=1) self.add(axes, surface, mesh) self.move_camera(phi=62 * DEGREES, theta=-15 * DEGREES, distance=7.8, run_time=1.8) self.wait(1.0) scene = SurfaceWaveScene()scene.construct()
Alternate face colors across a sampled surface with set_fill_by_checkerboard.
from manim import *import numpy as np class CheckerboardSurfaceScene(ThreeDScene): def construct(self): self.set_camera_orientation(phi=72 * DEGREES, theta=-35 * DEGREES, distance=8.0) axes = ThreeDAxes(x_length=5, y_length=5, z_length=3) surface = Surface( lambda u, v: [u, v, 0.35 * np.sin(2 * u) * np.cos(2 * v)], u_range=[-1.8, 1.8], v_range=[-1.8, 1.8], resolution=(10, 10), ) surface.set_fill_by_checkerboard(BLUE_D, BLUE_E, opacity=0.95) self.add(axes, surface) self.move_camera(phi=60 * DEGREES, theta=-18 * DEGREES, distance=7.4, run_time=1.8) self.wait(0.6) scene = CheckerboardSurfaceScene()scene.construct()