How to Work with Mobjects, VMobjects, and VGroups to Build Scenes in Manim

To build scenes in Manim, use VMobject subclasses for drawable shapes, VGroup to bundle them into single transformable units, and Mobject's family tree methods for layout and animation.

Manim, the mathematical animation engine created by 3Blue1Brown and maintained in the 3b1b/manim repository, organizes every visual element into a hierarchy of mobjects (mathematical objects). Understanding the relationship between the abstract Mobject base class, the vectorized VMobject, and the container VGroup is essential for composing clean, reusable animation code.

Understanding the Mobject Hierarchy

Manim’s rendering pipeline is built around three core classes defined in manimlib/mobject/mobject.py and manimlib/mobject/types/vectorized_mobject.py.

Mobject – The Abstract Foundation

Mobject is the root class for any drawable entity. It manages the family tree through submobjects (children) and parents (containers) lists, handles updaters via add_updater and remove_updater, and provides geometric transformations through apply_points_function.

Key capabilities include:

  • Family management: Methods like add, remove, clear, add_to_back, replace_submobject, and insert_submobject manipulate the scene graph.
  • Geometry: shift, scale, rotate, move_to, next_to, and align_on_border modify self.data['point'] through the unified transformation pipeline.
  • Updaters: The always and f_always properties register callables that execute every frame, enabling continuous behaviors. These receive self.time from the Scene class.

VMobject – Vectorized Graphics

VMobject (Vectorized Mobject) extends Mobject to store per-vertex data required for GPU rendering. Defined in manimlib/mobject/types/vectorized_mobject.py, it serves as the parent for all shape primitives like Circle, Square, and Polygon.

Critical features:

  • Data buffers: Stores stroke_rgba, stroke_width, fill_rgba, joint_angle, base_normal, and fill_border_width in the data_dtype definition. This data is sent to the GPU via VShaderWrapper.
  • Styling API: set_fill, set_stroke, set_shading, and set_flat_stroke operate recursively on the family by default.
  • Path handling: get_bezier_tuples, get_subcurve, and DashedVMobject enable complex curve manipulation.
  • Group factory: VMobject.get_group_class returns VGroup, ensuring that the expression a + b automatically produces a VGroup containing both objects.

VGroup – The Container Object

VGroup (defined around line 1312 in manimlib/mobject/types/vectorized_mobject.py) inherits from both Group and VMobject. This dual inheritance allows it to behave like a Python list while retaining full vector graphics capabilities.

Key behaviors:

  • Hybrid inheritance: Because it is both a Group and VMobject, a VGroup supports indexing (vg[0]) like a list and transformations like any other drawable object.
  • Uniform inheritance: Upon creation, it copies the first child’s uniform dictionary (self.uniforms.update(self.submobjects[0].uniforms)) to ensure consistent stroke and fill settings across the group.
  • Layout utilities: Methods such as arrange, arrange_in_grid, space_out_submobjects, and sort (defined on Mobject) work transparently on VGroup instances.

Working with Families and Transformations

Effective scene building relies on manipulating the mobject family tree and applying coordinate transformations.

Managing the Scene Graph

Every mobject maintains parent-child relationships. When you call self.add(mob) in your Scene, you are attaching the mobject to the scene's family tree. Changes to a parent automatically propagate to children’s bounding boxes and shader data.

Use add_to_back to control render order (z-index), and replace_submobject to swap elements during animations without breaking family references.

Applying Coordinate Functions

All geometric transformations route through apply_points_function in mobject.py. This ensures that whether you call shift, scale, or rotate, the underlying point data in self.data['point'] is updated consistently, and bounding boxes are recalculated for the entire family.

Practical Scene Building Patterns

The following patterns demonstrate how to compose scenes using the hierarchy effectively.

Pattern 1: Simple Composition with VGroup

Group related primitives to treat them as a single transformable unit.

from manimlib import *

class SimpleGroup(Scene):
    def construct(self):
        # Create primitive VMobjects

        circle = Circle(radius=2, color=RED).set_fill(opacity=0.3)
        square = Square(side_length=2, color=BLUE).set_fill(opacity=0.5)

        # Group them – VGroup behaves like a single VMobject

        logo = VGroup(circle, square).arrange(RIGHT, buff=0.5)

        # Position the group

        logo.to_edge(UP)

        # Add to scene with an animation

        self.play(FadeIn(logo, shift=DOWN))

        # Demonstrate collective transformation

        self.play(logo.animate.scale(1.5).rotate(PI / 4))
        self.wait()

Key points:

  • VGroup automatically inherits animate, enabling chained transformations on the whole group.
  • arrange(RIGHT) uses the layout utilities defined in Mobject.

