vs Manim

This page compares VectorMation with Manim Community side by side. Each example is taken from the Manim examples gallery. All VectorMation examples are runnable from examples/manim/.

Key Differences

VectorMation

Manim

Output

Native SVG

Rasterized video (Cairo/OpenGL)

Approach

Declarative: define animations, view any frame

Imperative: self.play() each step

Structure

Plain script

Class with construct() method

Preview

Hot-reload browser viewer

Re-render on every change

Angles

Degrees

Radians (PI / 4)

Timing

Seconds as floats (start=1, end=3)

run_time per self.play() call

Coordinates

Pixel-based (1920x1080 viewBox, center at 960, 540)

Unit-based (centered at ORIGIN)

Philosophy

Manim treats animation as a sequence of actions: create a shape, play an animation, wait, play another. Every step is explicitly played through self.play().

VectorMation treats objects as functions of time: you define what should happen and when, and the library renders any frame on demand. This means no self.play() calls, no self.wait(), and instant hot-reload in the browser.


Basic Concepts

Example: ManimCELogo

Logo recreation with basic shapes.

"""Manim equivalent: ManimCELogo -- community edition logo with shapes and M."""
from vectormation.objects import *

# Initialize the animation frame
canvas = VectorMathAnim()
canvas.set_background(fill='#ece6e2')
# 1 manim unit = 135px
unit = 135

# Shapes at manim default sizes (radius=1u, side=2u), centered at screen center
cir = Circle(unit, fill='#87c2a5', stroke_width=0, fill_opacity=1).shift(dx=-unit)
sq = Rectangle(2 * unit, 2 * unit, fill='#525893', stroke_width=0, fill_opacity=1).center_to_pos().shift(dy=-unit)
tr = EquilateralTriangle(2 * unit, fill='#e07a5f', stroke_width=0, fill_opacity=1).shift(dx=unit)

# M character: Manim uses MathTex("\mathbb{M}").scale(7), shifted 2.25*LEFT + 1.5*UP
char = TexObject(r'$$\mathbb{M}$$', font_size=350, fill='#343434').objects[0]
char.center_to_pos()
char.shift(dx=round(-2.25 * unit), dy=round(-1.5 * unit))

# Group and center (order matters for z-layering: triangle, square, circle, M)
logo = VCollection(tr, sq, cir, char)
logo.center_to_pos()

canvas.add_objects(logo)

canvas.show()
class ManimCELogo(Scene):
    def construct(self):
        self.camera.background_color = "#ece6e2"
        ds_m = MathTex(r"\mathbb{M}", fill_color="#343434").scale(7)
        ds_m.shift(2.25 * LEFT + 1.5 * UP)
        circle = Circle(color="#87c2a5", fill_opacity=1).shift(LEFT)
        square = Square(color="#525893", fill_opacity=1).shift(UP)
        triangle = Triangle(color="#e07a5f", fill_opacity=1).shift(RIGHT)
        logo = VGroup(triangle, square, circle, ds_m)
        logo.move_to(ORIGIN)
        self.add(logo)

Example: BraceAnnotation

A line with curly brace annotations.

"""Manim equivalent: BraceAnnotation -- brace with text label."""
from vectormation.objects import *

canvas = VectorMathAnim()
canvas.set_background()
# Manim: dot at [-2,-1] and [2,1] => centered on canvas
dot1 = Dot(cx=660, cy=690, fill='#fff', fill_opacity=1)
dot2 = Dot(cx=1260, cy=390, fill='#fff', fill_opacity=1)
line = Line(x1=660, y1=690, x2=1260, y2=390, stroke='#FF862F', stroke_width=8)
b1 = Brace(line, direction='down', label='Horizontal distance')
b2 = Brace(line, direction='right', label=r'$x-x_1$')

canvas.add_objects(line, dot1, dot2, b1, b2)

canvas.show()
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)

Example: VectorArrow

An arrow on a number plane with labels.

"""Manim equivalent: VectorArrow -- arrow with labeled endpoints on a grid."""
from vectormation.objects import *

canvas = VectorMathAnim()
canvas.set_background()
plane = NumberPlane()

# Arrow from origin (0,0) to (2,2) in logical coordinates
ox, oy = plane.coords_to_point(0, 0)
tx, ty = plane.coords_to_point(2, 2)
origin = Dot(cx=ox, cy=oy, r=8, fill='#fff', fill_opacity=1)
arrow = Arrow(x1=ox, y1=oy, x2=tx, y2=ty, stroke='#FFFFFF')

origin_text = Text('(0, 0)', font_size=36)
origin_text.center_to_pos(posx=ox, posy=oy + 50)
tip_text = Text('(2, 2)', font_size=36)
tip_text.center_to_pos(posx=tx + 50, posy=ty - 10)

canvas.add_objects(plane, origin, arrow, origin_text, tip_text)

canvas.show()
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)

Animations

Example: PointMovingOnShapes

A dot moves along a circle and then rotates around a point.

"""Manim equivalent: PointMovingOnShapes -- dot moving along and around shapes."""
from vectormation.objects import *

canvas = VectorMathAnim()
canvas.set_background()
cir = Circle(0, cx=960, cy=540, stroke='blue', fill_opacity=0)
cir.r.move_to(0, 1, 150)
dot = Dot(r=12, fill='#fff', fill_opacity=1)
dot.c.move_to(1, 2, (1110, 540))
dot.c.rotate_around(2, 4, (960, 540), 360)
dot.c.rotate_around(4, 5.5, (1260, 540), 360)
l = Line(960 + 3 * 150, 540, 960 + 5 * 150, 540)

