Python API¶
What an episode imports, and the tools an agent framework calls. Everything here is also reachable from the command line.
Episodes¶
manimvox.kit.episode.Episode ¶
Bases: Scene
narrate ¶
Play the voice line; animations inside the block run over it.
On exit, waits until the line (plus pad) has finished.
say ¶
Voice line + animations played together at its start.
steps ¶
Voice line with anims spread evenly across it, the last one finishing a moment
before the line ends. Each item is an Animation or a list of Animations played together.
beat ¶
A silent hold after a key reveal, so the picture lands before the next line.
intro ¶
Title card, then it settles into the persistent header. Anything drawn before intro()
(a cold open) is swept away first. question replaces the tagline on the card and is
what outro(answer=...) answers.
motif ¶
The title card's decoration, bottom right. The project's own mark when series.json gives one (theme.mark_svg, drawn in the accent), else two rings.
chapter ¶
Transition into a new chapter: big title, then it docks top-right. bridge is a
"So... / But..." line spoken over the finished picture before it is cleared.
outro ¶
Recap bullets and a pointer to the next episode. The last episode can
point elsewhere instead: next_label="READ", next_title="docs/design.md".
answer is spoken first, on screen under the question given to intro().
An empty recap skips the Recap heading and bullets, keeps the answer on screen,
and narration is still spoken over it.
pulse ¶
Send a glowing dot along a chain of mobjects (centre to centre).
manimvox.kit.episode.Episode3D ¶
Bases: Episode, ThreeDScene
An Episode with Manim's 3D camera: tilt and turn it with move_camera(), set_camera_orientation() and begin_ambient_camera_rotation(). The header, chapter label and progress bar stay put on screen. intro(), chapter() and outro() clear the stage, then put the camera back to the flat view, so their cards are never tilted. Text placed in the 3D scene turns with it; pin() a label to keep it flat.
Components¶
Diagram components. Each returns a VGroup the episode positions.
card ¶
card(title, body=None, w=3.8, color=BLUE, title_size=24, body_size=17, pad=0.26, fill=PANEL, icon=None)
Titled panel with an accent bar. body is a string or list of strings.
node ¶
Compact labelled box for diagrams.
arrow ¶
arrow(a, b, color=MUTED, label=None, buff=0.12, sw=3.0, label_size=14, curve=0.0, dashed=False, label_color=None, label_above=False)
Arrow from the edge of a to the edge of b (mobjects or points).
The label sits on the line; label_above=True lifts it clear, for arrows too
short to show both.
gnode ¶
A node drawn the way a product's graph view draws one: a filled shape
with the name beneath. shape is "circle" or "square". .shape is the
shape, .label the name; edges should meet .shape, not the group.
gedge ¶
Directed edge between two gnodes, rim to rim, label above its middle.
bullets ¶
Bulleted list. Returns a VGroup of rows (reveal them one by one).
stat ¶
Big number over a caption.
table ¶
Simple table. Returns VGroup of rows; each row = VGroup(bg, *cells).
code ¶
Monospace panel. colors maps substrings to colours (t2c).
line_spacing above 0 opens the lines up (try 0.8) for code read aloud line by line.
flow ¶
Horizontal chain of nodes joined by arrows. Returns (nodes, arrows).
heading ¶
Scene heading for the top of the stage (already positioned).
note ¶
Small source / caveat line pinned to the bottom of the stage.
lifeline ¶
Sequence-diagram column: a header box at top and a dashed line down to
bottom. Returns VGroup(head, line); .x is the column's x.
message ¶
Horizontal sequence-diagram message from lifeline a to lifeline b at
height y. A reply is usually dashed=True. Label sits above the arrow.
3D meshes¶
manimvox.kit.mesh ¶
3D models as triangles: a glTF, GLB, OBJ, STL or PLY file, or arrays, read into a Mesh the episode can draw.
Mesh
dataclass
¶
Triangles with Manim's z up: vertices (n, 3), faces (m, 3) indices into them, and per face its
colors as #RRGGBB (None when the source has none) and the name of the parts it came from.
load_mesh ¶
Read a 3D model: a .glb, .gltf, .obj, .stl or .ply file (export Blender scenes as glTF), or
(vertices, faces) arrays with optional per-face colours. up names the source's up axis, "y" (glTF,
OBJ) or "z" (STL, CAD); it becomes Manim's z, which points up once the camera tilts.
A model with more than max_faces triangles (at most 5000) is simplified to that many.
Files and simplifying need the trimesh extra. Raises ValueError for input it cannot draw.
Clips: frames from other libraries¶
manimvox.scenes.clip.Clip ¶
Bases: ImageMobject
Frames on the stage. Move and scale it like any mobject; self.play(clip.play(), run_time=...) plays it.
Its width is set in scene units, so a draft and a final render place it the same.
Positions on a clip are pixels of its source, counted from the top left. space is the source's size when
the frames were scaled down from it, so positions read off the original screenshot or recording still hold.
at ¶
The scene point over pixel (x, y) of the source, counted from the top left: where to put a label or an arrow tip on what the clip shows. It follows the clip when it moves, scales or zooms.
visible ¶
The part of region (x, y, w, h) inside the current view, or None when none of it is.
framing ¶
The view that shows region (x, y, w, h) with a margin, in the clip's own shape and inside the source.
zoom ¶
The animation that zooms into region (x, y, w, h) of the source inside the clip's own frame, so the clip never grows past its place on the stage. Make overlays after it: they point at the zoomed picture.
loop ¶
Keep playing the frames, over and over, under whatever the scene plays or waits next (a model
turning while the narration goes on). clip.clear_updaters() stops it on the frame it shows.
play ¶
play(run_time: float | None = None, start: float = 0.0, end: float | None = None, **kwargs) -> PlayClip
The animation that runs through the frames from start to end seconds of the clip (default: all of
it) over run_time seconds (default: that stretch's own length, so a recording plays at real speed).
Play one recording in parts, one part per narrated line.
manimvox.scenes.raster ¶
Frames from Pillow or numpy (from_frames), from pycairo (from_cairo), one image (still), a screenshot
file (screenshot) and a screen recording (from_video).
from_frames ¶
from_frames(draw: Callable[[float], object], seconds: float, fps: float = 15, width: float = DEFAULT_WIDTH) -> Clip
draw(t) returns the frame at t seconds as a PIL image or an (h, w, 3|4) uint8 array, every frame one size.
from_cairo ¶
from_cairo(draw: Callable, seconds: float, fps: float = 15, size: tuple[int, int] = (1280, 720), width: float = DEFAULT_WIDTH) -> Clip
draw(ctx, t, w, h) paints the frame at t seconds on a fresh, transparent canvas of size pixels.
still ¶
One image (PIL or array) as a one-frame Clip, sized in scene units like every clip.
screenshot ¶
A screenshot file (PNG, JPEG, WebP) as a one-frame Clip. Positions on it (at, zoom, the overlays in
manimvox.scenes) are the image's own pixels, so read them off the file.
from_video ¶
from_video(path, fps: float = 15, start: float = 0.0, end: float | None = None, width: float = DEFAULT_WIDTH, max_width: int = 1280) -> Clip
A screen recording (MP4, WebM, MOV) from start to end seconds as a Clip of fps frames a second.
Frames wider than max_width pixels are scaled down to save memory, but positions stay in the recording's
own pixels. Play it with clip.play(), which runs at the recording's speed by default.
manimvox.scenes.charts ¶
Charts in the theme: Matplotlib and Plotly figures as Clips, coloured with the project's roles.
palette ¶
Every theme role and named colour, by lower-case name.
series_colours ¶
The colours a chart gives its first, second, … series.
mpl_style ¶
Matplotlib rcParams in the theme. Build the figure inside with plt.rc_context(mpl_style()):.
from_matplotlib ¶
A Matplotlib Figure as one frame, or a FuncAnimation as all of its frames at fps.
plotly_template ¶
A Plotly template in the theme: fig.update_layout(template=plotly_template()).
from_plotly ¶
from_plotly(fig, seconds: float | None = None, fps: float = 10, size: tuple[int, int] = (1280, 720), width: float = DEFAULT_WIDTH) -> Clip
A Plotly figure as one frame, or fig(t) (a function returning a figure) as seconds of frames at fps.
Kaleido exports the frames, and it needs Chrome on the machine.
manimvox.scenes.model ¶
A 3D model rendered with real lighting and materials (PyVista, on VTK) as a Clip, the camera orbiting it.
Each frame is drawn from its own time: the camera angle and the model's animation are set for t, then the frame is rendered. Nothing runs on a clock, so a draft and a final render show the same frames.
from_pyvista ¶
from_pyvista(draw, seconds: float, fps: float = 15, size: tuple[int, int] = (1280, 720), width: float = DEFAULT_WIDTH, setup=None) -> Clip
Frames of a PyVista scene on a transparent background: setup(plotter) once (add meshes, lights),
then draw(plotter, t) before each frame at t seconds (move the camera, swap a clipped mesh, recolour).
Set the camera's position, focal_point and up in draw: PyVista's default up is z.
The same plotter serves every frame, so meshes added in setup stay. Needs the pyvista extra and OpenGL.
screen_xy ¶
Where the 3D point shows in the plotter's frame, in pixels from the top left: give it to
clip.at(x, y) to place a label or a path on the rendered picture. Call it inside draw, after the
camera is set for that frame.
from_model ¶
from_model(path, seconds: float = 4, fps: float = 15, size: tuple[int, int] = (1280, 720), width: float = DEFAULT_WIDTH, orbit: float = 60, azimuth: float = -30, elevation: float = 20, zoom: float = 1.0, animation: int | str | None = 0, loop: bool = False, up: str = 'y', color: str | None = None) -> Clip
The model in path (.glb or .gltf with its materials and animations; .obj, .stl or .ply in color,
default the theme accent) rendered with lighting on a transparent background, seconds long at fps.
The camera starts azimuth degrees round the model and elevation degrees above it, and turns
orbit degrees over the clip (0 holds still; 360 loops seamlessly); zoom above 1 moves it closer.
up is the model's up axis, "y" for glTF and OBJ, "z" for STL and CAD. A glTF animation clip (an index
or a name; None for none) plays from its start in step with the clip, holding its last pose, or
repeating if loop. Needs the pyvista extra and OpenGL.
Screenshots and screen recordings¶
manimvox.scenes.screen ¶
Overlays for screenshots and screen recordings: a spotlight on a region, a callout, a cursor that points and
clicks, and window chrome. Regions are (x, y, w, h) in the source's pixels, as Clip.at takes them, and every
overlay is placed for the clip's current position and zoom: make it after the clip has moved or zoomed.
box_on ¶
box_on(clip: Clip, region, color=ACCENT, sw: float = 4, r: float = 0.08, pad: float = 0.06) -> RoundedRectangle
An outline around region, a little outside it so it doesn't cover the region's own edge.
spotlight ¶
Dims the clip except region, and outlines the region. Returns a VGroup with .mask and .ring:
FadeIn it to focus, FadeOut it before the next spotlight or zoom.
callout ¶
callout(clip: Clip, region, text: str, side: str = 'right', color=ACCENT, size: int = 18, gap: float = 1.0) -> VGroup
A label gap scene units off side ("right", "left", "up", "down") of region, with an arrow to it,
kept on the stage. Returns a VGroup with .label, .arrow and .ring (the region's outline).
cursor ¶
A mouse pointer whose tip is the point it points at (.tip). Place it with point.
point ¶
The animation that moves pointer (from cursor) so its tip lands on target, a scene point or a
mobject's centre; for a spot on a clip, pass clip.at(x, y).
click ¶
The pointer presses where its tip is now, with a short flash around the spot: play it after the point
that moved it there, not alongside.
window ¶
Browser or app chrome around clip: a title bar above it with three dots and the url (or title),
and a border. Make it after placing the clip; group them to move both (Group(window(clip), clip)).
Returns a VGroup with .bar, .border and .label (None without a url or title).
Tools for agents¶
Typed functions that mirror the manimvox command, for agents with no shell.
Every tool takes the project folder (or any folder inside it) as project, where the command uses the current
folder. It returns a dataclass of plain values, so dataclasses.asdict gives JSON. A failed check is a result
with ok=False and its findings, as the command exits 1. A tool that cannot do what was asked raises ToolError,
whose message says what to do first. Nothing here prompts: a download goes ahead only with allow_download=True.
ToolError ¶
Bases: Exception
A tool could not do what was asked. The message says why, and what to run first.
add_block ¶
Copy blocks into the project's blocks/ folder; an episode then imports them with each block's import_line.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
project
|
str
|
The project folder, or any folder inside it. |
required |
names
|
list[str]
|
Block names from list_blocks, e.g. ["bar-ranking"]. |
required |
force
|
bool
|
Replace a copy already in the project, losing any edits made to it. |
False
|
check ¶
Run one check: theme (colour contrast and fonts), layout (the last draft) or audio (every line audible).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
project
|
str
|
The project folder, or any folder inside it. |
required |
what
|
str
|
theme, layout or audio. |
required |
episode
|
str | None
|
The episode, for layout and audio: ep01, 1, or its file stem. |
None
|
contact_sheet ¶
Write contact sheets (3 by 3 grids of frames) of an episode, from out/ if rendered, else the last draft.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
project
|
str
|
The project folder, or any folder inside it. |
required |
episode
|
str
|
The episode as listed in series.json: ep01, 1, or its file stem. |
required |
every
|
float
|
Seconds between sampled frames; above 0. |
5.0
|
doctor ¶
Check Python, cairo, Pango and its fonts, Manim, the chart extras and every voice engine; a FAIL row names its fix.
draft ¶
Render a fast 480p draft with no voice, write contact sheets and run the layout check.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
project
|
str
|
The project folder, or any folder inside it. |
required |
episode
|
str
|
The episode as listed in series.json: ep01, 1, or its file stem. |
required |
every
|
float
|
Seconds between the frames sampled into the contact sheets; above 0. |
5.0
|
index ¶
Write the playlist page out/index.html from the rendered episodes.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
project
|
str
|
The project folder, or any folder inside it. |
required |
join ¶
join(project: str, episodes: list[str] | None = None, output: str | None = None, cards: bool = True) -> JoinResult
Join rendered episodes into one MP4 behind a title card, with the subtitles shifted to match.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
project
|
str
|
The project folder, or any folder inside it. |
required |
episodes
|
list[str] | None
|
The episodes in order; by default every rendered episode in series.json order. |
None
|
output
|
str | None
|
The joined MP4, relative to the project folder or absolute; by default out/ |
None
|
cards
|
bool
|
False for no title card. Each episode keeps its own end card either way. |
True
|
list_blocks ¶
List the blocks: ready-made charts and diagrams that add_block copies into a project.
new_project ¶
new_project(directory: str, series: str, tagline: str = '', voice: str = 'kokoro:af_heart') -> ProjectResult
Create a project folder with series.json, FACTS.md, SERIES.md, episodes/, out/ and research/.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
directory
|
str
|
The folder to create, best given as an absolute path; it must not already hold a project. |
required |
series
|
str
|
The series name shown on every title card, e.g. "Hash Maps, Explained". |
required |
tagline
|
str
|
A short line shown under the series name. |
''
|
voice
|
str
|
The narration voice as engine:voice. |
'kokoro:af_heart'
|
package ¶
Write share/
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
project
|
str
|
The project folder, or any folder inside it. |
required |
prepare_voice ¶
prepare_voice(voice: str, project: str | None = None, allow_download: bool = False) -> PrepareResult
Get a voice engine, or one of its voices, ready to render: checks the voice and fetches a missing model.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
voice
|
str
|
An engine (kokoro) or engine:voice (kokoro:af_heart). |
required |
project
|
str | None
|
The project folder, where a cloning voice's reference recording is found. |
None
|
allow_download
|
bool
|
True only when the user has agreed to download the model. |
False
|
render ¶
render(project: str, episode: str, voice: str | None = None, allow_download: bool = False) -> RenderResult
Render the final 1080p episode with the voice, apply series.json post, and run the audio check; takes minutes.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
project
|
str
|
The project folder, or any folder inside it. |
required |
episode
|
str
|
The episode as listed in series.json: ep01, 1, or its file stem. |
required |
voice
|
str | None
|
engine:voice for this render; by default series.json's voice, then MANIMVOX_VOICE, then kokoro:af_heart. |
None
|
allow_download
|
bool
|
True only when the user has agreed to download the voice model if it is missing. |
False
|
voices ¶
List the voice engines (ready or not, licence, whether narration text leaves the machine), or one engine's voices.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
engine
|
str | None
|
An engine name such as kokoro, to list its voices; leave it out to list the engines. |
None
|