My Manim Post-Production Workflow and Tips

5 minute read

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:

  1. 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 argument skip_animations=SKIP_DEFAULT to each section I create.
    • When I want to focus on animating a single section, I change the value of SKIP_DEFAULT to True and the value of the skip_animations parameter of that secition to not 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 of Manim Community v0.17.3, the previously mentioned SKIP_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 the self.camera.frame.shift() method at the very beginning of the Scene.
    • Finally, I make sure that each section ends with a self.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 premature section transitions (such as FadeOut), as this can be done much more efficiently and with greater flexibility with video editing software.
  2. 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.
  3. 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.
  4. 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 each section as a seperate low-quality animation and create a .json file with metadata, and import all of the animated sections 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.
  5. 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.
  6. 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.
  7. 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!