canvas.add_objects(cir, dot, l)

canvas.show()
class PointMovingOnShapes(Scene):
    def construct(self):
        circle = Circle(radius=1, color=BLUE)
        dot = Dot()
        dot2 = dot.copy().shift(RIGHT)
        self.add(dot)
        line = Line([3, 0, 0], [5, 0, 0])
        self.add(line)
        self.play(GrowFromCenter(circle))
        self.play(Transform(dot, dot2))
        self.play(MoveAlongPath(dot, circle), run_time=2, rate_func=linear)
        self.play(Rotating(dot, about_point=[2, 0, 0]), run_time=1.5)

Example: MovingAround

A square shifts, changes colour, scales, and rotates.

"""Manim equivalent: MovingAround -- chain of shift, set_fill, scale, rotate."""
from vectormation.objects import *

canvas = VectorMathAnim()
canvas.set_background()
square = Rectangle(200, 200, fill='#58C4DD', fill_opacity=1)
square.center_to_pos(posx=960, posy=540)
square.shift(dx=-200, start=0, end=1)
square.set_color(1, 2, '#FF862F')
square.scale(0.3, 2, 3)
square.rotate_by(3, 4, 23)

canvas.add_objects(square)

canvas.show()
class MovingAround(Scene):
    def construct(self):
        square = Square(color=BLUE, fill_opacity=1)
        self.play(square.animate.shift(LEFT))
        self.play(square.animate.set_fill(ORANGE))
        self.play(square.animate.scale(0.3))
        self.play(square.animate.rotate(0.4))

Example: MovingAngle

An angle indicator tracks a rotating line.

"""Manim equivalent: MovingAngle -- angle arc between two lines, animated via updater."""
from vectormation.objects import *

canvas = VectorMathAnim()
canvas.set_background()
cx, cy = 960, 540
l1 = Line(x1=cx, y1=cy, x2=cx + 200, y2=cy, stroke='#fff')
l2 = Line(x1=cx, y1=cy, x2=cx + 200, y2=cy, stroke='#FFFF00')
l2.p2.rotate_around(0, 2, (cx, cy), degrees=-110)
l2.p2.rotate_around(2, 3.5, (cx, cy), degrees=70)
l2.p2.rotate_around(3.5, 5, (cx, cy), degrees=-140)

angle = Angle(vertex=l1.p1, p1=l1.p2, p2=l2.p2, radius=40,
              label=r'$\theta$', label_font_size=36)

canvas.add_objects(l1, l2, angle)

canvas.show()
class MovingAngle(Scene):
    def construct(self):
        rotation_center = LEFT
        theta_tracker = ValueTracker(110)
        line1 = Line(LEFT, RIGHT)
        line_moving = Line(LEFT, RIGHT)
        line_ref = line_moving.copy()
        line_moving.rotate(
            theta_tracker.get_value() * DEGREES, about_point=rotation_center
        )
        a = Angle(line1, line_moving, radius=0.5, other_angle=False)
        tex = MathTex(r"\theta").move_to(
            Angle(line1, line_moving, radius=0.5 + 3 * SMALL_BUFF,
                  other_angle=False).point_from_proportion(0.5)
        )
        self.add(line1, line_moving, a, tex)
        line_moving.add_updater(
            lambda x: x.become(line_ref.copy()).rotate(
                theta_tracker.get_value() * DEGREES, about_point=rotation_center
            )
        )
        a.add_updater(
            lambda x: x.become(Angle(line1, line_moving, radius=0.5))
        )
        self.play(theta_tracker.animate.set_value(40))
        self.play(theta_tracker.animate.increment_value(140))
        self.play(theta_tracker.animate.set_value(350))

Example: MovingDots

Two dots move independently with a line tracking between them.

"""Manim equivalent: MovingDots -- two dots linked by a line, each moving independently."""
from vectormation.objects import *

canvas = VectorMathAnim()
canvas.set_background()
d1, d2 = Dot(r=12, fill='#58C4DD', fill_opacity=1), Dot(r=12, fill='#83C167', fill_opacity=1)
l = Line(x1=960, y1=540, x2=860, y2=540, stroke='#FC6255')
d1.c.set_to(l.p1)
d2.c.set_to(l.p2)
l.p1.move_to(1.5, 3, (960, 240))
l.p2.move_to(1, 2.5, (1260, 540))

canvas.add_objects(l, d1, d2)

canvas.show()
class MovingDots(Scene):
    def construct(self):
        d1, d2 = Dot(color=BLUE), Dot(color=GREEN)
        dg = VGroup(d1, d2).arrange(RIGHT, buff=1)
        l1 = Line(d1.get_center(), d2.get_center()).set_color(RED)
        x = ValueTracker(0)
        y = ValueTracker(0)
        d1.add_updater(lambda z: z.set_x(x.get_value()))
        d2.add_updater(lambda z: z.set_y(y.get_value()))
        l1.add_updater(lambda z: z.become(Line(d1.get_center(), d2.get_center())))
        self.add(d1, d2, l1)
        self.play(x.animate.set_value(5))
        self.play(y.animate.set_value(4))

Example: MovingGroupToDestination

A group of dots shifts so that the red dot lands on the yellow dot.

"""Manim equivalent: MovingGroupToDestination -- move a group to align with a target."""
from vectormation.objects import *

canvas = VectorMathAnim()
canvas.set_background()
d1 = Dot(cx=660, cy=540)
d2 = Dot(cx=760, cy=540)
d3 = Dot(cx=860, cy=540, fill='#FC6255')
d4 = Dot(cx=960, cy=540)
group = VCollection(d1, d2, d3, d4)

dest = Dot(cx=1200, cy=300, fill='#FFFF00')

# Shift the group so d3 (the red dot) ends up at dest
target_x, target_y = 1200, 300
ref_x, ref_y = d3.c.at_time(0)
group.shift(dx=target_x - ref_x, dy=target_y - ref_y, start=0.5, end=2)

canvas.add_objects(group, dest)

canvas.show()
class MovingGroupToDestination(Scene):
    def construct(self):
        group = VGroup(Dot(LEFT), Dot(ORIGIN), Dot(RIGHT, color=RED), Dot(2 * RIGHT)).scale(1.4)
        dest = Dot([4, 3, 0], color=YELLOW)
        self.add(group, dest)
        self.play(group.animate.shift(dest.get_center() - group[2].get_center()))

Example: MovingFrameBox

A surrounding rectangle highlights parts of an equation.

"""Manim equivalent: MovingFrameBox -- LaTeX with animated bounding rectangles."""
from vectormation.objects import *

canvas = VectorMathAnim(width=1920, height=540)
canvas.set_background()
text = TexObject(r'$$\frac{d}{dx}f(x)g(x)=f(x)\frac{d}{dx}g(x)+g(x)\frac{d}{dx}f(x)$$', font_size=120)
text.center_to_pos(posx=960, posy=270)
text.write(0, 1)
rect1 = text.brect(0, 13, 25, follow=False, buff=12)
rect2 = text.brect(0, 26, 38, follow=False, buff=12)
obj = MorphObject(rect1, rect2, start=2, end=4)

canvas.add_objects(text, obj, rect1, rect2)

canvas.show()
class MovingFrameBox(Scene):
    def construct(self):
        text = MathTex(
            r"\frac{d}{dx}f(x)g(x)=", r"f(x)\frac{d}{dx}g(x)", "+",
            r"g(x)\frac{d}{dx}f(x)"
        )
        self.play(Write(text))
        framebox1 = SurroundingRectangle(text[1], buff=.1)
        framebox2 = SurroundingRectangle(text[3], buff=.1)
        self.play(Create(framebox1))
        self.play(ReplacementTransform(framebox1, framebox2))

Example: RotationUpdater

A line rotates forward, then reverses direction.

"""Manim equivalent: RotationUpdater -- line rotating forward and backward."""
from vectormation.objects import *

canvas = VectorMathAnim()
canvas.set_background()
l1 = Line(x1=760, y1=540, x2=960, y2=540)
l2 = Line(x1=760, y1=540, x2=960, y2=540, stroke=(255, 0, 0))
l2.p1.rotate_around(1, 3, l2.p2.at_time(0), degrees=130)
l2.p1.rotate_around(3, 5, l2.p2.at_time(0), degrees=130, clockwise=True)

canvas.add_objects(l1, l2)

canvas.show()
class RotationUpdater(Scene):
    def construct(self):
        def updater_forth(mobj, dt):
            mobj.rotate_about_origin(dt)
        def updater_back(mobj, dt):
            mobj.rotate_about_origin(-dt)
        line_reference = Line(ORIGIN, LEFT).set_color(WHITE)
        line_moving = Line(ORIGIN, LEFT).set_color(YELLOW)
        line_moving.add_updater(updater_forth)
        self.add(line_reference, line_moving)
        self.wait(2)
        line_moving.remove_updater(updater_forth)
        line_moving.add_updater(updater_back)
        self.wait(2)

Example: PointWithTrace

A dot moves along a path leaving a visible trace behind it.

"""Manim equivalent: PointWithTrace -- dot leaves a traced path as it moves."""
from vectormation.objects import *

canvas = VectorMathAnim()
canvas.set_background()
point = Dot(cx=960, cy=540)
point.c.rotate_around(1, 2, (1060, 540), 180, clockwise=True)
point.c.move_to(2.5, 3.5, (1160, 440))
point.c.move_to(4, 5, (1060, 440))
trace = Trace(point.c, start=0, end=5, dt=1/60)

canvas.add_objects(trace, point)

canvas.show()
class PointWithTrace(Scene):
    def construct(self):
        path = VMobject()
        dot = Dot()
        path.set_points_as_corners([dot.get_center(), dot.get_center()])
        def update_path(path):
            previous_path = path.copy()
            previous_path.add_points_as_corners([dot.get_center()])
            path.become(previous_path)
        path.add_updater(update_path)
        self.add(path, dot)
        self.play(Rotating(dot, angle=PI, about_point=RIGHT, run_time=2))
        self.play(dot.animate.shift(UP))
        self.play(dot.animate.shift(LEFT))

Example: SineCurveUnitCircle

A dot moves around a unit circle, tracing out a sine curve.

"""Manim equivalent: SineCurveUnitCircle -- sine wave traced from a unit circle."""
from vectormation.objects import *
import math

canvas = VectorMathAnim()
canvas.set_background()

ax = Axes(x_range=(-3, 14), y_range=(-1.5, 1.5), equal_aspect=True, x_tick_type='pi_tex')
c2p = ax.coords_to_point

RATE, DURATION = 0.5, 4
angle = lambda t: 2 * math.pi * RATE * t
cx, cy = c2p(-1, 0)
unit_px = c2p(1, 0)[0] - c2p(0, 0)[0]

