AnyToManim — CLI toolchain for narrated 3b1b-style videos
The product is a CLI, not an API harness. You (the host agent) do the creative stages — write the brief, the beat sheet, the storyboard, the scene code — guided by the spec files shipped in the package. The CLI does everything deterministic: TTS, duration measurement, rendering, concatenation.
Install & check
pipx install anytomanim # or: pip install -e . in a venv
anytomanim doctor # reports missing deps + fix hints
Workflow — export the spec, follow it
anytomanim export-guides ./guides # WORKFLOW/DIGEST/SCRIPT/SCENE_GUIDE + schema
Then follow guides/WORKFLOW.md:
- Write
brief.mdperDIGEST.md - Write
beats.jsonperSCRIPT.md(+beats-schema.json). ReadSCRIPT.mdfirst: narration has to chain, not list. - Read
ORCHESTRATION.md, then writestoryboard.jsonperSTORYBOARD.md. Open the runnable recipes for the cards you pick. Runanytomanim storyboard storyboard.json --beats beats.jsonand fix it. anytomanim tts --beats beats.json— synthesizes every beat, writesaudio_file+ realaudio_secondsback (re-run after edits: only changed beats)- Write
scenes/*.pyperSCENE_GUIDE.md(NarratedScene+MiMoService(beats_file=...),with self.voiceover(beat="b01") as tr). Follow the storyboard; start from the matchingrecipes/. anytomanim render scenes/X.py --quality ql— fix & retry on erroranytomanim preview <mp4>— READ sheet.png and the per-beat report (overlaps, overruns, frozen beats, small content, empty bands, a fixed header, stage changes the storyboard does not mention); fix and re-render until clean- Render
--quality qh,anytomanim assemble a.mp4 b.mp4 -o final.mp4,anytomanim subtitles beats.json --mux final.mp4
Iterate without a key / without cost: ANYTOMANIM_TTS=draft (local voice).
anytomanim run "<topic>" also exists — an embedded-LLM reference driver for
automated batch eval (requires an LLM API key; interactive agents should use
the guide-driven flow instead).
Contracts
beats.jsonis THE interface — public IR (narration + visual + audio_seconds).- Audio-first sync: synthesize → measure → every
run_timederives fromtr.duration, and the render replays exactly those wavs. Never invent durations. - A clean render is not done — look at the preview sheet every time.
- Continuity comes first. Before an object leaves, ask whether it can become the next thing. Fade out and redraw is the last resort.
- Draw the relation when you can, instead of writing it as a label.
- Voices:
--voice baihua|moli|milo; clone your own voice viaassets/voice/<name>.mp3+ voices.json entry. zh + en supported. - See
examples/in the repo for golden end-to-end runs.