| name | manim-3d |
| description | Use when creating 3D scenes in Manim Community Edition. Covers ThreeDScene, camera angles (phi, theta), ThreeDAxes, Surface and ParametricFunction for parametric 3D objects, ambient rotation, and the limitations (manim 3D is perspective-projected vector graphics: no lighting, no shadows, no z-buffer). |
Manim 3D
Manim-ce 3D is perspective-projected vector graphics: no lighting, no shadows, no z-buffer in the modern sense. For 3D-rendered math animations, use the --renderer=opengl flag or libraries like manimgl. For most pedagogical 3D math (rotating axes, parametric surfaces, projected curves), the Cairo renderer covers it.
ThreeDScene
from manim import *
class CubeIntro(ThreeDScene):
def construct(self):
self.set_camera_orientation(phi=70 * DEGREES, theta=-45 * DEGREES)
axes = ThreeDAxes()
cube = Cube(side_length=2, fill_opacity=0.3, fill_color=BLUE)
self.play(Create(axes), Create(cube))
self.begin_ambient_camera_rotation(rate=0.3)
self.wait(5)
self.stop_ambient_camera_rotation()
Subclass ThreeDScene (not Scene) for 3D capability; it provides the camera methods below.
Camera orientation
The camera has three angles in spherical coordinates:
phi: polar angle from +z axis. 0° = looking straight down z; 90° = looking along the xy-plane.
theta: azimuthal angle in the xy-plane from +x. 0° = looking along +x; rotates counterclockwise viewed from +z.
gamma: roll (in-plane rotation of the view).
self.set_camera_orientation(phi=70 * DEGREES, theta=-45 * DEGREES)
self.move_camera(phi=30 * DEGREES, theta=45 * DEGREES, run_time=2)
Use DEGREES constant (= π/180) to convert; manim's angle arguments are in radians.
A useful starting orientation: phi=70°, theta=-45° gives a typical "math textbook" 3D look.
Axes
axes = ThreeDAxes(
x_range=[-5, 5, 1],
y_range=[-5, 5, 1],
z_range=[-3, 3, 1],
x_length=8,
y_length=8,
z_length=5,
)
labels = axes.get_axis_labels(
Tex("x"), Tex("y"), Tex("z")
)
self.add(axes, labels)
coords_to_point(...) converts math coords to scene coords:
dot = Dot3D(axes.coords_to_point(2, 1, 3), color=YELLOW)
Parametric surfaces and curves
Surface (note: the class is Surface in newer manim-ce, was ParametricSurface):
def saddle(u, v):
return axes.coords_to_point(u, v, u*v / 3)
surf = Surface(
saddle,
u_range=[-2, 2],
v_range=[-2, 2],
resolution=(24, 24),
fill_opacity=0.7,
checkerboard_colors=[BLUE_D, BLUE_E],
)
3D parametric curve:
helix = ParametricFunction(
lambda t: axes.coords_to_point(np.cos(t), np.sin(t), t / (2*PI)),
t_range=[0, 4*PI],
color=YELLOW,
)
Fixed-orientation Mobjects (HUD-style)
By default, Mobjects rotate with the camera. To keep a Mobject "stuck to the screen":
title = Tex("Energy surface").to_corner(UL)
self.add_fixed_in_frame_mobjects(title)
Use this for titles, equations, axis labels you want visible regardless of camera angle.
Ambient camera rotation
self.begin_ambient_camera_rotation(rate=0.2)
self.wait(10)
self.stop_ambient_camera_rotation()
The default rotates only theta. For more control, animate the camera explicitly with move_camera.
Limitations to know
- No lighting model. Surfaces don't get shaded by light direction; "shading" is via
checkerboard_colors or fill color. Solid surfaces always look flat.
- No z-buffer ordering of vector primitives in the Cairo renderer: objects render in scene order. Use the OpenGL renderer (
manim --renderer=opengl) for correct z-ordering; some animations don't work there yet.
- High-resolution surfaces are slow.
resolution=(24, 24) is a reasonable default; (64, 64) only when you really need it.
- Animated 3D rotations through large angles can look jumpy because surfaces aren't tessellated for arbitrary view-dependent silhouettes.
When 3D earns its place
3D is worth it when the thing being explained is inherently 3D: parametric surfaces, vector fields, gradient descent on a loss surface, geometric proofs that require depth. Showing 2D concepts "in 3D" for style adds visual cost without adding clarity.
Related: [[manim-scenes]], [[manim-animations]].