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 --------------- .. list-table:: :header-rows: 1 :widths: auto * - - 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 -------------- .. admonition:: Example: ManimCELogo :class: example .. raw:: html Logo recreation with basic shapes. .. tab-set:: .. tab-item:: VectorMation .. literalinclude:: ../../examples/manim/manim_community_logo.py :language: python .. tab-item:: Manim .. code-block:: python 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) .. admonition:: Example: BraceAnnotation :class: example .. raw:: html A line with curly brace annotations. .. tab-set:: .. tab-item:: VectorMation .. literalinclude:: ../../examples/manim/brace_annotation.py :language: python .. tab-item:: Manim .. code-block:: python 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) .. admonition:: Example: VectorArrow :class: example .. raw:: html An arrow on a number plane with labels. .. tab-set:: .. tab-item:: VectorMation .. literalinclude:: ../../examples/manim/vector_arrow.py :language: python .. tab-item:: Manim .. code-block:: python 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 ---------- .. admonition:: Example: PointMovingOnShapes :class: example .. raw:: html A dot moves along a circle and then rotates around a point. .. tab-set:: .. tab-item:: VectorMation .. literalinclude:: ../../examples/manim/point_moving_on_shapes.py :language: python .. tab-item:: Manim .. code-block:: python 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) .. admonition:: Example: MovingAround :class: example .. raw:: html A square shifts, changes colour, scales, and rotates. .. tab-set:: .. tab-item:: VectorMation .. literalinclude:: ../../examples/manim/moving_around.py :language: python .. tab-item:: Manim .. code-block:: python 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)) .. admonition:: Example: MovingAngle :class: example .. raw:: html An angle indicator tracks a rotating line. .. tab-set:: .. tab-item:: VectorMation .. literalinclude:: ../../examples/manim/moving_angle.py :language: python .. tab-item:: Manim .. code-block:: python 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)) .. admonition:: Example: MovingDots :class: example .. raw:: html Two dots move independently with a line tracking between them. .. tab-set:: .. tab-item:: VectorMation .. literalinclude:: ../../examples/manim/moving_dots.py :language: python .. tab-item:: Manim .. code-block:: python 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)) .. admonition:: Example: MovingGroupToDestination :class: example .. raw:: html A group of dots shifts so that the red dot lands on the yellow dot. .. tab-set:: .. tab-item:: VectorMation .. literalinclude:: ../../examples/manim/moving_group.py :language: python .. tab-item:: Manim .. code-block:: python 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())) .. admonition:: Example: MovingFrameBox :class: example .. raw:: html A surrounding rectangle highlights parts of an equation. .. tab-set:: .. tab-item:: VectorMation .. literalinclude:: ../../examples/manim/moving_frame_box.py :language: python .. tab-item:: Manim .. code-block:: python 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)) .. admonition:: Example: RotationUpdater :class: example .. raw:: html A line rotates forward, then reverses direction. .. tab-set:: .. tab-item:: VectorMation .. literalinclude:: ../../examples/manim/rotation_updater.py :language: python .. tab-item:: Manim .. code-block:: python 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) .. admonition:: Example: PointWithTrace :class: example .. raw:: html A dot moves along a path leaving a visible trace behind it. .. tab-set:: .. tab-item:: VectorMation .. literalinclude:: ../../examples/manim/point_with_trace.py :language: python .. tab-item:: Manim .. code-block:: python 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)) .. admonition:: Example: SineCurveUnitCircle :class: example .. raw:: html A dot moves around a unit circle, tracing out a sine curve. .. tab-set:: .. tab-item:: VectorMation .. literalinclude:: ../../examples/manim/sine_curve_unit_circle.py :language: python .. tab-item:: Manim .. code-block:: python 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 -------- .. admonition:: Example: SinAndCosFunctionPlot :class: example .. raw:: html Sine and cosine curves on shared axes. .. tab-set:: .. tab-item:: VectorMation .. literalinclude:: ../../examples/manim/sin_cos_plot.py :language: python .. tab-item:: Manim .. code-block:: python 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) .. admonition:: Example: ArgMinExample :class: example .. raw:: html A dot slides along a parabola to its minimum. .. tab-set:: .. tab-item:: VectorMation .. literalinclude:: ../../examples/manim/arg_min.py :language: python .. tab-item:: Manim .. code-block:: python 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])) .. admonition:: Example: GraphAreaPlot :class: example .. raw:: html Shaded areas, vertical lines, and Riemann rectangles on a plot. .. tab-set:: .. tab-item:: VectorMation .. literalinclude:: ../../examples/manim/graph_area_plot.py :language: python .. tab-item:: Manim .. code-block:: python 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) .. admonition:: Example: PolygonOnAxes :class: example .. raw:: html A rectangle's area tracks along a hyperbola as the width changes. .. tab-set:: .. tab-item:: VectorMation .. literalinclude:: ../../examples/manim/polygon_on_axes.py :language: python .. tab-item:: Manim .. code-block:: python 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)) .. admonition:: Example: HeatDiagramPlot :class: example .. raw:: html A line graph plotting temperature against heat with axis labels. .. tab-set:: .. tab-item:: VectorMation .. literalinclude:: ../../examples/manim/heat_diagram.py :language: python .. tab-item:: Manim .. code-block:: python 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 -------- .. admonition:: Example: GradientImageFromArray :class: example .. raw:: html A black-to-white linear gradient with a green bounding rectangle. .. tab-set:: .. tab-item:: VectorMation .. literalinclude:: ../../examples/manim/gradient_image.py :language: python .. tab-item:: Manim .. code-block:: python 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) .. admonition:: Example: BooleanOperations :class: example .. raw:: html Boolean operations (intersection, union, exclusion, difference) on two overlapping ellipses. .. tab-set:: .. tab-item:: VectorMation .. literalinclude:: ../../examples/manim/boolean_operations.py :language: python .. tab-item:: Manim .. code-block:: python 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("Boolean Operation").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)) .. admonition:: Example: ZoomedInset :class: example .. raw:: html A zoomed inset magnifies a small region of the canvas. .. tab-set:: .. tab-item:: VectorMation .. literalinclude:: ../../examples/manim/zoomed_inset_manim.py :language: python .. tab-item:: Manim .. code-block:: python 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. .. admonition:: Example: FixedInFrameMObjectTest :class: example .. raw:: html Labeled 3D axes with a title. .. tab-set:: .. tab-item:: VectorMation .. literalinclude:: ../../examples/manim/three_d_axes.py :language: python .. tab-item:: Manim .. code-block:: python 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) .. admonition:: Example: ThreeDSurfacePlot :class: example .. raw:: html A Gaussian surface with checkerboard colours on 3D axes. .. tab-set:: .. tab-item:: VectorMation .. literalinclude:: ../../examples/manim/three_d_surface_plot.py :language: python .. tab-item:: Manim .. code-block:: python 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) .. admonition:: Example: ThreeDLightSourcePosition :class: example .. raw:: html A sphere with Lambertian shading and checkerboard colours on 3D axes. .. tab-set:: .. tab-item:: VectorMation .. literalinclude:: ../../examples/manim/three_d_sphere.py :language: python .. tab-item:: Manim .. code-block:: python 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) .. admonition:: Example: ThreeDCameraRotation :class: example .. raw:: html A saddle surface with the camera rotating 360 degrees. .. tab-set:: .. tab-item:: VectorMation .. literalinclude:: ../../examples/manim/three_d_camera.py :language: python .. tab-item:: Manim .. code-block:: python 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) .. admonition:: Example: ParametricCurve3DExample :class: example .. raw:: html A helix drawn as a parametric curve in 3D space. .. tab-set:: .. tab-item:: VectorMation .. literalinclude:: ../../examples/manim/three_d_helix.py :language: python .. tab-item:: Manim .. code-block:: python 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) .. admonition:: Example: FollowingGraphCamera :class: example .. raw:: html Camera zooms in and follows a dot sliding along a sine curve. .. tab-set:: .. tab-item:: VectorMation .. literalinclude:: ../../examples/manim/following_graph_camera.py :language: python .. tab-item:: Manim .. code-block:: python 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).