Skip to main content

Camera Basics

The camera determines what is visible in your scene and from what perspective. ManimVTK provides different camera types for different needs.
For 2D scenes, the camera is mostly invisible. For 3D scenes, camera control is essential.

Camera Types

2D Camera (Scene)

The default camera for 2D scenes:
class Basic2D(Scene):
    def construct(self):
        circle = Circle()
        self.play(Create(circle))
        # Camera is fixed, looking straight at the XY plane
Characteristics:
  • Fixed perspective
  • Orthographic projection
  • Default view: looking down Z-axis

3D Camera (ThreeDScene)

Camera with full 3D control:
class Basic3D(ThreeDScene):
    def construct(self):
        # Set camera angle
        self.set_camera_orientation(
            phi=60 * DEGREES,
            theta=30 * DEGREES
        )
        
        sphere = Sphere()
        self.play(Create(sphere))
Characteristics:
  • Adjustable viewing angle
  • Can rotate around objects
  • Perspective projection

Moving Camera (MovingCameraScene)

Camera that can zoom and pan in 2D:
class MovingCamera(MovingCameraScene):
    def construct(self):
        square = Square()
        self.add(square)
        
        # Zoom in
        self.play(self.camera.frame.animate.scale(0.5))
        
        # Pan to the right
        self.play(self.camera.frame.animate.shift(RIGHT * 2))

3D Camera Control

Setting Initial Orientation

class CameraOrientation(ThreeDScene):
    def construct(self):
        # Set at the beginning
        self.set_camera_orientation(
            phi=75 * DEGREES,      # Vertical angle (0 = top view, 90 = side view)
            theta=-30 * DEGREES,   # Horizontal rotation
            distance=8,            # Distance from origin
            gamma=0 * DEGREES      # Camera roll (usually 0)
        )
        
        # Add objects
        axes = ThreeDAxes()
        self.add(axes)
Understanding angles:
  • phi: Vertical angle from Z-axis (0° = top, 90° = side)
  • theta: Horizontal rotation around Z-axis
  • distance: How far the camera is from the origin
  • gamma: Camera rotation (tilt)

Animating Camera Movement

class CameraMovement(ThreeDScene):
    def construct(self):
        sphere = Sphere()
        self.add(sphere)
        
        # Initial position
        self.set_camera_orientation(phi=60 * DEGREES, theta=30 * DEGREES)
        self.wait()
        
        # Move camera
        self.move_camera(
            phi=90 * DEGREES,
            theta=45 * DEGREES,
            run_time=3
        )
        self.wait()

Ambient Camera Rotation

Continuous rotation for showcasing 3D objects:
class AmbientRotation(ThreeDScene):
    def construct(self):
        torus = Torus()
        self.add(torus)
        
        # Start rotation
        self.begin_ambient_camera_rotation(rate=0.2)
        
        # Let it rotate for 5 seconds
        self.wait(5)
        
        # Stop rotation
        self.stop_ambient_camera_rotation()
Rate parameter:
  • Positive: Counter-clockwise rotation
  • Negative: Clockwise rotation
  • Typical values: 0.1 to 0.3

Camera at a Specific Point

Point camera at an object:
class LookAtPoint(ThreeDScene):
    def construct(self):
        # Create objects at different locations
        sphere1 = Sphere().shift(LEFT * 2)
        sphere2 = Sphere().shift(RIGHT * 2)
        
        self.add(sphere1, sphere2)
        
        # Look at first sphere
        self.set_camera_orientation(phi=60 * DEGREES, theta=0)
        self.wait()
        
        # Adjust to look at second sphere
        self.move_camera(theta=30 * DEGREES, run_time=2)
        self.wait()

Moving Camera (2D)

Frame Control

class FrameControl(MovingCameraScene):
    def construct(self):
        # Get the camera frame
        frame = self.camera.frame
        
        # Create content larger than the frame
        grid = NumberPlane(x_range=[-10, 10], y_range=[-10, 10])
        self.add(grid)
        
        # Pan around
        self.play(frame.animate.shift(RIGHT * 3))
        self.play(frame.animate.shift(UP * 2))
        
        # Return to origin
        self.play(frame.animate.move_to(ORIGIN))

Zooming

class ZoomExample(MovingCameraScene):
    def construct(self):
        circle = Circle(radius=0.1)
        text = Text("Tiny text", font_size=12).next_to(circle, UP, buff=0.1)
        self.add(circle, text)
        
        # Zoom in to see details
        self.play(self.camera.frame.animate.scale(0.2))
        self.wait()
        
        # Zoom out
        self.play(self.camera.frame.animate.scale(5))
        self.wait()

Following Objects

class FollowObject(MovingCameraScene):
    def construct(self):
        dot = Dot()
        self.add(dot)
        
        # Camera follows the dot
        self.camera.frame.add_updater(
            lambda m: m.move_to(dot.get_center())
        )
        
        # Move the dot around
        self.play(dot.animate.shift(RIGHT * 5), run_time=3)
        self.play(dot.animate.shift(UP * 3), run_time=2)
        
        # Stop following
        self.camera.frame.clear_updaters()

