Animation engine for explanatory math videos.
—
Flexible camera system supporting 2D and 3D perspectives, camera movement, zooming, and multi-camera setups for complex visual presentations. Cameras control what portion of the mathematical space is rendered and how it appears on screen, providing the viewing framework for all Manim animations.
The fundamental camera class providing core rendering functionality and viewport management for 2D scenes.
class Camera:
"""
Base camera class handling viewport management and rendering coordination.
Controls what region of mathematical space is visible, pixel resolution,
and rendering parameters for converting mathematical objects to screen pixels.
"""
def __init__(
background_image: str = None,
frame_center: np.ndarray = ORIGIN,
image_mode: str = "RGBA",
n_channels: int = 4,
pixel_array_dtype: str = "uint8",
cairo_line_width_multiple: float = 0.01,
use_z_index: bool = True,
background: np.ndarray = None,
pixel_height: int = None,
pixel_width: int = None,
frame_height: float = None,
frame_width: float = None,
frame_rate: float = None,
background_color: str = None,
background_opacity: float = None,
**kwargs
) -> None:
"""
Parameters:
- background_image: Path to background image file
- frame_center: Center point of camera viewport in scene coordinates
- image_mode: Color mode ("RGBA", "RGB", etc.)
- n_channels: Number of color channels
- pixel_array_dtype: Data type for pixel array ("uint8", "float64")
- cairo_line_width_multiple: Scaling factor for Cairo line rendering
- use_z_index: Whether to use z-index for depth sorting
- background: Custom background array
- pixel_height: Vertical resolution in pixels
- pixel_width: Horizontal resolution in pixels
- frame_height: Viewport height in Manim units
- frame_width: Viewport width in Manim units
- frame_rate: Animation frame rate
- background_color: Background color
- background_opacity: Background opacity
"""
# Core rendering methods
def capture_mobjects(
mobjects: list[Mobject],
**kwargs
) -> None:
"""
Render mobjects to the camera's pixel array.
Converts mathematical mobjects to screen pixels using appropriate
rendering methods for each mobject type.
Parameters:
- mobjects: List of mobjects to render
"""
def get_image(self) -> Image.Image:
"""
Get rendered image as PIL Image object.
Returns:
- Image.Image: PIL image containing rendered scene
"""
def get_pixel_array(self) -> np.ndarray:
"""
Get raw pixel array from camera buffer.
Returns:
- np.ndarray: Pixel array with shape (height, width, channels)
"""
def set_pixel_array(
pixel_array: np.ndarray,
convert_from_floats: bool = False
) -> None:
"""
Set camera's pixel array directly.
Parameters:
- pixel_array: New pixel array
- convert_from_floats: Whether to convert from float [0,1] to int [0,255]
"""
def reset(self) -> None:
"""Reset camera to initial state with clear background."""
def init_background(self) -> None:
"""Initialize background based on configuration settings."""
def resize_frame_shape(self, fixed_dimension: int = 0) -> None:
"""
Resize frame to maintain aspect ratio.
Parameters:
- fixed_dimension: Which dimension to keep fixed (0=width, 1=height)
"""
# Coordinate transformation methods
def transform_points_pre_display(
mobject: Mobject,
points: np.ndarray
) -> np.ndarray:
"""
Transform points before display rendering.
Apply camera transformations to convert scene coordinates
to screen coordinates.
Parameters:
- mobject: Mobject being transformed
- points: Points in scene coordinates
Returns:
- np.ndarray: Points in screen coordinates
"""
def points_to_pixel_coords(
points: np.ndarray
) -> np.ndarray:
"""
Convert scene points to pixel coordinates.
Parameters:
- points: Points in scene coordinates
Returns:
- np.ndarray: Points in pixel coordinates
"""
def adjust_out_of_range_points(
points: np.ndarray
) -> np.ndarray:
"""
Adjust points that fall outside visible range.
Parameters:
- points: Scene coordinate points
Returns:
- np.ndarray: Adjusted points within visible range
"""
# Properties for frame dimensions and positioning
@property
def frame_height() -> float:
"""Height of camera viewport in scene units."""
@property
def frame_width() -> float:
"""Width of camera viewport in scene units."""
@property
def frame_center() -> np.ndarray:
"""Center point of camera viewport."""
@property
def pixel_height() -> int:
"""Vertical resolution in pixels."""
@property
def pixel_width() -> int:
"""Horizontal resolution in pixels."""
@property
def background_color() -> str:
"""Background color of the camera."""
@property
def background_opacity() -> float:
"""Background opacity of the camera."""Camera that can be moved, zoomed, and rotated during animations for dynamic perspective changes and cinematic effects.
class MovingCamera(Camera):
"""
Camera that can move through the scene with animated frame adjustments.
Allows for dynamic camera movements including panning, zooming, and rotation
to create cinematic effects and follow animated objects.
"""
def __init__(
frame: Mobject = None,
fixed_dimension: int = 0,
default_frame_stroke_color: str = WHITE,
default_frame_stroke_width: float = 0,
**kwargs
) -> None:
"""
Parameters:
- frame: Mobject representing camera frame (usually ScreenRectangle)
- fixed_dimension: Which dimension to keep fixed during scaling (0=width, 1=height)
- default_frame_stroke_color: Default color for frame border
- default_frame_stroke_width: Default width for frame border
"""
# Frame manipulation methods
@property
def frame() -> Mobject:
"""Camera frame mobject that defines the viewport."""
def auto_zoom(
*mobjects: Mobject,
margin: float = 1,
only_mobjects_in_frame: bool = False,
animate: bool = False
) -> None:
"""
Automatically adjust camera to fit specified mobjects.
Parameters:
- *mobjects: Mobjects to fit in frame
- margin: Additional margin around mobjects
- only_mobjects_in_frame: Whether to consider only visible mobjects
- animate: Whether to animate the zoom
"""
def save_camera_position(self) -> None:
"""Save current camera position and zoom for later restoration."""
def restore_camera_position(self) -> None:
"""Restore previously saved camera position and zoom."""
# Animation-friendly properties
@property
def frame_height() -> float:
"""Height of camera frame (can be animated)."""
@frame_height.setter
def frame_height(value: float) -> None:
"""Set frame height (animatable)."""
@property
def frame_width() -> float:
"""Width of camera frame (can be animated)."""
@frame_width.setter
def frame_width(value: float) -> None:
"""Set frame width (animatable)."""
@property
def frame_center() -> np.ndarray:
"""Center of camera frame (can be animated)."""
@frame_center.setter
def frame_center(value: np.ndarray) -> None:
"""Set frame center (animatable)."""
# Coordinate transformation overrides
def points_to_pixel_coords(
points: np.ndarray
) -> np.ndarray:
"""Convert points to pixels accounting for frame movement."""
def adjust_out_of_range_points(
points: np.ndarray
) -> np.ndarray:
"""Adjust points based on current frame position."""Specialized camera for 3D scenes with perspective projection, camera orientation, and 3D lighting effects.
class ThreeDCamera(Camera):
"""
Camera for 3D scenes with perspective projection and orientation control.
Provides full 3D camera functionality including perspective projection,
camera positioning in 3D space, and lighting for realistic 3D rendering.
"""
def __init__(
focal_distance: float = 20.0,
shading_factor: float = 0.2,
default_distance: float = 5.0,
light_source_start_point: np.ndarray = 9*DOWN + 7*LEFT + 10*OUT,
should_apply_shading: bool = True,
exponential_projection: bool = False,
phi: float = 0,
theta: float = -90*DEGREES,
gamma: float = 0,
zoom: float = 1,
**kwargs
) -> None:
"""
Parameters:
- focal_distance: Distance from camera to focal plane
- shading_factor: Intensity of 3D shading effects
- default_distance: Default z-distance for objects
- light_source_start_point: Initial position of light source
- should_apply_shading: Whether to apply 3D shading
- exponential_projection: Whether to use exponential perspective
- phi: Camera angle from z-axis (inclination) in radians
- theta: Camera angle from x-axis (azimuth) in radians
- gamma: Camera roll angle around viewing direction
- zoom: Camera zoom factor
"""
# 3D orientation and positioning
def set_euler_angles(
phi: float = None,
theta: float = None,
gamma: float = None
) -> None:
"""
Set camera orientation using Euler angles.
Parameters:
- phi: Inclination angle (angle from z-axis)
- theta: Azimuth angle (angle from x-axis)
- gamma: Roll angle (rotation around viewing direction)
"""
def get_euler_angles(self) -> tuple[float, float, float]:
"""
Get current camera orientation angles.
Returns:
- tuple: (phi, theta, gamma) angles in radians
"""
def set_focal_distance(self, distance: float) -> None:
"""
Set camera focal distance for perspective projection.
Parameters:
- distance: Distance from camera to focal plane
"""
def set_zoom(self, zoom_factor: float) -> None:
"""
Set camera zoom level.
Parameters:
- zoom_factor: Zoom multiplier (1.0 = default, >1.0 = zoom in)
"""
# 3D transformation methods
def get_rotation_matrix(self) -> np.ndarray:
"""
Get 3D rotation matrix for current camera orientation.
Returns:
- np.ndarray: 3x3 rotation matrix
"""
def reset_rotation_matrix(self) -> None:
"""Recalculate rotation matrix from current angles."""
def transform_points_pre_display(
mobject: Mobject,
points: np.ndarray
) -> np.ndarray:
"""
Apply 3D transformations including rotation and perspective projection.
Parameters:
- mobject: Mobject being transformed
- points: 3D points in scene coordinates
Returns:
- np.ndarray: 2D points after 3D projection
"""
def apply_3d_perspective(
points: np.ndarray
) -> np.ndarray:
"""
Apply perspective projection to 3D points.
Parameters:
- points: 3D points after rotation
Returns:
- np.ndarray: 2D points after perspective projection
"""
# Lighting and shading
def get_unit_normal(
points: np.ndarray,
face_indices: np.ndarray = None
) -> np.ndarray:
"""
Calculate unit normal vectors for 3D surfaces.
Parameters:
- points: 3D surface points
- face_indices: Indices defining surface faces
Returns:
- np.ndarray: Unit normal vectors
"""
def get_shading_factor(
points: np.ndarray,
unit_normal_vect: np.ndarray
) -> float:
"""
Calculate shading factor based on lighting angle.
Parameters:
- points: 3D surface points
- unit_normal_vect: Surface normal vectors
Returns:
- float: Shading factor [0, 1]
"""
# Object management for 3D scenes
def add_fixed_orientation_mobjects(
*mobjects: Mobject
) -> None:
"""
Add mobjects that maintain fixed orientation relative to camera.
These mobjects always face the camera regardless of camera rotation,
useful for text labels and 2D elements in 3D scenes.
Parameters:
- *mobjects: Mobjects to fix orientation
"""
def add_fixed_in_frame_mobjects(
*mobjects: Mobject
) -> None:
"""
Add mobjects that remain fixed in screen space.
These mobjects maintain fixed screen positions regardless of
camera movement, useful for UI elements and legends.
Parameters:
- *mobjects: Mobjects to fix in frame
"""
def remove_fixed_orientation_mobjects(
*mobjects: Mobject
) -> None:
"""Remove mobjects from fixed orientation list."""
def remove_fixed_in_frame_mobjects(
*mobjects: Mobject
) -> None:
"""Remove mobjects from fixed frame list."""
# Value trackers for smooth animation
def get_value_trackers(self) -> list[ValueTracker]:
"""
Get value trackers for animating camera parameters.
Returns:
- list[ValueTracker]: Trackers for phi, theta, focal_distance, gamma, zoom
"""
@property
def phi_tracker() -> ValueTracker:
"""Value tracker for phi (inclination) angle."""
@property
def theta_tracker() -> ValueTracker:
"""Value tracker for theta (azimuth) angle."""
@property
def gamma_tracker() -> ValueTracker:
"""Value tracker for gamma (roll) angle."""
@property
def focal_distance_tracker() -> ValueTracker:
"""Value tracker for focal distance."""
@property
def zoom_tracker() -> ValueTracker:
"""Value tracker for zoom level."""Advanced camera classes for specific use cases and complex rendering scenarios.
class MultiCamera(Camera):
"""
Camera supporting multiple simultaneous viewpoints.
Enables split-screen effects, picture-in-picture, and other
multi-viewport visualizations.
"""
def __init__(
*cameras: Camera,
**kwargs
) -> None:
"""
Parameters:
- *cameras: Sub-cameras for different viewports
"""
def capture_mobjects(
mobjects: list[Mobject],
**kwargs
) -> None:
"""Render scene using all sub-cameras simultaneously."""
def set_split_positions(
positions: list[tuple[float, float, float, float]]
) -> None:
"""
Set viewport positions for each sub-camera.
Parameters:
- positions: List of (x, y, width, height) for each viewport
"""
class MappingCamera(Camera):
"""
Camera with coordinate space transformations and mappings.
Allows for non-linear coordinate transformations and custom
mathematical space mappings.
"""
def __init__(
mapping_func: Callable[[np.ndarray], np.ndarray] = None,
min_anchor_points: int = 50,
max_anchor_points: int = 100,
**kwargs
) -> None:
"""
Parameters:
- mapping_func: Function to transform coordinates
- min_anchor_points: Minimum points for mapping interpolation
- max_anchor_points: Maximum points for mapping interpolation
"""
def points_to_pixel_coords(
points: np.ndarray
) -> np.ndarray:
"""Apply coordinate mapping before pixel conversion."""
class SplitScreenCamera(MultiCamera):
"""
Camera for split-screen presentations with automatic layout.
Convenient wrapper for common split-screen configurations
with automatic viewport management.
"""
def __init__(
left_camera: Camera,
right_camera: Camera,
split_ratio: float = 0.5,
**kwargs
) -> None:
"""
Parameters:
- left_camera: Camera for left viewport
- right_camera: Camera for right viewport
- split_ratio: Horizontal split position (0.5 = center)
"""from manim import *
class CameraMovementExample(MovingCameraScene):
def construct(self):
# Create objects
square = Square(color=BLUE)
circle = Circle(color=RED).shift(RIGHT * 4)
self.add(square, circle)
# Move camera to focus on square
self.play(
self.camera.frame.animate.move_to(square).scale(0.5)
)
self.wait(1)
# Move camera to circle
self.play(
self.camera.frame.animate.move_to(circle)
)
self.wait(1)
# Zoom out to show both
self.play(
self.camera.frame.animate.move_to(ORIGIN).scale(2)
)class ThreeDCameraExample(ThreeDScene):
def construct(self):
# Set initial camera orientation
self.set_camera_orientation(phi=75*DEGREES, theta=45*DEGREES)
# Create 3D objects
axes = ThreeDAxes()
cube = Cube(side_length=2, fill_opacity=0.7)
sphere = Sphere(radius=1.2).shift(UP * 2)
self.add(axes, cube, sphere)
# Animate camera rotation
self.play(
self.camera.phi_tracker.animate.set_value(30*DEGREES),
self.camera.theta_tracker.animate.set_value(60*DEGREES),
run_time=3
)
# Move camera position
self.play(
self.camera.frame_center.animate.shift(RIGHT * 2),
self.camera.zoom_tracker.animate.set_value(1.5),
run_time=2
)
# Add fixed orientation text
text = Text("3D Scene", font_size=48)
self.add_fixed_in_frame_mobjects(text)
text.to_corner(UL)class AutoZoomExample(MovingCameraScene):
def construct(self):
# Create scattered objects
dots = VGroup(*[
Dot(np.random.uniform(-6, 6, 3))
for _ in range(10)
])
self.add(dots)
# Auto-zoom to fit all dots
self.camera.auto_zoom(*dots, margin=1, animate=True)
self.wait(2)
# Focus on subset
subset = dots[:3]
self.camera.auto_zoom(*subset, margin=0.5, animate=True)
self.wait(2)class MultiCameraExample(Scene):
def setup(self):
# Create split-screen camera
left_camera = Camera()
right_camera = Camera()
self.camera = SplitScreenCamera(
left_camera,
right_camera,
split_ratio=0.6
)
def construct(self):
# Content will appear in both viewports
circle = Circle(color=BLUE)
square = Square(color=RED)
self.add(circle, square)class Advanced3DCameraExample(ThreeDScene):
def construct(self):
# Create complex 3D scene
axes = ThreeDAxes()
surface = Surface(
lambda u, v: [u, v, u**2 + v**2],
u_range=[-2, 2],
v_range=[-2, 2],
resolution=16,
fill_opacity=0.8
)
self.add(axes, surface)
# Set initial position
self.set_camera_orientation(phi=60*DEGREES, theta=30*DEGREES)
# Create orbital camera movement
def orbit_camera():
self.begin_ambient_camera_rotation(rate=0.1)
# Add light tracking
self.camera.light_source.move_to([5, 5, 5])
# Animate camera parameters simultaneously
self.play(
self.camera.phi_tracker.animate.set_value(90*DEGREES),
self.camera.zoom_tracker.animate.set_value(0.8),
run_time=4
)
# Start orbital motion
orbit_camera()
self.wait(6)
self.stop_ambient_camera_rotation()class CoordinateTransformExample(Scene):
def construct(self):
# Show how camera transforms coordinates
axes = Axes()
point_in_scene = np.array([2, 1, 0])
# Convert to pixel coordinates
pixel_coords = self.camera.points_to_pixel_coords(
np.array([point_in_scene])
)[0]
# Create visual indicators
scene_dot = Dot(point_in_scene, color=RED)
pixel_text = Text(
f"Pixel: ({pixel_coords[0]:.0f}, {pixel_coords[1]:.0f})",
font_size=24
).next_to(scene_dot, UP)
coord_text = Text(
f"Scene: ({point_in_scene[0]}, {point_in_scene[1]})",
font_size=24
).next_to(scene_dot, DOWN)
self.add(axes, scene_dot, pixel_text, coord_text)Install with Tessl CLI
npx tessl i tessl/pypi-manim