# Sine curve growing from x=0
curve = ax.plot(math.sin, x_range=(0, 0), stroke='#FCE94F')
curve._domain_max.set_onward(0, angle)

# Unit circle at (-1, 0)
circle = Circle(r=unit_px, cx=cx, cy=cy, stroke='red', stroke_width=2, fill_opacity=0)

# Orbiting dot + sine-tracking dot
dot = Dot(fill='#FFFF00')
dot.c.set_onward(0, lambda t: c2p(-1 + math.cos(angle(t)), math.sin(angle(t))))

curve_dot = Dot(fill='#FFFF00', r=5)
curve_dot.c.set_onward(0, lambda t: c2p(angle(t), math.sin(angle(t))))

# Radius line and horizontal connector
radius_line = Line(x1=cx, y1=cy, stroke='#58C4DD', stroke_width=2)
radius_line.p2.set_onward(0, lambda t: dot.c.at_time(t))

h_line = Line(stroke='#FFFF00', stroke_width=2, opacity=0.6)
h_line.p1.set_onward(0, lambda t: dot.c.at_time(t))
h_line.p2.set_onward(0, lambda t: curve_dot.c.at_time(t))

canvas.add_objects(ax, circle, radius_line, h_line, dot, curve_dot)

canvas.show(end=DURATION)
class SineCurveUnitCircle(Scene):
    # Full source: ~60 lines across show_axis(), show_circle(),
    # move_dot_and_draw_curve() with dt-based updaters and
    # always_redraw() for the radius line, connecting line, and curve.
    def construct(self):
        self.show_axis()
        self.show_circle()
        self.move_dot_and_draw_curve()
        self.wait()

Plotting

Example: SinAndCosFunctionPlot

Sine and cosine curves on shared axes.

"""Manim equivalent: SinAndCosFunctionPlot -- sin and cos on axes."""
from vectormation.objects import *
import math

canvas = VectorMathAnim()
canvas.set_background()
axes = Axes(x_range=(-2 * math.pi, 2 * math.pi), y_range=(-1.5, 1.5),
            x_label='x', y_label='y')
axes.add_function(math.sin, label=r'$\sin(x)$', stroke='#58C4DD')
axes.add_function(math.cos, label=r'$\cos(x)$', stroke='#FC6255')

canvas.add_objects(axes)

canvas.show()
class SinAndCosFunctionPlot(Scene):
    def construct(self):
        axes = Axes(
            x_range=[-10, 10.3, 1], y_range=[-1.5, 1.5, 1],
            x_length=10, axis_config={"color": GREEN},
            x_axis_config={"numbers_to_include": np.arange(-10, 10.01, 2)},
            tips=False,
        )
        sin_graph = axes.plot(lambda x: np.sin(x), color=BLUE)
        cos_graph = axes.plot(lambda x: np.cos(x), color=RED)
        sin_label = axes.get_graph_label(sin_graph, r"\sin(x)", x_val=-10)
        cos_label = axes.get_graph_label(cos_graph, label=r"\cos(x)")
        self.add(axes, sin_graph, cos_graph, sin_label, cos_label)

Example: ArgMinExample

A dot slides along a parabola to its minimum.

"""Manim equivalent: ArgMinExample -- dot moving along a parabola to its minimum."""
from vectormation.objects import *
from vectormation import easings, attributes

canvas = VectorMathAnim()
canvas.set_background()
ax = Axes(x_range=(0, 10), y_range=(0, 55), tex_ticks=True)

func = lambda x: 2 * (x - 5) ** 2
curve = ax.plot(func, stroke='#C55F73')

# Dot that travels along the curve to the minimum using graph_position
dot = Dot(fill='#FFFF00')
x_val = attributes.Real(0, 0)
x_val.move_to(0, 3, 5, easing=easings.smooth)
dot.c.set_onward(0, ax.graph_position(func, x_val))

canvas.add_objects(ax, dot)

canvas.show(end=3)
class ArgMinExample(Scene):
    def construct(self):
        ax = Axes(x_range=[0, 10], y_range=[0, 100, 10],
                  axis_config={"include_tip": False})
        t = ValueTracker(0)
        def func(x):
            return 2 * (x - 5) ** 2
        graph = ax.plot(func, color=MAROON)
        initial_point = [ax.coords_to_point(t.get_value(), func(t.get_value()))]
        dot = Dot(point=initial_point)
        dot.add_updater(
            lambda x: x.move_to(ax.c2p(t.get_value(), func(t.get_value())))
        )
        x_space = np.linspace(*ax.x_range[:2], 200)
        minimum_index = func(x_space).argmin()
        self.add(ax, graph, dot)
        self.play(t.animate.set_value(x_space[minimum_index]))

Example: GraphAreaPlot

Shaded areas, vertical lines, and Riemann rectangles on a plot.

"""Manim equivalent: GraphAreaPlot -- two curves with Riemann rects and area between curves."""
from vectormation.objects import *

canvas = VectorMathAnim()
canvas.set_background()
ax = Axes(x_range=(0, 5), y_range=(0, 6))

f1 = lambda x: 4 * x - x ** 2
f2 = lambda x: 0.8 * x ** 2 - 3 * x + 4
curve1 = ax.plot(f1, stroke='#58C4DD')
curve2 = ax.plot(f2, stroke='#83C167')

# Riemann rectangles under curve1 in x=[0.3, 0.6]
riemann = ax.get_riemann_rectangles(f1, x_range=(0.3, 0.6), dx=0.03,
                                    fill='#58C4DD', fill_opacity=0.5)

