Camera and Frame in ManimGL
ManimGL's camera system is centered around the CameraFrame, accessible via self.camera.frame. This provides powerful control over both 2D and 3D perspectives.
Accessing the Camera Frame
from manimlib import *
class CameraExample(Scene):
def construct(self):
# Get the camera frame
frame = self.camera.frame
# frame is a Mobject, so it has all Mobject methods
# move_to, shift, scale, rotate, etc.2D Camera Movement
Basic Movement
# Shift the camera
self.play(frame.animate.shift(RIGHT * 2))
# Move to a specific position
self.play(frame.animate.move_to([3, 2, 0]))
# Scale (zoom)
self.play(frame.animate.scale(0.5)) # Zoom in
self.play(frame.animate.scale(2)) # Zoom outFollowing Objects
class FollowObject(Scene):
def construct(self):
frame = self.camera.frame
dot = Dot(color=RED)
# Camera follows dot
frame.add_updater(lambda m: m.move_to(dot))
# Move dot around
self.add(dot)
self.play(dot.animate.shift(RIGHT * 5), run_time=3)
self.play(dot.animate.shift(UP * 3), run_time=2)
self.wait()Frame Dimensions
# Set frame width/height
frame.set_width(10)
frame.set_height(6)
# Animate frame size
self.play(frame.animate.set_width(20), run_time=2)3D Camera Orientation
reorient() Method
The reorient() method is the primary way to set 3D camera orientation in ManimGL.
# Signature:
# frame.reorient(theta, phi, gamma=0, center=ORIGIN, height=8)
# Parameters:
# - theta: Rotation around z-axis (azimuthal angle) in degrees
# - phi: Angle from z-axis (polar angle) in degrees
# - gamma: Roll angle in degrees (optional)
# - center: Point the camera looks at (optional)
# - height: Frame height (optional)
# Common views:
frame.reorient(0, 0) # Front view (XY plane)
frame.reorient(20, 70) # Isometric-like view
frame.reorient(0, 90) # Top-down view (XY plane from above)
frame.reorient(90, 90) # Side view (YZ plane)
frame.reorient(45, 45) # Diagonal viewEuler Angles
# Set angles individually
frame.set_theta(30 * DEGREES)
frame.set_phi(70 * DEGREES)
frame.set_gamma(0 * DEGREES)
# Set all at once
frame.set_euler_angles(
theta=30 * DEGREES,
phi=70 * DEGREES,
gamma=0 * DEGREES
)
# Get current angles
theta = frame.get_theta()
phi = frame.get_phi()
gamma = frame.get_gamma()Incremental Rotation
# Increment angles (useful for animations)
frame.increment_theta(10 * DEGREES)
frame.increment_phi(5 * DEGREES)
frame.increment_gamma(2 * DEGREES)
# Animated increments
self.play(frame.animate.increment_theta(90 * DEGREES))Animating Camera
Simple Camera Animations
class AnimateCamera(Scene):
def construct(self):
frame = self.camera.frame
cube = Cube()
self.add(cube)
# Reorient to isometric view
self.play(frame.animate.reorient(20, 70), run_time=2)
self.wait()
# Rotate around object
self.play(frame.animate.increment_theta(360 * DEGREES), run_time=8)
self.wait()Continuous Camera Motion
class ContinuousRotation(Scene):
def construct(self):
frame = self.camera.frame
frame.reorient(20, 70)
sphere = Sphere(radius=2, color=BLUE)
self.add(sphere)
# Add continuous rotation updater
frame.add_updater(lambda m, dt: m.increment_theta(20 * dt))
# Let it rotate for 10 seconds
self.wait(10)
# Stop rotation
frame.clear_updaters()
self.wait()Camera Zoom In/Out
class ZoomEffect(Scene):
def construct(self):
frame = self.camera.frame
objects = VGroup(*[Square() for _ in range(5)])
objects.arrange(RIGHT, buff=1)
self.add(objects)
# Zoom out to see all objects
self.play(frame.animate.set_width(20), run_time=2)
self.wait()
# Zoom in on first object
self.play(
frame.animate.set_width(2).move_to(objects[0]),
run_time=2
)
self.wait()Fixing Mobjects in Frame
fix_in_frame() Method
Keep 2D elements fixed in screen space while the camera moves.
class FixedInFrame(Scene):
def construct(self):
frame = self.camera.frame
frame.reorient(20, 70)
# 3D object that moves with camera
cube = Cube(color=BLUE)
self.add(cube)
# 2D label that stays fixed
title = Text("Rotating Cube", font_size=60)
title.to_edge(UP)
title.fix_in_frame() # Fixes it to screen space
self.add(title)
# Rotate camera - cube rotates, title stays fixed
self.play(frame.animate.reorient(60, 80), run_time=3)
self.wait()Multiple Fixed Elements
class MultipleFixed(Scene):
def construct(self):
frame = self.camera.frame
frame.reorient(30, 70)
# 3D content
surface = Sphere(radius=2, color=BLUE, opacity=0.7)
self.add(surface)
# Fixed UI elements
title = Text("3D Visualization", font_size=48)
title.to_edge(UP)
title.fix_in_frame()
subtitle = Text("Interactive Camera", font_size=30, color=GREY)
subtitle.next_to(title, DOWN)
subtitle.fix_in_frame()
controls = Text("Press 'd' to rotate", font_size=24)
controls.to_corner(DL)
controls.fix_in_frame()
self.add(title, subtitle, controls)
# Rotate camera
self.play(frame.animate.increment_theta(180 * DEGREES), run_time=6)Reset Camera
# Reset to default state
frame.to_default_state()
# Animate reset
self.play(frame.animate.to_default_state())Camera Center
# Set what the camera looks at
frame.set_center([2, 3, 0])
# Animate center change
self.play(frame.animate.set_center([0, 0, 2]))
# Get current center
center = frame.get_center()Advanced Camera Patterns
Orbit Camera Around Object
class OrbitCamera(Scene):
def construct(self):
frame = self.camera.frame
frame.reorient(30, 70)
# Central object
torus = Torus(r1=2, r2=0.5, color=YELLOW)
self.add(torus)
# Orbit 360 degrees
self.play(
frame.animate.increment_theta(360 * DEGREES),
run_time=10,
rate_func=linear
)Camera Following Path
class CameraPath(Scene):
def construct(self):
frame = self.camera.frame
# Create path
path = Circle(radius=5)
self.add(path)
# Dot to follow
dot = Dot(color=RED)
dot.move_to(path.point_from_proportion(0))
# Camera follows dot
frame.add_updater(lambda m: m.move_to(dot))
# Move dot along path
self.play(
MoveAlongPath(dot, path),
run_time=8,
rate_func=linear
)Multiple Camera Positions
class CameraTour(Scene):
def construct(self):
frame = self.camera.frame
# Create scene
objects = VGroup(
Square(side_length=2, color=RED).shift(LEFT * 3),
Circle(radius=1, color=BLUE),
Triangle(color=GREEN).shift(RIGHT * 3)
)
self.add(objects)
# Tour each object
for obj in objects:
self.play(
frame.animate.set_width(3).move_to(obj),
run_time=2
)
self.wait()
# Return to overview
self.play(
frame.animate.set_width(14).move_to(ORIGIN),
run_time=2
)Dynamic Camera with Updater
class DynamicCamera(Scene):
def construct(self):
frame = self.camera.frame
# Moving object
dot = Dot(color=RED)
# Camera tracks and zooms based on distance from origin
def update_frame(frame):
frame.move_to(dot)
dist = np.linalg.norm(dot.get_center())
frame.set_width(max(8, dist * 2))
frame.add_updater(update_frame)
# Move dot around
self.add(dot)
self.play(dot.animate.shift(RIGHT * 5 + UP * 3), run_time=4)
self.play(dot.animate.shift(LEFT * 8 + DOWN * 2), run_time=4)
self.wait()Light Source
Accessing and Moving Light
class LightControl(Scene):
def construct(self):
frame = self.camera.frame
frame.reorient(20, 70)
# Get light source
light = self.camera.light_source
# Create 3D object
sphere = Sphere(radius=2, color=BLUE)
sphere.set_gloss(0.8)
self.add(sphere)
# Show light position (for debugging)
light_indicator = Dot(color=YELLOW)
light_indicator.add_updater(lambda m: m.move_to(light.get_center()))
self.add(light_indicator)
# Move light around
self.play(light.animate.move_to([5, 5, 5]), run_time=2)
self.wait()
self.play(light.animate.move_to([-5, -5, 5]), run_time=2)
self.wait()Best Practices
- Store frame reference:
frame = self.camera.frameat the start - Use reorient() for 3D: Cleaner than setting angles individually
- fix_in_frame() for UI: Keep labels and titles readable
- Smooth transitions: Use appropriate run_time for camera movements
- rate_func=linear: For continuous rotations
- to_default_state(): Reset camera when needed
- Updaters for following: Use updaters to track moving objects
Common Patterns
Zoom and pan
def zoom_to(self, mobject, scale_factor=1.5):
frame = self.camera.frame
self.play(
frame.animate
.set_width(mobject.get_width() * scale_factor)
.move_to(mobject),
run_time=2
)360-degree showcase
def showcase_3d(self, mobject):
frame = self.camera.frame
frame.reorient(20, 70)
self.play(
frame.animate.increment_theta(360 * DEGREES),
run_time=8,
rate_func=linear
)Picture-in-picture effect
# Small inset camera view
small_frame = self.camera.frame.copy()
small_frame.set_width(4)
small_frame.to_corner(UR, buff=0.5)
small_frame.fix_in_frame()