What is Manim?

Manim is a python library for creating 2D animations, particularly mathematical and technological visualizations. It was created by the YouTube creator 3Blue1Brown for his videos which visualize mathematical concepts, and is now maintained by a wider community. It can be seen in actions in the video below:

Manim is very handy and has lots of useful features for mathematical and computing based visualizations, such as TeX and LaTeX support and 3D graphs.

Installation

Manim can be installed through any standard python package manager, for example using pip:

python3 -m pip install manim

And can then be run directly from the command line, for example to render a high quality video of a scene called ‘HelloManim’ from the file ‘mycode.py’:

python3 -m manim -pqh mycode.py HelloManim

Scenes

Manim works by inheriting from a core Scene class which takes instructions to play and add elements to the visualisation. A simple scene to construct the manim logo is below:

from manim import *

class ManimCELogo(Scene):
    def construct(self):
        self.camera.background_color = "#ece6e2"
        logo_green = "#87c2a5"
        logo_blue = "#525893"
        logo_red = "#e07a5f"
        logo_black = "#343434"
        ds_m = MathTex(r"\mathbb{M}", fill_color=logo_black).scale(7)
        ds_m.shift(2.25 * LEFT + 1.5 * UP)
        circle = Circle(color=logo_green, fill_opacity=1).shift(LEFT)
        square = Square(color=logo_blue, fill_opacity=1).shift(UP)
        triangle = Triangle(color=logo_red, fill_opacity=1).shift(RIGHT)
        logo = VGroup(triangle, square, circle, ds_m)  # order matters
        logo.move_to(ORIGIN)
        self.add(logo)

And it produces:

My Goal With Manim

My goal with Manim was to learn to produce good visuals for presentations and videos I would have to make. To this end I decided that my goal should be to make a simple animation that displays the file format of the .DIR files used in the Harry Potter PS1 games that I reverse-engineered in a prior week.

The resultant animation is below:

And its code is as follows:

from manim import *

class VisualizeBinary(Scene):
    def draw_hex_dump(self):
        # The hex string is split into an array of hex pairs
        hex_values = "24 00 00 00 44 00 60 00 73 00 60 00 20 00 57 00 68 00 6B 00 6B 00 20 00 42 00 64 00 20 00 4C 00 6E 00 72 00 73 00 00 00 1A 00 00 00 41 00 71 00 64 00 60 00 20 00 62 00 6B 00 64 00 60 00 71 00 64 00 63 00 00 00 1C 00".split(" ")

        # Group hex values into rows of 8 and format them as a string
        formatted_hex = ""
        for i in range(len(hex_values)):
            formatted_hex += " ".join(hex_values[i * 8 : (i * 8) + 8])
            formatted_hex += "\n"

        # Display the formatted hex dump
        hex_text = Text(formatted_hex)
        self.play(Write(hex_text))

        return hex_text

    def highlight_file_count(self, hex_text):
        # Create a rectangle to highlight the file count in the hex dump
        file_count_box = Rectangle(width=1.65, height=0.6, stroke_width=0)
        file_count_box.align_to(hex_text, UL)
        file_count_box.shift(UP * 0.05 + LEFT * 0.05)
        file_count_box.set_fill(RED, opacity=0.5)
        self.play(Create(file_count_box))

        # Add a label for the file count
        file_count_label = Text("File count", font_size=24)
        file_count_label.next_to(
            file_count_box, UP, buff=0.1
        )  # Adjust the buffer for spacing
        self.play(Write(file_count_label))

    def highlight_file_name(self, hex_text):
        # Create rectangles to highlight the file name in two separate locations
        file_name_box1 = Rectangle(width=(1.6 * 3) + 0.25, height=0.6, stroke_width=0)
        file_name_box1.align_to(hex_text, UL)
        file_name_box1.shift(UP * 0.05 + LEFT * 0.05 + RIGHT * 1.7)
        file_name_box1.set_fill(GREEN, opacity=0.5)

        file_name_box2 = Rectangle(width=(1.6 * 3) + 0.25, height=0.6, stroke_width=0)
        file_name_box2.align_to(hex_text, UL)
        file_name_box2.shift(UP * 0.05 + LEFT * 0.05 + DOWN * 0.65)
        file_name_box2.set_fill(GREEN, opacity=0.5)

        # Animate the creation of both file name boxes
        self.play(Create(file_name_box1), Create(file_name_box2))

        # Add a label for the file name
        file_name_label = Text("File name", font_size=24)
        file_name_label.next_to(
            file_name_box1, UP + UP, buff=0.1
        )  # Adjust the buffer for spacing
        self.play(Write(file_name_label))

    def highlight_file_size(self, hex_text):
        # Create a rectangle to highlight the file size in the hex dump
        file_size_box = Rectangle(width=1.65, height=0.6, stroke_width=0)
        file_size_box.align_to(hex_text, UL)
        file_size_box.shift(UP * 0.05 + LEFT * 0.05 + DOWN * 0.65 + RIGHT * 5.1)
        file_size_box.set_fill(YELLOW, opacity=0.5)
        self.play(Create(file_size_box))

        # Add a label for the file size
        file_size_label = Text("File size", font_size=24)
        file_size_label.next_to(
            file_size_box, RIGHT, buff=0.1
        )  # Adjust the buffer for spacing
        self.play(Write(file_size_label))

    def highlight_file_offset(self, hex_text):
        # Create a rectangle to highlight the file offset in the hex dump
        file_offset_box = Rectangle(width=1.65, height=0.6, stroke_width=0)
        file_offset_box.align_to(hex_text, UL)
        file_offset_box.shift(UP * 0.05 + LEFT * 0.05 + (DOWN * 0.65 * 2))
        file_offset_box.set_fill(BLUE, opacity=0.5)
        self.play(Create(file_offset_box))

        # Add a label for the file offset
        file_offset_label = Text("File offset", font_size=24)
        file_offset_label.next_to(
            file_offset_box, LEFT, buff=0.1
        )  # Adjust the buffer for spacing
        self.play(Write(file_offset_label))

    def construct(self):
        # Visualize the hex dump and then highlight different sections
        hex_text = self.draw_hex_dump()
        self.highlight_file_count(hex_text)
        self.highlight_file_name(hex_text)
        self.highlight_file_size(hex_text)
        self.highlight_file_offset(hex_text)

        # Pause the animation to keep the final frame visible
        self.wait(5)

Future Use Cases For Manim

In the future I hope to use Manim for some of my University and work presentations, and I also think it has potential in automatically generating visualisations of data formats, for example it could be used to visualise binary information automatically using Kaitai Struct a format for specifying binary file layouts, which would allow intuitive visualization and demonstration of binary file layouts based on their contents using the parsers.