# Shaded area between curve2 and curve1 in x=[2, 3]
area = ax.get_area(curve2, x_range=(2, 3), bounded_graph=curve1,
                   fill='#888888', fill_opacity=0.5)

# Vertical reference lines at x=2 and x=3
vline1 = ax.get_vertical_line(2, y_val=f1(2))
vline2 = ax.get_vertical_line(3, y_val=f1(3))

canvas.add_objects(ax)

canvas.show()
class GraphAreaPlot(Scene):
    def construct(self):
        ax = Axes(x_range=[0, 5], y_range=[0, 6],
                  x_axis_config={"numbers_to_include": [2, 3]}, tips=False)
        curve_1 = ax.plot(lambda x: 4 * x - x ** 2, x_range=[0, 4], color=BLUE_C)
        curve_2 = ax.plot(
            lambda x: 0.8 * x ** 2 - 3 * x + 4, x_range=[0, 4], color=GREEN_B
        )
        line_1 = ax.get_vertical_line(ax.input_to_graph_point(2, curve_1), color=YELLOW)
        line_2 = ax.get_vertical_line(ax.i2gp(3, curve_1), color=YELLOW)
        riemann_area = ax.get_riemann_rectangles(
            curve_1, x_range=[0.3, 0.6], dx=0.03, color=BLUE, fill_opacity=0.5
        )
        area = ax.get_area(curve_2, [2, 3], bounded_graph=curve_1, color=GREY, opacity=0.5)
        self.add(ax, curve_1, curve_2, line_1, line_2, riemann_area, area)

Example: PolygonOnAxes

A rectangle’s area tracks along a hyperbola as the width changes.

"""Manim equivalent: PolygonOnAxes -- dynamic rectangle under a 1/x curve."""
from vectormation.objects import *
from vectormation import attributes

canvas = VectorMathAnim()
canvas.set_background()
ax = Axes(x_range=(0, 10), y_range=(0, 10), tex_ticks=True)

f = lambda x: 25 / x if x > 0.1 else 100
curve = ax.plot(f, stroke='yellow')

# Animated t from 5 -> 9 -> 2.5 -> 5
t_attr = attributes.Real(0, 5)
t_attr.move_to(0, 2, 9)
t_attr.move_to(2, 4, 2.5)
t_attr.move_to(4, 6, 5)

rect = ax.get_rect(0, 0, t_attr, t_attr.apply(f),
                   fill='blue', fill_opacity=0.4, stroke='yellow')

dot = Dot(fill='#fff')
dot.c.set_onward(0, ax.graph_position(f, t_attr))

# Also add final morphing because we can
star = Star(outer_radius=100, fill='green')
circle = Cross(200)
rect2 = Rectangle(200, 200, fill='green')
coll = VCollection(star, circle, rect2).arrange_in_grid(1, 3, buff=100).center_to_pos()
coll.spin(7, 8)
morph = MorphObject(rect, star, 6, 7)
morph2 = MorphObject(curve, circle, 6, 7)
morph3 = MorphObject(ax.axes, rect2, 6, 7)
dot.fadeout(6, 7)

canvas.add_objects(ax, dot)
canvas.add_objects(morph, morph2, morph3, coll)

canvas.show()
class PolygonOnAxes(Scene):
    def construct(self):
        ax = Axes(x_range=[0, 10], y_range=[0, 10], x_length=6, y_length=6,
                  axis_config={"include_tip": False})
        t = ValueTracker(5)
        k = 25
        graph = ax.plot(lambda x: k / x, color=YELLOW_D,
                        x_range=[k / 10, 10.0, 0.01], use_smoothing=False)
        def get_rectangle():
            polygon = Polygon(*[ax.c2p(*i) for i in
                [(t.get_value(), k / t.get_value()), (0, k / t.get_value()),
                 (0, 0), (t.get_value(), 0)]])
            polygon.set_fill(BLUE, opacity=0.5).set_stroke(YELLOW_B)
            return polygon
        polygon = always_redraw(get_rectangle)
        dot = Dot()
        dot.add_updater(lambda x: x.move_to(ax.c2p(t.get_value(), k / t.get_value())))
        self.add(ax, graph, dot)
        self.play(Create(polygon))
        self.play(t.animate.set_value(10))
        self.play(t.animate.set_value(k / 10))
        self.play(t.animate.set_value(5))

Example: HeatDiagramPlot

A line graph plotting temperature against heat with axis labels.

"""Manim equivalent: HeatDiagramPlot -- line graph from discrete data points."""
from vectormation.objects import *

canvas = VectorMathAnim()
canvas.set_background()
ax = Axes(x_range=(0, 40), y_range=(-8, 32),
          x_label=r'$\Delta Q$', y_label=r'$T[^\circ C]$')

x_vals = [0, 8, 38, 39]
y_vals = [20, 0, 0, -5]
graph = ax.plot_line_graph(x_vals, y_vals, stroke='#FFFF00')
canvas.add_objects(ax)

canvas.show()
class HeatDiagramPlot(Scene):
    def construct(self):
        ax = Axes(
            x_range=[0, 40, 5], y_range=[-8, 32, 5], x_length=9, y_length=6,
            x_axis_config={"numbers_to_include": np.arange(0, 40, 5)},
            y_axis_config={"numbers_to_include": np.arange(-5, 34, 5)},
            tips=False,
        )
        labels = ax.get_axis_labels(
            x_label=Tex(r"$\Delta Q$"), y_label=Tex(r"T[$^\circ C$]")
        )
        x_vals = [0, 8, 38, 39]
        y_vals = [20, 0, 0, -5]
        graph = ax.plot_line_graph(x_values=x_vals, y_values=y_vals)
        self.add(ax, labels, graph)

Advanced

Example: GradientImageFromArray

A black-to-white linear gradient with a green bounding rectangle.

"""Manim equivalent: GradientImageFromArray -- linear gradient with bounding rectangle."""
from vectormation.objects import *
from vectormation.colors import LinearGradient

canvas = VectorMathAnim()
canvas.set_background()

# Black-to-white linear gradient
grad = LinearGradient([('0%', '#000'), ('100%', '#fff')])
canvas.add_gradient(grad)

# Gradient rectangle centered on screen
rect = Rectangle(width=600, height=400, x=660, y=340, fill=grad, stroke_width=0)

# Green bounding rectangle
border = SurroundingRectangle(rect, buff=0, stroke='#00FF00', stroke_width=4,
                              fill_opacity=0)

canvas.add_objects(rect, border)

canvas.show()
class GradientImageFromArray(Scene):
    def construct(self):
        n = 256
        imageArray = np.uint8(
            [[i * 256 / n for i in range(0, n)] for _ in range(0, n)]
        )
        image = ImageMobject(imageArray).scale(2)
        image.background_rectangle = SurroundingRectangle(image, GREEN)
        self.add(image, image.background_rectangle)

Example: BooleanOperations

Boolean operations (intersection, union, exclusion, difference) on two overlapping ellipses.

"""Manim equivalent: BooleanOperations -- boolean ops on overlapping ellipses."""
from vectormation import *

canvas = VectorMathAnim()
canvas.set_background(fill='#1a1a2e')

ellipse1 = Ellipse(rx=270, ry=338, cx=825, cy=540,
                   fill=BLUE, fill_opacity=0.5, stroke=BLUE, stroke_width=10)
ellipse2 = Ellipse(rx=270, ry=338, cx=1095, cy=540,
                   fill=RED, fill_opacity=0.5, stroke=RED, stroke_width=10)

title = Text('Boolean Operations', x=960, y=120, font_size=72, fill='#fff',
             stroke_width=0, text_anchor='middle')

canvas.add_objects(ellipse1, ellipse2, title)
ellipse1.fadein(start=0, end=0.5)
ellipse2.fadein(start=0, end=0.5)
title.fadein(start=0, end=0.5)

# Each boolean op: appears at ellipse overlap, then scales down and moves under its label.
# Staggered like Manim: one at a time, label fades in after move.
ops = [
    ('Intersection', Intersection, GREEN, 1600, 250),
    ('Union',        Union,        ORANGE, 1600, 700),
    ('Exclusion',    Exclusion,    YELLOW, 300, 250),
    ('Difference',   Difference,   PINK, 300, 700),
]

t = 1.0
for name, cls, color, tx, ty in ops:
    op = cls(ellipse1, ellipse2, fill=color, fill_opacity=0.5, creation=t, stroke_width=10)
    canvas.add_objects(op)
    op.scale(0.4, start=t + 0.5, end=t + 1.3)
    op.center_to_pos(posx=tx, posy=ty + 100, start=t + 0.5, end=t + 1.3)
    label = Text(name, x=tx, y=ty - 70, font_size=48, fill='#ddd',
                 stroke_width=0, text_anchor='middle')
    canvas.add_objects(label)
    label.fadein(start=t, end=t + 0.5)
    t += 1.5

canvas.show(end=t + 0.5)
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 = Ellipse(
            width=4.0, height=5.0, fill_opacity=0.5, color=RED, stroke_width=10
        ).move_to(RIGHT)
        bool_ops_text = MarkupText("<u>Boolean Operation</u>").next_to(ellipse1, UP * 3)
        ellipse_group = Group(ellipse1, ellipse2)
        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))
        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(e, DOWN, buff=difference_text.height * 3))
        difference_text.next_to(d, UP)
        self.play(FadeIn(difference_text))

Example: ZoomedInset

A zoomed inset magnifies a small region of the canvas.

"""Zoomed inset camera: magnify a region of the canvas."""
from vectormation import (
    VectorMathAnim, Dot, Text, ZoomedInset,
    LinearGradient, Circle
)

canvas = VectorMathAnim()

# Gradient canvas background (mimics the pixelated ImageMobject in Manim)
grad = LinearGradient(
    [('0%', '#001430'), ('25%', '#643200'), ('50%', '#1e6400'), ('75%', '#0000c8'),
     ('100%', '#ff0021')],
    x1='0%', y1='0%', x2='100%', y2='100%',
)
canvas.add_def(grad)
canvas.set_background(fill=grad)

# Create some small objects that benefit from zooming
for i in range(5):
    d = Dot(cx=400 + i * 30, cy=540, r=6, fill='#e94560')
    canvas.add_objects(d)
    d.fadein(start=0, end=0.5)

label = Text('tiny dots', x=460, y=590, font_size=14, text_anchor='middle')
canvas.add_objects(label)
label.fadein(start=0, end=0.5)

# Small shape in the destination area so the inset has content after moving
cir = Circle(r=30, cx=900, cy=350, fill='BLUE', stroke='#58C4DD')
cir_label = Text('tiny circle', x=900, y=415, font_size=14, text_anchor='middle')
canvas.add_objects(cir, cir_label)

# Source and display must share the same aspect ratio (3:2 here)
zi = ZoomedInset(
    canvas,
    source=(360, 480, 200, 133),     # 3:2 region around dots
    display=(1100, 200, 600, 400),   # 3:2 display area
    frame_color='#FFFF00',
    display_color='#FFFF00',
)
canvas.add_objects(zi)
zi.fadein(start=0.5, end=1)

# Animate the source sweeping across the canvas
zi.move_source(800, 300, start=2, end=4)

canvas.show(end=5)
class MovingZoomedSceneAround(ZoomedScene):
    def __init__(self, **kwargs):
        ZoomedScene.__init__(
            self, zoom_factor=0.3,
            zoomed_display_height=1, zoomed_display_width=6,
            image_frame_stroke_width=20,
            zoomed_camera_config={"default_frame_stroke_width": 3},
            **kwargs
        )

    def construct(self):
        dot = Dot().shift(UL * 2)
        image = ImageMobject(np.uint8([[0, 100, 30, 200], [255, 0, 5, 33]]))
        image.height = 7
        self.add(image, dot)
        frame = self.zoomed_camera.frame
        zoomed_display = self.zoomed_display
        zoomed_display_frame = zoomed_display.display_frame
        frame.move_to(dot)
        frame.set_color(PURPLE)
        zoomed_display_frame.set_color(RED)
        zoomed_display.shift(DOWN)
        zd_rect = BackgroundRectangle(zoomed_display, fill_opacity=0, buff=MED_SMALL_BUFF)
        self.add_foreground_mobject(zd_rect)
        unfold_camera = UpdateFromFunc(zd_rect, lambda rect: rect.replace(zoomed_display))
        self.play(Create(frame))
        self.activate_zooming()
        self.play(self.get_zoomed_display_pop_out_animation(), unfold_camera)
        self.play(frame.animate.shift(2.5 * DOWN + 4 * LEFT))
        self.play(zoomed_display.animate.shift(2 * UP + 2 * RIGHT).scale(1.5))

3D

VectorMation renders 3D scenes using orthographic projection to SVG, with animatable camera angles (phi/theta), Lambertian shading, depth-sorted rendering, LaTeX axis labels, and filled surfaces with checkerboard colours.

Example: FixedInFrameMObjectTest

Labeled 3D axes with a title.

"""Manim equivalent: FixedInFrameMObjectTest -- labeled 3D axes."""
from vectormation.objects import *

canvas = VectorMathAnim()
canvas.set_background()

axes = ThreeDAxes()

title = Text('3D Axes', font_size=48, fill='#fff', stroke_width=0)
title.to_corner('UL')

canvas.add_objects(axes, title)

canvas.show()
class FixedInFrameMObjectTest(ThreeDScene):
    def construct(self):
        axes = ThreeDAxes()
        self.set_camera_orientation(phi=75 * DEGREES, theta=-45 * DEGREES)
        text3d = Text("This is a 3D text")
        self.add_fixed_in_frame_mobjects(text3d)
        text3d.to_corner(UL)
        self.add(axes)

Example: ThreeDSurfacePlot

A Gaussian surface with checkerboard colours on 3D axes.

"""Manim equivalent: ThreeDSurfacePlot -- Gaussian surface on 3D axes."""
from vectormation.objects import *
import math

canvas = VectorMathAnim()
canvas.set_background()

axes = ThreeDAxes(x_range=(-3, 3), y_range=(-3, 3), z_range=(0, 1.5))

sigma = 0.4
def gaussian(x, y):
    d2 = x * x + y * y
    return math.exp(-d2 / (2.0 * sigma * sigma))

axes.plot_surface(gaussian, resolution=(24, 24),
                  checkerboard_colors=('#FF862F', '#4488ff'),
                  stroke_color='#225588', stroke_width=0.3,
                  fill_opacity=0.85)

canvas.add_objects(axes)

canvas.show()
class ThreeDSurfacePlot(ThreeDScene):
    def construct(self):
        resolution_fa = 24
        self.set_camera_orientation(phi=75 * DEGREES, theta=-30 * DEGREES)
        def param_gauss(u, v):
            x, y = u, v
            sigma, mu = 0.4, [0.0, 0.0]
            d = np.sqrt((x - mu[0])**2 + (y - mu[1])**2)
            z = np.exp(-(d**2 / (2.0 * sigma**2)))
            return np.array([x, y, z])
        gauss_plane = Surface(param_gauss,
            resolution=(resolution_fa, resolution_fa),
            v_range=[-2, +2], u_range=[-2, +2])
        gauss_plane.scale(2, about_point=ORIGIN)
        gauss_plane.set_style(fill_opacity=1, stroke_color=GREEN)
        gauss_plane.set_fill_by_checkerboard(ORANGE, BLUE, opacity=0.5)
        axes = ThreeDAxes()
        self.add(axes, gauss_plane)

Example: ThreeDLightSourcePosition

A sphere with Lambertian shading and checkerboard colours on 3D axes.

"""Manim equivalent: ThreeDLightSourcePosition -- sphere on 3D axes."""
from vectormation.objects import *

canvas = VectorMathAnim()
canvas.set_background()

axes = ThreeDAxes(x_range=(-3, 3), y_range=(-3, 3), z_range=(-2, 2))

sphere = Sphere3D(radius=1.5, fill_color='#FC6255',
                  checkerboard_colors=('#FC6255', '#c44030'),
                  resolution=(16, 32), fill_opacity=0.9)
axes.add_surface(sphere)

canvas.add_objects(axes)