Pattern 2: Nested Groups and Per-Subobject Styling

Access individual children to apply distinct styles while maintaining group coherence.

class NestedGroups(Scene):
    def construct(self):
        # Base shapes

        outer = Circle(radius=3, color=YELLOW).set_fill(opacity=0.2)
        inner = Circle(radius=1, color=GREEN).set_fill(opacity=0.8)

        # Group the two circles

        ring = VGroup(outer, inner)
        ring.center()                     # Center both circles together

        # Add a third element that stays independent

        dot = Dot(radius=0.1, color=WHITE).next_to(ring, DOWN, buff=0.3)

        # Combine into a top‑level group

        whole = VGroup(ring, dot)

        # Style subobjects individually

        outer.set_stroke(width=8, color=ORANGE)   # only outer circle gets thick stroke

        inner.set_fill(opacity=0.4)               # dim inner fill

        self.add(whole)
        self.play(whole.animate.rotate(2 * PI), run_time=4)
        self.wait()

Key points:

  • Sub-objects are accessible via indexing (ring[0] is outer).
  • Styling calls recurse through the family by default, but you can limit recursion with recurse=False.

Pattern 3: Live Updaters on VMobjects

Use always and f_always to create frame-wise animations without explicit self.play calls.

class LiveWave(Scene):
    def construct(self):
        wave = VMobject()
        wave.set_points_as_corners([LEFT, RIGHT])   # start as a simple line

        wave.set_stroke(color=PURPLE, width=8)

        # Make the wave oscillate forever

        wave.always.apply_function(
            lambda pts: pts + np.column_stack((
                np.zeros(len(pts)),
                0.5 * np.sin(pts[:, 0] + self.time),
                np.zeros(len(pts))
            ))
        )

        self.add(wave)
        self.wait(5)   # watch the live animation

Key points:

  • wave.always.apply_function registers a frame-wise updater that mutates points.
  • self.time is supplied by the Scene base class in manimlib/scene/scene.py.

Key Source Files for Deep Dives

To understand the internals behind these patterns, study the following files in the 3b1b/manim repository:

File Purpose
manimlib/mobject/mobject.py Core implementation of the family tree, point handling, updaters, and generic transformations.
manimlib/mobject/types/vectorized_mobject.py Defines VMobject, its data buffers, styling API, and the VGroup container.
manimlib/scene/scene.py Provides the Scene class that owns the camera, orchestrates self.add, self.play, and passes the self.time variable to updaters.
manimlib/mobject/types/__init__.py Re-exports the most-used shape classes (Circle, Square, Polygon) which all inherit from VMobject.
manimlib/mobject/svg/svg_mobject.py Shows how SVG paths are converted into a VMobject hierarchy, useful when you need custom vector graphics.

Summary

  • Mobject is the abstract base that handles family trees, coordinate transforms, and updaters in manimlib/mobject/mobject.py.
  • VMobject adds GPU-ready vertex buffers and styling controls for vector graphics, serving as the parent for all shapes.
  • VGroup acts as a dual-purpose container: it supports list-like indexing while retaining full VMobject transform capabilities, making it ideal for bundling complex assets.
  • Use always and f_always updaters for continuous animations driven by self.time.
  • Reference manimlib/mobject/types/vectorized_mobject.py and manimlib/mobject/mobject.py when extending or debugging the hierarchy.

Frequently Asked Questions

What is the difference between Mobject and VMobject?

Mobject is the abstract foundation that manages the scene graph, transformations, and updaters but contains no rendering data. VMobject (Vectorized Mobject) inherits from Mobject and adds GPU-specific buffers for stroke, fill, and vertex data, making it the appropriate base for any visible shape like circles or polygons.

When should I use VGroup instead of Group?

You should use VGroup when you need a container that behaves as a single drawable object. Because VGroup inherits from both Group and VMobject, it supports vector styling and transformations while allowing list-like access to its children. Use Group only when you need a pure organizational container without the rendering overhead of VMobject.

How do I access individual submobjects in a VGroup?

You can access children via standard indexing: my_vgroup[0] returns the first submobject. You can also iterate over the group with for mob in my_vgroup:. To style individual members without affecting siblings, call methods directly on the indexed submobject, as styling operations recurse through the family by default.

Can I apply different styles to submobjects within a VGroup?

Yes. While VGroup copies the first child's uniform dictionary upon creation to ensure visual consistency, you can override styles on individual submobjects afterward. Access the child via indexing and apply methods like set_stroke or set_fill. To prevent a style call from propagating to children, pass recurse=False to the styling method.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →