Skip to content

Camera

The film's code
import manimgx as m


class CameraHero(m.Scene):
    def construct(self) -> None:
        circle = m.Circle(radius=3, color=m.BLUE)
        tangent = m.Line(4 * m.LEFT, 4 * m.RIGHT, color=m.YELLOW).shift(3 * m.UP)
        self.add(circle, tangent)
        frame = self.camera.frame
        self.play(frame.animate.scale(0.25).move_to(3 * m.UP), run_time=2)
        self.wait()
        self.play(frame.animate.scale(4).move_to(m.ORIGIN), run_time=2)

Every scene has a camera, self.camera. Its frame, self.camera.frame, is a mobject: move it to pan, scale it to zoom, and animate it as any other mobject. A frame half as large shows everything twice as large.

The camera also sets the picture's look: its background, and, for 3D, its exposure, its tone mapping, its bloom and its shadows.

Camera

What a frame shows: a view of the scene, made of mobjects, so that whatever moves a mobject moves the view.

Its frame is a rectangle in the scene whose center and size are the view: move it, scale it or animate it, and the view pans and zooms. In three dimensions (a ThreeDScene's camera), it also orbits the frame's center, with perspective: five value trackers hold its angles phi and theta, its roll gamma, its zoom and its focal_distance (phi_tracker, theta_tracker, gamma_tracker, zoom_tracker and focal_distance_tracker), and its light is a point, light_source. A camera only describes the view: it never draws.

m.Camera(*, three_d=False, frame_width=None, phi=0, theta=-90 * DEGREES, gamma=0, focal_distance=20.0, zoom=1, light_source_start_point=9 * DOWN + 7 * LEFT + 10 * OUT)
three_d

Whether it sees in three dimensions: with perspective and depth, from its orbit.

frame_width

The width of its frame, in scene units; None for the configured one (about 14.2 at 16:9). Its height is the configured one; a width out of the video's proportions stretches the picture.

phi

The angle between its line of sight and the z axis, in radians: 0 looks straight down on the xy plane.

theta

Its angle around the z axis, in radians, counterclockwise from the x axis: at −90° it looks from the side of negative y, so that x points right.

gamma

How far it turns about its line of sight, in radians.

focal_distance

Its distance from the frame's center, in scene units: the nearer, the stronger the perspective.

zoom

How much it magnifies: 2 shows everything twice as large.

light_source_start_point

Where the light is, in scene coordinates.

Source

src/manimgx/scene.py

def __init__(
    self,
    *,
    three_d: bool = False,
    frame_width: float | None = None,
    phi: float = 0,
    theta: float = -90 * DEGREES,
    gamma: float = 0,
    focal_distance: float = 20.0,
    zoom: float = 1,
    light_source_start_point: Point3D = 9 * DOWN + 7 * LEFT + 10 * OUT,
) -> None:
    from manimgx.mobject import Point

    self.three_d = three_d
    """Whether the camera sees in three dimensions: with perspective and depth, from
    its orbit."""
    self.frame = _view_rectangle(
        config.frame_height, frame_width or config.frame_width
    )
    """The view: a rectangle in the scene, centered on the point the camera looks
    at, as wide and as tall as what it shows.

    Move it, scale it or animate it to pan and zoom:
    `self.play(self.camera.frame.animate.scale(0.5))` zooms in, showing half as
    much, twice as large. Its outline is not drawn (a stroke width of 0).
    """
    self.phi_tracker, self.theta_tracker, self.gamma_tracker = (
        ValueTracker(phi),
        ValueTracker(theta),
        ValueTracker(gamma),
    )
    self.focal_distance_tracker, self.zoom_tracker = (
        ValueTracker(focal_distance),
        ValueTracker(zoom),
    )
    self.light_source = Point(light_source_start_point)
    """Where the light comes from: a point in the scene, which lights the mobjects
    shaded in three dimensions (`shade_in_3d`). Move it like any mobject."""
    # the mobjects pinned to the screen, as they were given: what they are made of
    # is read every frame (see `fixed_in_frame_mobjects`)
    self._fixed_in_frame: dict[Mobject, None] = {}
    self.background_color = config.background_color
    self.background_opacity = config.background_opacity
    """The opacity of the background, from 0 to 1: the configured one unless
    changed."""
    self.exposure = 1.0
    """How much the light that mobjects with a [material][manimgx.Material] reflect
    is scaled before it is shown: 2 is twice as bright (one stop)."""
    self.tone_mapping: Literal["linear", "agx"] = "linear"
    """How light brighter than white is shown: "linear" clips it (a lit surface's
    colors as they are, up to white), "agx" rolls it off as film does (AgX: highlights
    desaturate toward white, and nothing clips). The light lit surfaces send into a
    pixel is averaged first and shown once, as a camera records it."""
    self.ambient_occlusion = 0.0
    """How strongly the light from all around (an ambient or an environment light) is
    shut out of corners, creases and the ground under things, where the surfaces near
    them block it: 0 not at all (off), 1 Filament's strength, more darker. Only
    mobjects with a [material][manimgx.Material] are lit by that light, and only opaque
    ones shut it out or are darkened; a sun's, a point's or a spot's light is left as it
    is.

    It is found from what the view shows (Filament's screen-space ambient
    obscurance): a surface the view cannot see, such as a wall seen edge-on, shuts
    nothing out. Light bounces between the surfaces that shut it out, so light-colored
    ones darken less, and white ones hardly at all."""
    self.ambient_occlusion_radius = 0.3
    """How far around a point the surfaces that shut light out of it are sought, in
    scene units: about the size of the creases and contacts to darken."""
    self.bloom = 0.0
    """How much of the light that mobjects with a [material][manimgx.Material] send
    into the view the lens spreads around it, as a glow (bloom), from 0 to 1: 0 none
    (off), 0.05 a lens's faint glow, plain around light brighter than white, 1 all of
    it. What the glow spreads, the light leaves (Filament's interpolating bloom): the
    view keeps the light it has, spread.

    It is spread from the light the view shows: what lies nearer, a mobject fixed in
    the frame too, hides the light behind it. It falls off with the distance from
    the light: half of it lies within a twentieth of the view's height, nearly all of
    it within a quarter. It is light, over everything the view shows: over a lit
    surface, it adds to the surface's light before the tone mapping shows them; over
    display paint (the background, mobjects without a material, mobjects fixed in the
    frame), the tone mapping shows it on its own and it adds to the paint, as a screen
    adds light (white stays white); where the view shows nothing (a transparent
    background), it shows alone, as opaque as it is bright. Mobjects without a material
    send no light: they never glow."""

The frame

frame

The view: a rectangle in the scene, centered on the point the camera looks at, as wide and as tall as what it shows.

Move it, scale it or animate it to pan and zoom: self.play(self.camera.frame.animate.scale(0.5)) zooms in, showing half as much, twice as large. Its outline is not drawn (a stroke width of 0).

frame_center

The point the camera looks at: its frame's center. Set it to move the frame there.

camera.frame_center
Source

src/manimgx/scene.py

def frame_center(self) -> Point3D:
    """The point the camera looks at: its frame's center. Set it to move the frame
    there."""
    return self.frame.get_center()

frame_width

The width of the view, in scene units: its frame's.

camera.frame_width
Source

src/manimgx/scene.py

def frame_width(self) -> float:
    """The width of the view, in scene units: its frame's."""
    return self.frame.width

frame_height

The height of the view, in scene units: its frame's.

camera.frame_height
Source

src/manimgx/scene.py

def frame_height(self) -> float:
    """The height of the view, in scene units: its frame's."""
    return self.frame.height

auto_zoom

Fit the view around mobjects, in two dimensions: center the frame on them, and size it to fit them.

The frame keeps its proportions: its width fits the mobjects, or its height, whichever of the two they fill more of, and margin is added to it.

camera.auto_zoom(mobjects, margin=0, animate=True)
mobjects

The mobjects to fit, the frame itself left out; at least one must remain (a ValueError otherwise).

margin

How much wider (or taller) than the mobjects the view is, in scene units.

animate

Whether to return an animation of the frame, to play, rather than fit it at once.

Returns The animation, to play; with animate False, the frame, fitted.

Source

src/manimgx/scene.py

def auto_zoom(
    self,
    mobjects: Iterable[Mobject],
    margin: float = 0,
    animate: bool = True,
) -> Animate[Mobject] | Mobject:
    """Fit the view around mobjects, in two dimensions: center the frame on them,
    and size it to fit them.

    The frame keeps its proportions: its width fits the mobjects, or its height,
    whichever of the two they fill more of, and `margin` is added to it.

    Args:
        mobjects: The mobjects to fit, the frame itself left out; at least one must
            remain (a ValueError otherwise).
        margin: How much wider (or taller) than the mobjects the view is, in scene
            units.
        animate: Whether to return an animation of the frame, to play, rather than
            fit it at once.

    Returns:
        The animation, to play; with `animate` False, the frame, fitted.
    """
    mobs = [m for m in mobjects if m is not self.frame]
    if not mobs:
        raise ValueError(
            "Could not determine bounding box of the mobjects given to 'auto_zoom'."
        )
    left, right = (
        min(m.get_critical_point(LEFT)[0] for m in mobs),
        max(m.get_critical_point(RIGHT)[0] for m in mobs),
    )
    up, down = (
        max(m.get_critical_point(UP)[1] for m in mobs),
        min(m.get_critical_point(DOWN)[1] for m in mobs),
    )
    target = (
        (self.frame.animate if animate else self.frame)
        .set_x((left + right) / 2)
        .set_y((up + down) / 2)
    )
    if (right - left) / self.frame.width > (up - down) / self.frame.height:
        return target.set(width=right - left + margin)
    return target.set(height=up - down + margin)

The look

background_color

The color of the background: the configured one unless changed. Set it to any color.

camera.background_color
Source

src/manimgx/scene.py

def background_color(self) -> ManimColor:
    """The color of the background: the configured one unless changed. Set it to any
    color."""
    return self._background_color

background_opacity

The opacity of the background, from 0 to 1: the configured one unless changed.

exposure

How much the light that mobjects with a material reflect is scaled before it is shown: 2 is twice as bright (one stop).

tone_mapping

How light brighter than white is shown: "linear" clips it (a lit surface's colors as they are, up to white), "agx" rolls it off as film does (AgX: highlights desaturate toward white, and nothing clips). The light lit surfaces send into a pixel is averaged first and shown once, as a camera records it.

bloom

How much of the light that mobjects with a material send into the view the lens spreads around it, as a glow (bloom), from 0 to 1: 0 none (off), 0.05 a lens's faint glow, plain around light brighter than white, 1 all of it. What the glow spreads, the light leaves (Filament's interpolating bloom): the view keeps the light it has, spread.

It is spread from the light the view shows: what lies nearer, a mobject fixed in the frame too, hides the light behind it. It falls off with the distance from the light: half of it lies within a twentieth of the view's height, nearly all of it within a quarter. It is light, over everything the view shows: over a lit surface, it adds to the surface's light before the tone mapping shows them; over display paint (the background, mobjects without a material, mobjects fixed in the frame), the tone mapping shows it on its own and it adds to the paint, as a screen adds light (white stays white); where the view shows nothing (a transparent background), it shows alone, as opaque as it is bright. Mobjects without a material send no light: they never glow.

ambient_occlusion

How strongly the light from all around (an ambient or an environment light) is shut out of corners, creases and the ground under things, where the surfaces near them block it: 0 not at all (off), 1 Filament's strength, more darker. Only mobjects with a material are lit by that light, and only opaque ones shut it out or are darkened; a sun's, a point's or a spot's light is left as it is.

It is found from what the view shows (Filament's screen-space ambient obscurance): a surface the view cannot see, such as a wall seen edge-on, shuts nothing out. Light bounces between the surfaces that shut it out, so light-colored ones darken less, and white ones hardly at all.

ambient_occlusion_radius

How far around a point the surfaces that shut light out of it are sought, in scene units: about the size of the creases and contacts to darken.

light_source

Where the light comes from: a point in the scene, which lights the mobjects shaded in three dimensions (shade_in_3d). Move it like any mobject.

Fixed on the screen

fixed_in_frame_mobjects

The mobjects pinned to the screen: those given to add_fixed_in_frame_mobjects, with every member of their families as they are now: a part one gains later (a number's new digits, what an updater or a Transform makes) is pinned with it.

camera.fixed_in_frame_mobjects
Source

src/manimgx/scene.py

def fixed_in_frame_mobjects(self) -> set[Mobject]:
    """The mobjects pinned to the screen: those given to
    [`add_fixed_in_frame_mobjects`][manimgx.Camera.add_fixed_in_frame_mobjects],
    with every member of their families as they are now: a part one gains later (a
    number's new digits, what an updater or a Transform makes) is pinned with it."""
    return {m for mob in self._fixed_in_frame for m in mob.get_family()}

add_fixed_in_frame_mobjects

Pin mobjects to the screen: each is drawn where its points are, as in a two-dimensional view centered on the origin, over everything else.

They stay where they are on the screen whatever the camera's angles, zoom and center: a title placed with to_corner stays in its corner. Everything in them is pinned, now and later: a member added later (a number's new digits, what an updater or a Transform makes) is pinned too.

camera.add_fixed_in_frame_mobjects(*mobjects)
*mobjects

The mobjects to pin, with their families.

Source

src/manimgx/scene.py

def add_fixed_in_frame_mobjects(self, *mobjects: Mobject) -> None:
    """Pin mobjects to the screen: each is drawn where its points are, as in a
    two-dimensional view centered on the origin, over everything else.

    They stay where they are on the screen whatever the camera's angles, zoom and
    center: a title placed with `to_corner` stays in its corner. Everything in them
    is pinned, now and later: a member added later (a number's new digits, what an
    updater or a Transform makes) is pinned too.

    Args:
        *mobjects: The mobjects to pin, with their families.
    """
    self._fixed_in_frame.update(dict.fromkeys(mobjects))

Its angles, in 3D

three_d

Whether the camera sees in three dimensions: with perspective and depth, from its orbit.

get_phi

The angle between the camera's line of sight and the z axis.

camera.get_phi()

Returns The angle, in radians.

Source

src/manimgx/scene.py

def get_phi(self) -> float:
    """The angle between the camera's line of sight and the z axis.

    Returns:
        The angle, in radians.
    """
    return self.phi_tracker.get_value()

get_theta

The camera's angle around the z axis, counterclockwise from the x axis.

camera.get_theta()

Returns The angle, in radians.

Source

src/manimgx/scene.py

def get_theta(self) -> float:
    """The camera's angle around the z axis, counterclockwise from the x axis.

    Returns:
        The angle, in radians.
    """
    return self.theta_tracker.get_value()

get_gamma

How far the camera turns about its line of sight.

camera.get_gamma()

Returns The angle, in radians.

Source

src/manimgx/scene.py

def get_gamma(self) -> float:
    """How far the camera turns about its line of sight.

    Returns:
        The angle, in radians.
    """
    return self.gamma_tracker.get_value()

get_zoom

How much the camera magnifies.

camera.get_zoom()

Returns 1 for none.

Source

src/manimgx/scene.py

def get_zoom(self) -> float:
    """How much the camera magnifies.

    Returns:
        The zoom: 1 for none.
    """
    return self.zoom_tracker.get_value()

get_focal_distance

The camera's distance from its frame's center.

camera.get_focal_distance()

Returns The distance, in scene units.

Source

src/manimgx/scene.py

def get_focal_distance(self) -> float:
    """The camera's distance from its frame's center.

    Returns:
        The distance, in scene units.
    """
    return self.focal_distance_tracker.get_value()

set_phi

Set the angle between the camera's line of sight and the z axis, at once.

camera.set_phi(value)
value

The angle, in radians: 0 looks straight down on the xy plane.

Source

src/manimgx/scene.py

def set_phi(self, value: float) -> None:
    """Set the angle between the camera's line of sight and the z axis, at once.

    Args:
        value: The angle, in radians: 0 looks straight down on the xy plane.
    """
    self.phi_tracker.set_value(value)

set_theta

Set the camera's angle around the z axis, at once.

camera.set_theta(value)
value

The angle, in radians, counterclockwise from the x axis.

Source

src/manimgx/scene.py

def set_theta(self, value: float) -> None:
    """Set the camera's angle around the z axis, at once.

    Args:
        value: The angle, in radians, counterclockwise from the x axis.
    """
    self.theta_tracker.set_value(value)

set_gamma

Set how far the camera turns about its line of sight, at once.

camera.set_gamma(value)
value

The angle, in radians.

Source

src/manimgx/scene.py

def set_gamma(self, value: float) -> None:
    """Set how far the camera turns about its line of sight, at once.

    Args:
        value: The angle, in radians.
    """
    self.gamma_tracker.set_value(value)