canvas.show()
class ThreeDLightSourcePosition(ThreeDScene):
    def construct(self):
        axes = ThreeDAxes()
        sphere = Surface(
            lambda u, v: np.array([
                1.5 * np.cos(u) * np.cos(v),
                1.5 * np.cos(u) * np.sin(v),
                1.5 * np.sin(u)
            ]), v_range=[0, TAU], u_range=[-PI / 2, PI / 2],
            checkerboard_colors=[RED_D, RED_E], resolution=(15, 32))
        self.renderer.camera.light_source.move_to(3 * IN)
        self.set_camera_orientation(phi=75 * DEGREES, theta=30 * DEGREES)
        self.add(axes, sphere)

Example: ThreeDCameraRotation

A saddle surface with the camera rotating 360 degrees.

"""Demonstrate 3D camera rotation animation."""
from vectormation.objects import *
import math

canvas = VectorMathAnim()
canvas.set_background()

axes = ThreeDAxes(x_range=(-3, 3), y_range=(-3, 3), z_range=(-2, 2))

# Add a filled surface
def saddle(x, y):
    return (x**2 - y**2) / 6

axes.plot_surface(saddle, resolution=(20, 20),
                  fill_color='#58C4DD', fill_opacity=0.8)

# Rotate camera 360 degrees over 6 seconds
axes.set_camera_orientation(0, 6, theta=axes.theta.at_time(0) + math.tau)

canvas.add_objects(axes)

canvas.show(end=6)
class ThreeDCameraRotation(ThreeDScene):
    def construct(self):
        axes = ThreeDAxes()
        self.set_camera_orientation(phi=75 * DEGREES, theta=30 * DEGREES)
        self.add(axes)
        self.begin_ambient_camera_rotation(rate=0.1)
        self.wait(6)

Example: ParametricCurve3DExample

A helix drawn as a parametric curve in 3D space.

"""Demonstrate ParametricCurve3D with a helix."""
from vectormation.objects import *
import math

canvas = VectorMathAnim()
canvas.set_background()

axes = ThreeDAxes(x_range=(-2, 2), y_range=(-2, 2), z_range=(-1, 2.5))

# A helix wrapping around the z-axis
def helix(t):
    return (math.cos(t), math.sin(t), t / (2 * math.pi))

curve = ParametricCurve3D(helix, t_range=(0, 4 * math.pi), num_points=200,
                          stroke='#FFFF00', stroke_width=3)
axes.add_3d(curve)

# A dot at the start
axes.add_3d(Dot3D((1, 0, 0), radius=6, fill='#FC6255'))

canvas.add_objects(axes)

canvas.show()
class ParametricCurve3DExample(ThreeDScene):
    def construct(self):
        axes = ThreeDAxes()
        self.set_camera_orientation(phi=75 * DEGREES, theta=-45 * DEGREES)
        curve = ParametricFunction(
            lambda t: np.array([np.cos(t), np.sin(t), t / (2 * PI)]),
            t_range=[0, 4 * PI], color=YELLOW
        )
        dot = Dot3D(axes.c2p(1, 0, 0), color=RED)
        self.add(axes, curve, dot)

Example: FollowingGraphCamera

Camera zooms in and follows a dot sliding along a sine curve.

"""Manim equivalent: FollowingGraphCamera -- camera follows a dot along a graph."""
from vectormation.objects import *
import math

canvas = VectorMathAnim()
canvas.set_background()

ax = Axes(x_range=(-1, 10), y_range=(-1, 10))
func = lambda x: math.sin(x)
ax.plot(func, stroke='#58C4DD')

# Dot moves along the sine curve from x=0 to x=3*pi over [0.5, 2.0]
dot = Dot(cx=ax.coords_to_point(0, 0)[0], cy=ax.coords_to_point(0, 0)[1], fill='#FF8C00')
x_coor = lambda t: min(max((t - 0.5) / 1.5, 0), 1) * 3 * math.pi
dot.c.set_onward(0, lambda t: ax.coords_to_point(x_coor(t), func(x_coor(t)), t))

# Camera: zoom in, follow the dot, then zoom out
canvas.camera_zoom(2, start=0, end=0.5, cx=dot.c.at_time(0)[0], cy=dot.c.at_time(0)[1])
canvas.camera_follow(dot, start=0.5, end=2.0)
canvas.camera_reset(start=2.0, end=2.5)

canvas.add_objects(ax, dot)

canvas.show()
class FollowingGraphCamera(MovingCameraScene):
    def construct(self):
        self.camera.frame.save_state()
        ax = Axes(x_range=[-1, 10], y_range=[-1, 10])
        graph = ax.plot(lambda x: np.sin(x), color=BLUE,
                        x_range=[0, 3 * PI])
        dot = Dot(ax.i2gp(0, graph), color=ORANGE)
        self.add(ax, graph, dot)
        self.play(self.camera.frame.animate.scale(0.5).move_to(dot))
        def update_camera(mob):
            mob.move_to(dot.get_center())
        self.camera.frame.add_updater(update_camera)
        self.play(MoveAlongPath(dot, graph, rate_func=linear),
                  run_time=8)
        self.camera.frame.remove_updater(update_camera)
        self.play(Restore(self.camera.frame))

Summary

VectorMation recreates all 25 Manim example gallery scenes.

VectorMation replaces Manim’s imperative self.play() pattern with a declarative, time-based approach. Animations are defined by their time interval and the library can render any frame instantly. This makes iteration faster (browser hot-reload) and code shorter (no ceremony of classes and play calls).