Camera Presets

Common 3D Views

class CommonViews(ThreeDScene):
    def construct(self):
        axes = ThreeDAxes()
        self.add(axes)
        
        # Top view
        self.set_camera_orientation(phi=0, theta=0)
        self.wait()
        
        # Front view
        self.move_camera(phi=90 * DEGREES, theta=0)
        self.wait()
        
        # Side view
        self.move_camera(phi=90 * DEGREES, theta=90 * DEGREES)
        self.wait()
        
        # Isometric view
        self.move_camera(phi=60 * DEGREES, theta=45 * DEGREES)
        self.wait()

Reset Camera

# Reset to default position
self.set_camera_orientation(phi=0, theta=0, distance=8)

# Or in MovingCameraScene
self.play(self.camera.frame.animate.move_to(ORIGIN).scale(1))

Advanced Techniques

Camera Along Path

class CameraPath(ThreeDScene):
    def construct(self):
        surface = Sphere()
        self.add(surface)
        
        # Define camera positions
        positions = [
            (60 * DEGREES, 0 * DEGREES),
            (60 * DEGREES, 90 * DEGREES),
            (60 * DEGREES, 180 * DEGREES),
            (60 * DEGREES, 270 * DEGREES),
            (60 * DEGREES, 360 * DEGREES),
        ]
        
        # Move through positions
        for phi, theta in positions:
            self.move_camera(phi=phi, theta=theta, run_time=1.5)

Combine Zoom and Pan

class ZoomAndPan(MovingCameraScene):
    def construct(self):
        shapes = VGroup(*[Square() for _ in range(9)])
        shapes.arrange_in_grid(3, 3, buff=1)
        self.add(shapes)
        
        # Zoom and pan to top-left square
        self.play(
            self.camera.frame.animate
                .scale(0.3)
                .move_to(shapes[0])
        )
        self.wait()

Smooth Camera Transitions

class SmoothTransition(ThreeDScene):
    def construct(self):
        cube = Cube()
        self.add(cube)
        
        # Smooth camera movement with rate function
        self.set_camera_orientation(phi=60 * DEGREES, theta=0)
        
        self.move_camera(
            phi=90 * DEGREES,
            theta=180 * DEGREES,
            run_time=4,
            rate_func=smooth  # Smooth ease in/out
        )

Best Practices

  • 2D content: Use standard Scene
  • 3D objects: Use ThreeDScene with good phi/theta
  • Detail work: Use MovingCameraScene to zoom
# Good - slow enough to follow
self.move_camera(phi=90 * DEGREES, run_time=3)

# Too fast - disorienting
self.move_camera(phi=90 * DEGREES, run_time=0.2)
Set initial camera position that shows your object well:
# Good starting point for most 3D objects
self.set_camera_orientation(phi=60 * DEGREES, theta=45 * DEGREES)
# Good - slow rotation for showcase
self.begin_ambient_camera_rotation(rate=0.1)

# Too fast - nauseating
self.begin_ambient_camera_rotation(rate=1.0)

Camera Configuration

Via Code

# In your scene
class ConfiguredCamera(ThreeDScene):
    def construct(self):
        # Camera settings
        self.camera.frame_width = 15
        self.camera.frame_height = 15
        
        # Your content
        sphere = Sphere()
        self.add(sphere)

Via Config File

[camera]
frame_width = 15
frame_height = 15

Common Patterns

Orbit Around Object

class OrbitPattern(ThreeDScene):
    def construct(self):
        obj = Torus()
        self.add(obj)
        
        # Set initial position
        self.set_camera_orientation(phi=60 * DEGREES, theta=0)
        
        # Orbit 360 degrees
        self.move_camera(theta=360 * DEGREES, run_time=8)

Zoom to Detail

class ZoomToDetail(MovingCameraScene):
    def construct(self):
        # Large scene
        full_scene = VGroup(*[Circle() for _ in range(20)])
        full_scene.arrange_in_grid(4, 5)
        self.add(full_scene)
        
        self.wait()
        
        # Zoom to one element
        target = full_scene[5]
        self.play(
            self.camera.frame.animate
                .scale(0.2)
                .move_to(target)
        )
        self.wait()

Pan Across Scene

class PanAcross(MovingCameraScene):
    def construct(self):
        # Create wide content
        content = VGroup(
            Text("Part 1").shift(LEFT * 6),
            Text("Part 2"),
            Text("Part 3").shift(RIGHT * 6)
        )
        self.add(content)
        
        # Pan from left to right
        self.camera.frame.move_to(LEFT * 6)
        self.play(
            self.camera.frame.animate.move_to(ORIGIN),
            run_time=3
        )
        self.play(
            self.camera.frame.animate.move_to(RIGHT * 6),
            run_time=3
        )

Next Steps