How to Configure and Control the Manim Camera System and Camera Frame for Rendering
Manim separates rendering context management into the Camera class and geometric viewport control into the CameraFrame class, allowing you to configure field-of-view, orientation, and motion via methods like set_field_of_view(), reorient(), and add_ambient_rotation().
To configure and control the Manim camera system and camera frame for rendering, you must understand the relationship between the Camera class in manimlib/camera/camera.py and the CameraFrame class in manimlib/camera/camera_frame.py. These two components manage the ModernGL rendering context and the 3D geometric viewport respectively, providing fine-grained control over perspective, orientation, and animation.
Understanding the Manim Camera Architecture
Manim’s rendering pipeline delegates responsibilities to two cooperating classes that handle distinct aspects of the visualization process.
The Camera Class
The Camera class, defined at manimlib/camera/camera.py#L25, holds the ModernGL context, framebuffer objects, and background settings. It delegates all view-related transforms to its internal CameraFrame instance. Key methods include __init__, init_frame, refresh_uniforms, get_location, and resize_frame_shape.
During each render pass, the camera updates shader uniforms—such as the view matrix, pixel size, and camera position—via Camera.refresh_uniforms (lines 38‑46). These uniforms are essential for the shaders to project 3D scene geometry onto the 2D framebuffer.
The CameraFrame Class
The CameraFrame class, located at manimlib/camera/camera_frame.py#L23, is a subclass of Mobject that represents the virtual “screen” in 3‑D space. It stores orientation, field-of-view, focal distance, and computes view matrices used by shaders. Important methods include reorient, set_euler_axes, rotate, set_focal_distance, set_field_of_view, increment_theta, add_ambient_rotation, get_view_matrix, and to_fixed_frame_point.
The view matrix itself is assembled in CameraFrame.get_view_matrix (lines 100‑116) as a 4×4 affine transform based on the current orientation, scale, and center.
Configuring Camera Parameters for Rendering
When a Scene or ThreeDScene is instantiated, it builds a Camera object (see Scene.__init__ lines 107‑113). The camera receives a frame configuration (frame_config) forwarded to CameraFrame.__init__ (line 31), allowing immediate customization of the viewport.
Setting Field of View and Focal Distance
To achieve specific photographic effects, control the camera’s optical properties directly.
set_field_of_view(fov) stores the vertical FOV in uniforms["fovy"] (lines 22‑24). The view matrix automatically uses this value on the next refresh_uniforms call.
set_focal_distance(dist) computes a new fovy from the distance and current frame height (lines 16‑19), effectively zooming the camera without explicit angle calculations.
from manimlib import *
class TelephotoEffect(Scene):
def construct(self):
# Narrow the field of view for a telephoto effect
self.camera.frame.set_field_of_view(30 * DEG)
# Alternatively, set a specific focal distance (in scene units)
self.camera.frame.set_focal_distance(8.0)
# Add a simple object to see the effect
sphere = Sphere(radius=1, color=BLUE).shift(OUT * 5)
self.add(sphere)
Adjusting Camera Orientation with Euler Angles
The scene sets a default Euler orientation (default_frame_orientation = (0, 0)) and calls self.frame.reorient(*self.default_frame_orientation) (lines 13‑14). You can override this at any time.
reorient(theta, phi, gamma, center, height) is a shortcut for set_euler_angles plus optional move_to and set_height. Angles are interpreted in degrees (DEG unit) and passed to set_euler_angles, which builds a scipy.spatial.transform.Rotation object (lines 172‑185).
rotate(angle, axis) updates the internal quaternion via Rotation.from_rotvec and stores it in uniforms["orientation"] (lines 26‑29).
from manimlib import *
class RotatingCamera(Scene):
def construct(self):
# Look down the z‑axis, then rotate 45° around x, 30° around y
self.camera.frame.reorient(theta=45, phi=30)
# Animate a smooth turn around the scene
self.play(
self.camera.frame.animate.increment_theta(PI/2, units=RADIANS),
run_time=3,
)
Enabling Ambient Rotation
For continuous, automatic camera motion without explicit animation keys, use ambient rotation.
add_ambient_rotation(speed) registers an updater that increments theta each frame via increment_theta (lines 12‑14). The speed is typically specified in degrees per second.
from manimlib import *
class SpinningView(ThreeDScene):
def construct(self):
self.camera.frame.add_ambient_rotation(0.2 * DEG) # 0.2° per second
cube = Cube(side_length=2, fill_opacity=0.6).rotate(PI/4, axis=UP)
self.add(cube)
# Let the ambient rotation run while we animate the cube
self.play(cube.animate.rotate(2 * PI, axis=OUT), run_time=6)
Controlling the Camera Frame in 3D Scenes
Beyond static configuration, the CameraFrame can be manipulated dynamically as a Mobject, allowing for complex camera animations and interactive control.
Shifting and Scaling the Frame
Since CameraFrame inherits from Mobject, it supports standard geometric operations.
shift(vector)moves the frame’s center in 3D space.scale(factor, about_point=...)rescales the frame’s width and height, effectively zooming or widening the view.
These changes propagate to the view matrix on the next render pass via refresh_uniforms.
Switching Floor Planes
The orientation of the ground plane affects how Euler angles are interpreted.
Scene.set_floor_plane("xy") forces the frame to use the "zxz" Euler axes, while "xz" selects "zxy" (lines 27‑33 in manimlib/scene/scene.py). This is useful when you want the camera to orbit around different axes or when importing models with specific up-vectors.
from manimlib import *
class FloorPlaneDemo(Scene):
def construct(self):
# Default is xy (uses "zxz" Euler axes)
self.set_floor_plane("xy")
self.camera.frame.shift(OUT * 3)
# Switch to xz floor (uses "zxy" Euler axes)
self.wait(2)
self.set_floor_plane("xz")
self.camera.frame.shift(UP * 2)
Mouse and Keyboard Interaction
Interactive scenes can respond to user input by manipulating the camera frame directly.
- Reset key: Pressing the reset key (
manim_config.key_bindings.reset) executesself.play(self.camera.frame.animate.to_default_state()), restoring the original orientation and frame shape (lines 44‑45). - Mouse motion: In
Scene.on_mouse_motion, mouse deltas are transformed into the frame’s coordinate system viaframe.to_fixed_frame_pointand applied toincrement_theta/increment_phiwhen 3‑D pan is active, enabling live rotation control (lines 51‑56).
Summary
- Manim’s camera system splits responsibilities between the
Cameraclass (manimlib/camera/camera.py), which manages the ModernGL context and framebuffers, and theCameraFrameclass (manimlib/camera/camera_frame.py), which handles the geometric viewport. - Configure rendering parameters by calling
set_field_of_view(),set_focal_distance(), or passingframe_configduring scene initialization to control perspective and zoom. - Control orientation using
reorient()with Euler angles,rotate()for quaternion-based rotation, oradd_ambient_rotation()for continuous automatic spinning. - Manipulate the frame dynamically via
shift(),scale(), and floor plane switching (set_floor_plane()) to adapt the camera to different scene layouts and interaction modes.
Frequently Asked Questions
How do I set a custom field of view in Manim?
Call self.camera.frame.set_field_of_view(fov) where fov is an angle in degrees (e.g., 30 * DEG). This value is stored in the shader uniforms at uniforms["fovy"] and takes effect on the next frame render. For a zoom effect without calculating angles, use set_focal_distance(distance) instead, which computes the appropriate FOV based on the frame height.
What is the difference between Camera and CameraFrame?
The Camera class (manimlib/camera/camera.py) owns the ModernGL context, framebuffer objects, and background color. It handles low-level tasks like refresh_uniforms() and resizing the pixel buffer. The CameraFrame class (manimlib/camera/camera_frame.py) is a subclass of Mobject that represents the virtual screen in 3D space. It stores orientation quaternions, Euler angles, and view matrices, and provides methods like reorient() and get_view_matrix() that the Camera calls during rendering.
How can I rotate the camera continuously during an animation?
Use self.camera.frame.add_ambient_rotation(speed) where speed is the rotation rate in degrees per second (e.g., 0.2 * DEG). This registers an updater that increments the camera’s theta angle each frame. To stop the rotation, you would remove the updater or set the speed to zero. For scripted rotation instead of continuous motion, use self.play(self.camera.frame.animate.increment_theta(angle)).
How do I change the floor plane orientation in Manim?
Call self.set_floor_plane(plane) where plane is either "xy" or "xz". This method, defined in manimlib/scene/scene.py, changes the Euler axis convention used by the camera frame: "xy" uses "zxz" axes (default), while "xz" uses "zxy" axes. This is useful when you want the camera to orbit around a different axis or when aligning the view with models that have a specific "up" vector. After switching, you may need to adjust the camera position using self.camera.frame.shift().
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →