Imported from App-Midnight/hackcmu (
skills/visari/SKILL.md). Install upstream withnpx skills add App-Midnight/hackcmu --skill visari. Copyright stays with the author.
Author with visari
Make the intended inference visible with the smallest useful scene. Use ordinary Manim objects and animations; visari supplies identity, layout, and timing contracts. Compute mathematics and algorithm state in ordinary Python. visari does not infer the explanation or verify arbitrary mathematical claims.
Use the implemented contracts
visari's scene checker and measured layout exist to reduce authoring tokens and generation latency. Express the scene through the supported API and let visari handle the mechanics covered by its contracts. Do not repeatedly reason through, reimplement, or defensively verify those mechanics in generated code.
Choose the explanation, content, relationships, and pacing. Declare subjects, layout constraints, future states, and timing once; use visari's measured placement, identity handling, and checks to enforce them. Prefer an existing template or layout operation over hand-computed coordinates and custom bookkeeping.
Trust is specific to the declared contract and reported coverage. A successful check is enough to move past the properties it checks; do not add a second verifier or speculative repair loop. Optimize total tokens and latency per correct video: reuse covered behavior and spend visual feedback on custom decisions.
Choose the feedback path
Choose per shot/action, including its interaction with surrounding evidence. Keep visari's useful identity, layout, state, and timing contracts in both paths.
- Covered behavior — no model previews. Use existing supported operations with measured composition, reserved future content, and relevant checks. A table-cell update within its reservation or a measured node-label replacement is a candidate. Once checks pass, render the deliverable without sending frames back to the model or adding a visual-review turn, when project and task instructions permit skipping review.
- Custom behavior — render and inspect. Use native Manim where visari does not own the needed behavior: novel copy routes, progressive expression choreography, interacting moving labels, dense diagrams, custom transforms/updaters, or 3D and camera motion. Review the custom portion and transitions into/out of it; expand to the whole sequence when dependencies or pacing span shots.
A template's presence, a scene being "simple," or a checker pass alone does not
make the composition covered. A table with custom FRONT/BACK pointers needs
review of those pointers; a positioned expression does not establish a safe copy
path. Treat unresolved relevant coverage gaps as custom, not as passes. Do not
use --endpoints-only to qualify motion for the no-preview path. Explicit user
requests and project requirements for visual review take precedence.
In both paths, check content, execute the scene checks, and verify the encoded output. Skipping previews is a cost policy, not a claim of visual or human acceptance. Do not flatten needed 3D geometry, remove evidence, or build a new abstraction just to qualify for the covered path. Example-specific choreography can remain custom; extract it only when repeated use justifies reusable behavior.
Locate the current API
Work from the active visari project root: the directory with visari/__init__.py,
pyproject.toml, and AUTHORING.md. In this checkout it is two levels above this
skill. Commands and code paths below are relative to that root; bundled links
are relative to this skill. If installed elsewhere, locate the user's checkout
instead of assuming the skill directory contains visari or hardcoding a machine path.
Use the project environment (.venv/bin/python here; Python 3.12). For missing
dependencies, consult README.md and pyproject.toml; do not replace the local
Manim dependency with an arbitrary pip version. Importing visari registers its fonts
and sets Text/MarkupText's default font to Latin Modern Roman. MathTex and
Expression also require LaTeX and dvisvgm.
Use this guide and the relevant reference for routine authoring. Look up unfamiliar
signatures in current docs or capability examples; inspect visari/__init__.py and
the relevant implementation only when those leave a concrete question or a
diagnostic unresolved. Do not rediscover internals to justify using a documented
contract. Prefer current APIs over frozen examples/evaluation/ snapshots or
implementation plans.
Read only the material needed for the scene:
| Need | Reference |
|---|---|
| Pick a primitive or express a checked change | Building blocks |
| Start a small runnable scene | Starter; adapt its content and timing |
| Shot lifecycle, placement, expressions, state | Relevant section of AUTHORING.md |
| Content-sized panel, node/graph, plot, number line, table options | TEMPLATES.md |
| Compose panels with moving diagrams | Measured composition; examples/capabilities/content_sized_panels.py |
| Fast execution feedback | CHECKING.md |
| Spoken audio or phrase synchronization | NARRATION.md before preparing or rendering audio |
| Native 3D/camera motion | AUTHORING.md, “Preserve native space and camera motion”; examples/capabilities/spatial_camera.py |
Compose the explanation
For a lesson, sketch each shot's claim, visible evidence, primary focus, what
carries or retires, and action/reading time before choosing coordinates. Keep
this proportional to the task; a small edit does not need a separate storyboard.
Use Shot/play_shots for a lesson and Beat/play_beats for isolated actions.
stageis the full settled composition, with one sharedSubjectper logical object.focuscontains one Subject, or exactly two forcomposition="compare". A focus may be a child of a staged group.- Compositions are
hero,transform,detail,compare. Action kinds areestablish,transform,recompose,inspect. These declare intent; the operation must perform the actual reveals, changes, and removals. establishmust introduce at least one focus absent from the previous stage.transformandrecomposemust carry at least one focus from the previous stage into the new one.inspectkeeps the same stage IDs, has no operation, and uses zero action time. Supplystageandfocusas tuples of Subjects.- Keep sources, axes, and comparison evidence while they support the claim. Omitted stage layers must leave or become part of a newly staged group. Stage only the parts actually present during progressive construction.
- Use
complete_stage=True(the API default isFalse) for new shot sequences to catch undeclared visible vector objects at settlement and hold-end. Declare pre-added content withinitial_stage. Name independently essential children when their presence matters. - Declare titles with
title=in every shot where they remain staged. Playback enforces a six-second default cumulative title budget (max_title_duration), counting actions/holds while staged and the retirement action; it does not remove titles automatically. Keep attachedAnnotations declared until their text retires. Recompose around the next inference instead of accumulating panels.
Preserve identity and space
Keep Subject handles and resolve .mobject inside each operation, after
earlier replacements. Use subject.replace(scene, fresh_object, placement=space, run_time=duration) for representation changes. Its default sequential fades avoid
superimposed text; mode="transform" is for meaningful shape correspondence.
Raw-object updaters and prebuilt animations do not retarget automatically.
The current object must already be in the scene; the target must be fresh, with
no family members already present. Fade mode needs at least two frames. Without
placement, replacement retains the current center and checks no reserved slot.
Compose from measured content outward:
- Build readable text and diagrams, including attached labels, at authored sizes.
Declare future text and motion extrema before allocating space.
statesreserves replacement widths/heights at one anchor;motion_statesreserves the union of positioned snapshots, including displacement. Do not mix them on one Placement. - For content that needs a panel, use
Panel(id, {name: mobject}, states={name: [future]}). It arranges parts and sizes the border from their reserved footprints. Keeppanel.part(name)and passpanel.placement(name)to replacement. Do not guess a border's dimensions and fit text into it. Content that needs no border can use measured rows/columns. - Arrange complete component Subjects with
area.arrange(..., direction="right" or "down", gap=...). For a moving diagram, includeplacement.reservationand the visible subject in a nativeVGroupbefore arranging its root Subject. That invisible child makes native bounds include the full path. Panel already includes its text reservations. Neither helper infers missing diagram labels or clears collisions between children of an arbitrary group. - Translate complete groups so their reservations travel with them. After a
move, call
placement.position(snapshot)on each detached motion target before using it. Usewith space.watch(scene):through motion and replacement to check the live slot. Resolve Subjects again after replacements.
The initial Area constrains reservation creation; after translation, a
Placement checks against its live reservation, not that original Area or the
frame. Use the CLI's supported frame checks for the translated composition.
Area.arrange returns Placements in input order; keep them for replacements.
Unlike Panel, it does not attach reservations to a group automatically.
Motion placements accept only the original subject object and declared snapshot
instances in .position(...); anchored states placements accept fresh objects
that fit. .position(target) moves the target without adding or rebinding it.
Placement(...) alone neither moves nor adds objects: call .position() to
place its subject. Panel(...) and area.arrange(...) position content but do
not add it to the scene. Add/reveal the complete component through native Manim.
Panel state keys are local part names; Area.arrange(states=...) keys are Subject
IDs. See Measured composition
for the replacement pattern and a runnable motion example.
Use Area.inside(...) for fixed authored boundaries; it captures coordinates
when called and must be rebuilt after moving the border. Annotation positions
a subject-bound label within its declared area and subject states; it is not a
moving panel slot. Reservations support translation, not scaling or rotation.
Call Annotation.position() explicitly to place/update the label; attachment
does not install a following updater. Author and clean up any native updater.
Overflow calls for recomposition, progressive reveal, or another shot; do not
shrink text, omit required evidence, or enlarge a region beyond the frame.
These are axis-aligned contracts, not an automatic collision solver or camera
projection check. Preserve actual 3D geometry and native camera/label tools when
depth or viewpoint is the evidence.
Use visari.palette: INK for text, MUTED for axes/connectors, BLUE and APRICOT
for distinct objects, SUCCESS/ERROR for outcomes. Keep object colors consistent
and use labels as well as color. Honor an explicitly requested visual style.
Budget playback and speech
An operation (scene, duration) plays immediately and spends its allocated time.
Build native animations there and pass run_time=duration; divide that allocation
explicitly if it contains multiple sequential plays. Put reading time in hold.
Inspection uses operation=None, action_duration=0; stop distracting updaters
before the hold. budget preflights the sum of declared actions and holds in
that playback call; it does not fill unused time or cover outside plays.
Silent playback checks each action/hold with a one-frame tolerance, so use
frame-compatible durations and verify the encoded total separately.
precondition takes no arguments, returns None on success, and raises
ValueError with a correction on failure; returning a boolean is an error.
For narration, prepare audio outside construct(), then load SpeechClip records
and attach Narration to inclusive shot ranges. Follow NARRATION.md for script
format, provider settings, caching, cue offsets, and the pinned engine's required
MP4 finalization. Derive timing from prepared WAVs; never guess phrase offsets
from text length. Speech can extend holds but cannot shorten authored motion or
evade the budget. Narrated playback rounds positive actions/holds upward to
whole frames and passes resolved action seconds to operations. Inclusive ranges
cannot overlap; measured phrase cues target action starts and are bound to the
prepared WAV hash. Do not silently drop requested audio on failure.
Check, render, selectively inspect
Run from the project root, substituting the authored file and class. The bundled
starter is skills/visari/assets/starter.py, class VisariStarter.
.venv/bin/visari check scene.py SceneName --json
Use the checker after scene edits for the edit → check → correction loop before
rendering. Correct reported failures without duplicating the checker's work in
scene-specific validation. Then follow the selected feedback path below.
The checker preserves the real animation lifecycle and scene clock, reuses
text/TeX caches, and skips video output.
Read JSON status and source-linked diagnostics: exit 0 means checked execution
passed, 1 means a scene/check exception, and 2 means timeout or worker failure.
Fix scene errors at their source; diagnose worker failures before revising scene
logic. The default deadline is 30 seconds; use --timeout when needed.
If the CLI is missing after an update, follow CHECKING.md to sync the package.
The default checker uses the native Cairo renderer and a Scene constructor that
accepts skip_animations; custom renderers and interactive scenes are unsupported.
It steps native animations and updaters at the configured frame
rate without drawing pixels. It catches frame clipping, overlaps between separate
text objects inside groups, and straight strokes crossing text glyph bounds.
Read the reported scene times and object paths as well as source lines. Glyph
bounds are conservative; curved strokes, general shape collisions, camera
projection, raster visibility, and pixel readability still need rendered inspection.
Read coverage_limits, geometry.unsupported, geometry.skipped_animations,
and geometry.diagnostics_truncated; a pass can still have unsupported coverage. Geometry
uses vector opacity above 1%, records the first finding per object pair/code,
and caps diagnostics at 100. Intermediate fade clipping and source/copy contact
during native TransformFromCopy have explicit exemptions; settled states are
checked normally. --endpoints-only skips these automatic checks and intermediate
updates.
The checker executes Python, not just syntax, and is not a sandbox. Semantic and
containment contracts still need to be authored.
Shot playback checks stage presence and retirement without a manifest; direct
Beat playback alone does not check required-subject visibility or geometry.
The CLI's automatic geometry checks are separate from both.
For diagnostic evidence, pass a fresh RunManifest per sequence and write it in
finally, as the starter shows. Shot playback registers its required evidence;
direct Beats need explicit Evidence records. The manifest samples at settlement
and hold-end; intermediate samples must be authored explicitly and are not filled
by the CLI or Placement guards. CLI status="pass" can coexist with manifest
unreviewed checks; neither represents visual acceptance.
For deliberate contact, register the related Subjects as Evidence and declare
PermittedOverlap(first_id, second_id, reason) in the manifest's overlaps tuple.
Also set scene.manifest = run so the CLI honors those relationships; passing
manifest=run to playback alone does not attach it. Permit only the intended
relationship after inspecting it, not unrelated collisions. See AUTHORING.md
for manifest sampling, roles, and review records.
Covered path: render once at the requested delivery quality; no draft render
or image feedback is required. Verify encoding and duration, and finalize/check
requested narration using NARRATION.md. Leave visual acceptance unreviewed.
Custom path: after checks pass, render a draft, for example:
.venv/bin/python -m manim -ql scene.py SceneName
Inspect a chronological overview plus moving and settled states of custom actions. Use denser timestamped frames or short clips around copies, crossings, and camera moves; a final frame or sparse contact sheet cannot establish motion quality. Check visible mathematical content, source preservation, correspondence, label readability, and reading time. Inspect critical text at delivery resolution when preview resolution is insufficient. Feed back only the relevant images/clips, shot/subject IDs, timestamps, and concrete defects, not the full docs or raw logs.
Default to at most two visual repair rounds unless the user sets another budget. Repair locally without dropping required evidence; rerun checks and inspect the changed action plus affected neighboring transitions. Reuse an unchanged draft if it already meets delivery settings; otherwise render the final version and check affected visuals at final quality. Verify encoding, duration, and requested narration. If review tools are unavailable or the budget is exhausted, report the unreviewed scope or remaining defects rather than claiming acceptance or retrying indefinitely. Model review is not human review or proof of comprehension; keep those records distinct. Audio synchronization needs its own checks/listening.
Report the output path, feedback path and reason, checks actually run, repair rounds if any, and unreviewed visual/audio aspects. Animation failures can leave partial frames and changed objects; there is no rollback.
Keep this skill lean
When visari changes, update the affected rule or example and verify it against the current implementation. Add a reference only for a recurring authoring decision that existing docs do not explain cheaply. Keep full API details in project docs; do not copy them here or turn individual lesson fixes into universal rules.