My Manim Post-Production Workflow and Tips
Published:
I have recently finished publishing a series of animated Linear Algebra videos on inner product spaces, which were created with the animation tool Manim and in collaboration with Animathica throughout three whole years. For each video, I wrote a script, directed a team of undergraduate students from UNAM’s Faculty of Sciences as they began to animate each scene, polished both the script and animations into their final form, recorded the script narration and background music, and edited everything together. I now want to write down some of what I’ve learned.
I firstly want to focus on my post-production workflow when using Manim, listing useful tips for each step:
- Polish all
Scene
animations while narrating the script out loud.- For each animation segment that I want to sync up to a narration audio queue, I create a
section
with the exact words that I want to sync it to as its name. - Near the beginning of each animation script, I define a
SKIP_DEFAULT = False
variable and add the argumentskip_animations=SKIP_DEFAULT
to eachsection
I create. - When I want to focus on animating a single section, I change the value of
SKIP_DEFAULT
toTrue
and the value of theskip_animations
parameter of that secition tonot SKIP_DEFAULT
; in this way, all other animations are bypassed when rendering. The same can be done for multiple consecutive sections. - If I am concerned with modifying a specific frame of animation, I comment out every subsequent animation in the same scene (this can be done easily in Vim by using the VISUAL BLOCK mode) and render the last frame with the
-s
flag (Note: as ofManim Community v0.17.3
, the previously mentionedSKIP_DEFAULT
trick will unfortunately not work with the-s
flag, which is why commenting out code is necessary here). - If I ever need to align the camera frame throughout a whole Scene, I change the Scene’s class to
MovingCameraScene
and move the camera with theself.camera.frame.shift()
method at the very beginning of the Scene. - Finally, I make sure that each
section
ends with aself.wait()
, which will be useful later on. - Past this point, the idea is to only modify the duration of some animations to perfectly sync them with the whole audio bits, but not modify
wait
times nor add prematuresection
transitions (such asFadeOut
), as this can be done much more efficiently and with greater flexibility with video editing software.
- For each animation segment that I want to sync up to a narration audio queue, I create a
- Record the narrations from the script while visualizing the corresponding animations fresh off my head.
- While recording, I tap my microphone once each time I try a new take.
- Afterwards, I import the recording into an audio editing software and edit it backwards, from finish to start, easily detecting and removing all erroneous takes, and further cleaning up the audio by reducing noise as much as possible.
- Import the edited narration audio into the video editing software’s project bin (I use Kdenlive) and drag it into the highest priority audio channel as is.
- Generate and import low-quality animation placeholders, align them to the narration audio and edit the video’s pacing.
- I render all scenes using the
-ql --save_sections
flags, which whill render eachsection
as a seperate low-quality animation and create a.json
file with metadata, and import all of the animatedsections
into the project bin. - For each animated
section
, I drag it to the highest priority video channel and use the sections’ names included in the corresponding scene’s.json
file to align it to the corresponding part of the narration audio. - To perfect the video’s pacing, I use Kdenlive’s “Spacer Tool”.
- Note: If any additional modifications should be done to the animations, they should be done before the next step. For animated sections that are already aligned to the audio, modifying their code and rerendering them with the
-ql --save_sections
flag should update them inside the video editing software while keeping their starting point in place. This is useful for perfectly syncing animated sections to whole audio bits.
- I render all scenes using the
- Once animations are in their final form, render them in high quality and replace the low-quality placeholders.
- For 4K quality, I use the
-qk --save_sections
flags. - I create a backup video project file and open it via a text editor, quickly replacing every instance of “480p15” (folders that
manim
creates for low-quality renders) with “2160p60” (folders for 4K renders). - I open the modified backup video project and change the project settings to 4K video. I then save, close and reopen the project.
- For 4K quality, I use the
- Fill up the blank space and add background music!
- For each animated
section
whose ending needs to be lengthened, I navigate to the final frame of animation and add it to the project bin (in Kdenlive this can easily be done with the extract frame to project option). - I then drag each final frame to the lower priority video channel such that it starts before the corresponding
section
’s animation ends, and lengthen this final frame or add effects (such as Fadeouts) as needed. - I add background music to the lower priority audio channels and mix the audio levels.
- For extra sound clarity, I cut out all of the remaning silent audio bits from the narration audio.
- For each animated
- Render the whole project.
- In Kdenlive, I generate a rendering script with my desired characteristics.
- I then restart my computer and, in a fresh session, open a virtual terminal and execute the script with
melt /path/to/generated/script.mlt
. The terminal then displays the number of the frame being processed until it finishes processing the whole video.
That’s all for now. Next time, I might talk about the pre-production process; that is, how I go about writing the narration scripts for these videos!