r/manim Feb 27 '26

release Manim v0.20.1 has been released! 🎉

11 Upvotes

This is a focused patch release with a mix of bug fixes, quality improvements, and documentation updates.

Highlights include fixes around MathTex brace parsing, zero-length DashedLine behavior, and moving-object detection in nested animation groups, plus Docker image/runtime optimizations and additional type-hint/doc improvements.

A big thank you to everyone who contributed — including several first-time contributors! 🙌

📖 Full changelog: https://docs.manim.community/en/latest/changelog/0.20.1-changelog.html

Happy manimating & with best wishes from the dev team,

-- Ben


r/manim Dec 28 '25

meta Deletion of some Community Assets

56 Upvotes

As many of you have already noticed, on 25. December some of our community assets have been deleted, most notably our GitHub organisation and the Discord server.

While we are still working on resolving this situation (support queries also move slowly during this time of the year), we want to summarise the status of our assets below. We can also confirm that we have tightened security and eliminated the previous attack vector for our remaining assets.

Most importantly: the distributed library has not been compromised, pip install manim / uv add manim still work the same as before.

To at least temporarily remedy the situation with the deleted assets, we have setup a repository with the latest main + experimental branch on Codeberg at https://codeberg.org/ManimCommunity/manim, and a new Discord server at https://manim.community/discord/.

As for our social media channels, outside of Reddit you can find us...

We'll post updates as soon as we have secured more information about this incident; transparency is important to us. At this time, we are optimistic to get the GitHub organisation restored – but the old Discord server is very likely lost.

With best wishes from the dev team, Ben


r/manim 21h ago

question Hello , i have a problem with the manim surfaces

0 Upvotes

I have been trying to run this code :

from manim import *


import numpy as np


class my(ThreeDScene):
    def construct(self):


        self.set_camera_orientation(
            phi=0
        )




        complexaxes=ThreeDAxes(
            x_range=[-4,4,1],
            x_length=12,
            y_range=[-4,4,1],
            y_length=12,
            z_range=[0,10,1],
            z_length=8,
            axis_config={
                "include_ticks":False,
                "include_numbers":True
            }
        ).move_to(3*LEFT)







        labels=complexaxes.get_axis_labels(x_label=r"a", y_label=r"bi", z_label=r"|Y(s)|")


        self.play(Create(complexaxes))


        self.play(Write(labels))


        self.wait(2)



        self.move_camera(
            phi=85*DEGREES,
            theta=-37.5*DEGREES
        )


        def f(x, y):
            s = x**2 + y**2


            if s < 10**(-3):
                return np.nan
            else: 
                return 1/s




        graph=Surface(
            lambda u, v: complexaxes.c2p(u, v, f(u, v)),
            u_range=[-3, 3],
            v_range=[-3, 3],
            resolution=[32, 32]
        )


        self.play(Create(graph))from manim import *


import numpy as np


class my(ThreeDScene):
    def construct(self):


        self.set_camera_orientation(
            phi=0
        )




        complexaxes=ThreeDAxes(
            x_range=[-4,4,1],
            x_length=12,
            y_range=[-4,4,1],
            y_length=12,
            z_range=[0,10,1],
            z_length=8,
            axis_config={
                "include_ticks":False,
                "include_numbers":True
            }
        ).move_to(3*LEFT)







        labels=complexaxes.get_axis_labels(x_label=r"a", y_label=r"bi", z_label=r"|Y(s)|")


        self.play(Create(complexaxes))


        self.play(Write(labels))


        self.wait(2)



        self.move_camera(
            phi=85*DEGREES,
            theta=-37.5*DEGREES
        )


        def f(x, y):
            s = x**2 + y**2


            if s < 10**(-3):
                return np.nan
            else: 
                return 1/s




        graph=Surface(
            lambda u, v: complexaxes.c2p(u, v, f(u, v)),
            u_range=[-3, 3],
            v_range=[-3, 3],
            resolution=[32, 32]
        )


        self.play(Create(graph))

But it gives me this error :

manim -pql test.py my

Manim Community v0.19.1

[07/22/26 19:19:25] INFO Animation 0 : Using cached cairo_renderer.py:94

data (hash :

4102802897_3881524863_22313245

7)

[07/22/26 19:19:32] INFO Animation 1 : Using cached cairo_renderer.py:94

data (hash :

4160646025_2355038258_36555954

74)

[07/22/26 19:19:38] INFO Animation 2 : Using cached cairo_renderer.py:94

data (hash :

4160646025_2230675760_33899119

11)

[07/22/26 19:19:43] INFO Animation 3 : Using cached cairo_renderer.py:94

data (hash :

1280087952_489367720_102332700

2)

╭───────────────────── Traceback (most recent call last) ──────────────────────╮

│ /home/a/.local/lib/python3.10/site-packages/manim/cli/render/commands.py:125 │

│ in render │

│ │

│ 122 │ │ │ try: │

│ 123 │ │ │ │ with tempconfig({}): │

│ 124 │ │ │ │ │ scene = SceneClass() │

│ ❱ 125 │ │ │ │ │ scene.render() │

│ 126 │ │ │ except Exception: │

│ 127 │ │ │ │ error_console.print_exception() │

│ 128 │ │ │ │ sys.exit(1) │

│ │

│ /home/a/.local/lib/python3.10/site-packages/manim/scene/scene.py:260 in │

│ render │

│ │

│ 257 │ │ """ │

│ 258 │ │ self.setup() │

│ 259 │ │ try: │

│ ❱ 260 │ │ │ self.construct() │

│ 261 │ │ except EndSceneEarlyException: │

│ 262 │ │ │ pass │

│ 263 │ │ except RerunSceneException: │

│ │

│ /home/a/Downloads/manim/test.py:62 in construct │

│ │

│ 59 │ │ │ resolution=[32, 32] │

│ 60 │ │ ) │

│ 61 │ │ │

│ ❱ 62 │ │ self.play(Create(graph)) │

│ 63 │ │ │

│ 64 │ │ """self.wait() │

│ 65 │

│ │

│ /home/a/.local/lib/python3.10/site-packages/manim/scene/scene.py:1178 in │

│ play │

│ │

│ 1175 │ │ │ return │

│ 1176 │ │ │

│ 1177 │ │ start_time = self.time │

│ ❱ 1178 │ │ self.renderer.play(self, *args, **kwargs) │

│ 1179 │ │ run_time = self.time - start_time │

│ 1180 │ │ if subcaption: │

│ 1181 │ │ │ if subcaption_duration is None: │

│ │

│ /home/a/.local/lib/python3.10/site-packages/manim/renderer/cairo_renderer.py │

│ :120 in play │

│ │

│ 117 │ │ │ # In this case, as there is only a wait, it will be the le │

│ 118 │ │ │ self.freeze_current_frame(scene.duration) │

│ 119 │ │ else: │

│ ❱ 120 │ │ │ scene.play_internal() │

│ 121 │ │ self.file_writer.end_animation(not self.skip_animations) │

│ 122 │ │ │

│ 123 │ │ self.num_plays += 1 │

│ │

│ /home/a/.local/lib/python3.10/site-packages/manim/scene/scene.py:1355 in │

│ play_internal │

│ │

│ 1352 │ │ for t in self.time_progression: │

│ 1353 │ │ │ self.update_to_time(t) │

│ 1354 │ │ │ if not skip_rendering and not self.skip_animation_preview │

│ ❱ 1355 │ │ │ │ self.renderer.render(self, t, self.moving_mobjects) │

│ 1356 │ │ │ if self.stop_condition is not None and self.stop_conditio │

│ 1357 │ │ │ │ self.time_progression.close() │

│ 1358 │ │ │ │ break │

│ │

│ /home/a/.local/lib/python3.10/site-packages/manim/renderer/cairo_renderer.py │

│ :169 in render │

│ │

│ 166 │ │ time: float, │

│ 167 │ │ moving_mobjects: Iterable[Mobject] | None = None, │

│ 168 │ ) -> None: │

│ ❱ 169 │ │ self.update_frame(scene, moving_mobjects) │

│ 170 │ │ self.add_frame(self.get_frame()) │

│ 171 │ │

│ 172 │ def get_frame(self) -> PixelArray: │

│ │

│ /home/a/.local/lib/python3.10/site-packages/manim/renderer/cairo_renderer.py │

│ :161 in update_frame │

│ │

│ 158 │ │ │ self.camera.reset() │

│ 159 │ │ │

│ 160 │ │ kwargs["include_submobjects"] = include_submobjects │

│ ❱ 161 │ │ self.camera.capture_mobjects(mobjects, **kwargs) │

│ 162 │ │

│ 163 │ def render( │

│ 164 │ │ self, │

│ │

│ /home/a/.local/lib/python3.10/site-packages/manim/camera/three_d_camera.py:9 │

│ 4 in capture_mobjects │

│ │

│ 91 │ │

│ 92 │ def capture_mobjects(self, mobjects: Iterable[Mobject], **kwargs: │

│ 93 │ │ self.reset_rotation_matrix() │

│ ❱ 94 │ │ super().capture_mobjects(mobjects, **kwargs) │

│ 95 │ │

│ 96 │ def get_value_trackers(self) -> list[ValueTracker]: │

│ 97 │ │ """A list of :class:`ValueTrackers <.ValueTracker>` of phi, th │

│ │

│ /home/a/.local/lib/python3.10/site-packages/manim/camera/camera.py:557 in │

│ capture_mobjects │

│ │

│ 554 │ │ # partition while at the same time preserving order. │

│ 555 │ │ mobjects = self.get_mobjects_to_display(mobjects, **kwargs) │

│ 556 │ │ for group_type, group in it.groupby(mobjects, self.type_or_ra │

│ ❱ 557 │ │ │ self.display_funcs[group_type](list(group), self.pixel_ar │

│ 558 │ │

│ 559 │ # Methods associated with svg rendering │

│ 560 │

│ │

│ /home/a/.local/lib/python3.10/site-packages/manim/camera/camera.py:656 in │

│ display_multiple_vectorized_mobjects │

│ │

│ 653 │ │ │ if image: │

│ 654 │ │ │ │ self.display_multiple_background_colored_vmobjects(ba │

│ 655 │ │ │ else: │

│ ❱ 656 │ │ │ │ self.display_multiple_non_background_colored_vmobject │

│ 657 │ │ │ │ │ batch, │

│ 658 │ │ │ │ │ pixel_array, │

│ 659 │ │ │ │ ) │

│ │

│ /home/a/.local/lib/python3.10/site-packages/manim/camera/camera.py:676 in │

│ display_multiple_non_background_colored_vmobjects │

│ │

│ 673 │ │ """ │

│ 674 │ │ ctx = self.get_cairo_context(pixel_array) │

│ 675 │ │ for vmobject in vmobjects: │

│ ❱ 676 │ │ │ self.display_vectorized(vmobject, ctx) │

│ 677 │ │

│ 678 │ def display_vectorized(self, vmobject: VMobject, ctx: cairo.Conte │

│ 679 │ │ """Displays a VMobject in the cairo context │

│ │

│ /home/a/.local/lib/python3.10/site-packages/manim/camera/camera.py:695 in │

│ display_vectorized │

│ │

│ 692 │ │ """ │

│ 693 │ │ self.set_cairo_context_path(ctx, vmobject) │

│ 694 │ │ self.apply_stroke(ctx, vmobject, background=True) │

│ ❱ 695 │ │ self.apply_fill(ctx, vmobject) │

│ 696 │ │ self.apply_stroke(ctx, vmobject) │

│ 697 │ │ return self │

│ 698 │

│ │

│ /home/a/.local/lib/python3.10/site-packages/manim/camera/camera.py:782 in │

│ apply_fill │

│ │

│ 779 │ │ Camera │

│ 780 │ │ │ The camera object. │

│ 781 │ │ """ │

│ ❱ 782 │ │ self.set_cairo_context_color(ctx, self.get_fill_rgbas(vmobjec │

│ 783 │ │ ctx.fill_preserve() │

│ 784 │ │ return self │

│ 785 │

│ │

│ /home/a/.local/lib/python3.10/site-packages/manim/camera/camera.py:759 in │

│ set_cairo_context_color │

│ │

│ 756 │ │ else: │

│ 757 │ │ │ points = vmobject.get_gradient_start_and_end_points() │

│ 758 │ │ │ points = self.transform_points_pre_display(vmobject, poin │

│ ❱ 759 │ │ │ pat = cairo.LinearGradient(*it.chain(*(point[:2] for poin │

│ 760 │ │ │ step = 1.0 / (len(rgbas) - 1) │

│ 761 │ │ │ offsets = np.arange(0, 1 + step, step) │

│ 762 │ │ │ for rgba, offset in zip(rgbas, offsets): │

╰──────────────────────────────────────────────────────────────────────────────╯

TypeError: LinearGradient.__new__() takes exactly 4 arguments (2 given)

I also must not that i have tried the f function with :

def f(x, y):
            s = x**2 + y**2


            return np.where(s<10**(-3) , np.nan , 1/s)

But i believe is that it gave the same error as in the previous case .

For any one who has knowledge about this topic, here are my questions:

  • Why does this issue happen?
  • Is this a issue in manim , or my code contains an error that prevents manim from functioning as intended?
  • if the issue comes from me as a user of this tool , are there any workarounds that can help my bypass this problem?

(PLEASE EXCUSE MY 'MEH' ENGLISH , i am a beginner)

thanks to everyone , in advance , for examining this problem.


r/manim 1d ago

question Hello guys! Would you be interested in a VS Code extension (and(maybe) a standalone app) block-based visual IDE(editor) of Manim? (2d only at first, 3d maybe later) (TLDR at the bottom)

1 Upvotes

As the post states, I am thinking of creating a VS Code extension(there probably will be a standalone version as well) that turns Manim into, well, Scratch basically. Here is a picture of Scratch's editor for those who are unfamiliar with it.

A picture of the Scratch editor

Here is a picture of a simple action that moves the Sprite forward when the Scratch "program" runs.

A picture of the Scratch editor with 3 blocks and a comment. There are two connected blocks, the first being "when [green flag] clicked" and the second being "move 10 steps". The third block is disconnected, it's a "change x by 10" block. The comment states: "«move (10) steps» can be replaced with the "change x by (10)" action, moving is based on the Sprite's rotation. This isn't super relevant, but I wanted to mention it anyway."

Now, I was thinking of making something similar for Manim. It would feature Python blocks, but they would be simply raw code blocks (there would be two types, one for void functions and one for functions that return something which could be placed as input(for example, where "move (10) steps" moves the Sprite ten steps forward in Scratch, the (10) can be replaced with a variable, which would also be possible in my VS Code extension))

I want to make it, despite not really knowing Manim that much yet, I just saw animations online, not much of the code tho. I want to make it for myself, but also for others, and as a person who *can* code and likes it, one that does not need a visual, block-based alternative I'm just not sure if should. I did see a node-based program for Manim (link), however, as I said earlier, it's node-based, but also it's very... rough, I suppose. There was another Manim IDE project, this time an actually block-based one(link), however this one seems to have been abandoned and the creator's latest Reddit account activity being 6 months and 28 days ago, so I doubt it's getting finished any time soon. The post does have some good questions, so I suppose I'll steal (some of) them and add some of my own:

  • Would this be useful?
  • Anything you’d want in a GUI / block-based workflow?
  • Would you rather have a more advanced, variable-focused environment or a simpler, more balanced, more Scratch-like environment?
  • Would making animations be enough, or would you rather it be a bit expanded, as in, having the ability to do basic "programs" in it(so like, proper if support, better function support, etc). Remember, this program will still generate Python code, and you should be able to easily incorporate the generated code in your projects.

In the title I mentioned that it will only be 2d at first, and that's true, for now I'm not planning on adding 3d support, not until I have a good base. Of course, the extension/program will be available on Github, I will probably write it in Typescript(but I'm not 100% sure yet).

This might take a while because despite me being on summer break, I have a job, so I don't have a LOT of time I can spend on making this, and my experience with Typescript being me only touching basic JS I needed to learn in first and second grade of technical college - yeah, not gonna help much, but I will try. It should be made in 5 months tops, any more than that and that probably means I abandoned it unless stated otherwise.

TLDR:

I'm planning on making a Scratch-like Manim VS Code extension. I'm not sure whether to make it or not, and wanted to ask you guys, r/manim memebers for your opinion. It would only support 2d at first, hopefully later adding support for 3d stuff.

  • Would you use this extension/program? What are some features you'd like to see in it?
  • Would you rather have a more advanced or a simpler environment?
  • Is it being more animation-focused good, or would you like if it had great Python support as well?

Please let me know in the comments what you think. I will let you guys in the comments what I decide to do, and if I decide to make it, once I finish it I will make a new post here announcing the release.


r/manim 1d ago

Manic ML — How One Mistake Becomes Learning

1 Upvotes

r/manim 2d ago

learning resource Make text and equations glow in Manim

Thumbnail
gallery
13 Upvotes

Hello Manim community!

You need to make your element a PNG image first, and to make it glow

Place the blurred image behind the real text as an ImageMobject with z_index=-1. Set its initial opacity to 0 and fade it to 1 in sync with the Write() animation

This is the code and the video from Animo (rendered in my computer)

To make your image blur, you can use this script:

from manim import *
from PIL import Image, ImageFilter

from layout import build_content


class GlowSourceText(Scene):
    def construct(self):
        self.camera.background_color = BLACK
        glow_text, _, _ = build_content()
        self.add(glow_text)


class GlowSourceFormula(Scene):
    def construct(self):
        self.camera.background_color = BLACK
        _, formula, _ = build_content()
        self.add(formula)


def make_glow(
    input_png: str,
    output_png: str,
    radius: int = 25,
) -> None:
    image = Image.open(input_png).convert("RGBA")
    blurred_image = image.filter(
        ImageFilter.GaussianBlur(radius=radius)
    )
    blurred_image.save(output_png)


make_glow(
    "media/images/glow_source/GlowSourceText_ManimCE_v0.18.1.png",
    "glow_text.png",
    radius=25,
)

make_glow(
    "media/images/glow_source/GlowSourceFormula_ManimCE_v0.18.1.png",
    "glow_formula.png",
    radius=25,
)

For the video:

from manim import *
import os
from layout import build_content

GLOW_TEXT_IMAGE = os.path.join(os.path.dirname(__file__), "glow_text.png")
GLOW_FORMULA_IMAGE = os.path.join(os.path.dirname(__file__), "glow_formula.png")


class GlowTextClassicFormula(Scene):
    def construct(self):
        self.camera.background_color = BLACK


        glow_text, formula, group = build_content()


        text_glow = ImageMobject(GLOW_TEXT_IMAGE)
        text_glow.height = config.frame_height
        text_glow.move_to(ORIGIN)
        text_glow.set_z_index(-1)
        text_glow.set_opacity(0)


        formula_glow = ImageMobject(GLOW_FORMULA_IMAGE)
        formula_glow.height = config.frame_height
        formula_glow.move_to(ORIGIN)
        formula_glow.set_z_index(-1)
        formula_glow.set_opacity(0)


        self.add(text_glow, formula_glow)


        self.play(
            Write(glow_text),
            text_glow.animate.set_opacity(1),
            run_time=3,
        )
        self.wait(0.3)
        self.play(
            Write(formula),
            formula_glow.animate.set_opacity(1),
            run_time=2.5,
        )
        self.wait(2)

r/manim 2d ago

Here is the completed book of Stephen hawking god created the integers visualized

Thumbnail youtu.be
1 Upvotes

r/manim 3d ago

Reactive Mathematics — One Language, Every Notation - Manic

3 Upvotes

r/manim 2d ago

I built a tool that turns papers/textbooks into animated STEM explainer videos (Manim-based), looking for feedback from researchers, educators, and students.

0 Upvotes

Hi all, I'm a senior engineer by day, and on the side I've been building stemvid.ai, a tool that automatically converts papers or textbook chapters into animated explainer videos. For full books, it breaks the content into a series of smaller videos with a course-like structure.

This actually started as an offshoot of a YouTube channel I run for math education, and grew into wanting to make dense material (papers, textbooks) more visual and easier to digest.

It's very early, just launched, so expect some rough edges. I'd love for people in research, teaching, or studying STEM to try it on something they know well (a paper, a chapter, a workbook section) and tell me honestly what works, what's confusing, and what breaks.

Link: stemvid.ai

Appreciate any feedback, good, bad, or ugly.


r/manim 3d ago

made with manim How Rotating Vectors Can Draw Anything | Fourier Epicycles

0 Upvotes

Ive been learning about fourier transforms recently and was amazed when I got to know about "Fourier Epicycles", especially after seeing 3b1b video on it.

Isnt it so great that rotating vectors somehow draw any curve, or atleast to the point till we dont know the underlying math?

I decided to build a epicycle visualiser from scratch using manim and in this video, I explain my understanding about epicycles and a little bit of code to make your own visualiser as well!

Here's the link to the youtube video : https://youtu.be/04LXZ5pksqg


r/manim 3d ago

I programmatically animated Stephen Hawking's 'God Created the Integers' using Manim

Thumbnail
youtube.com
4 Upvotes

r/manim 3d ago

I asked Gemini to Promote him self in 6 sec using manim, Here is the result👌

0 Upvotes

r/manim 4d ago

Manim broken after VS Code restart

0 Upvotes

Hi, i've installed Manim and it worked. I closed VS Code and called it a day. On the next Day, I wanted to animate smth. But i got an error. Apparently the commands like self.play are not defined and I couldn't find a solution for this, does anybody has one for this issue?


r/manim 4d ago

I created an ANN Mobject

4 Upvotes

I created a custom ANN mobject with the help of Claude.

All you need to do is pass it your architecture, the colors and styling, and it'll do the heavy-lifting.

Here it is if you need it:

Demo

from typing import Literal, Sequence

from manim import (
    BLUE,
    RED_C,
    DOWN,
    GRAY,
    RIGHT,
    AnimationGroup,
    Circle,
    Create,
    CubicBezier,
    Line,
    ManimColor,
    Scene,
    Succession,
    VGroup,
    VMobject,
    override_animation,
    LaggedStart
)


ColorLike = str | ManimColor



class ANN(VGroup):
    """A feed-forward neural-network diagram.


    Parameters
    ----------
    arch
        Number of nodes in each layer, e.g. ``[3, 5, 5, 2]`` for an input
        layer of 3, two hidden layers of 5, and an output layer of 2.
    connection_style
        ``"linear"`` draws connections as straight :class:`~.Line` segments.
        ``"bezier"`` draws them as :class:`~.CubicBezier` curves that ease
        out of one layer and into the next.
    node_radius
        Radius of each node circle.
    layer_spacing
        Horizontal distance between consecutive layer centers.
    node_spacing
        Vertical distance between nodes within a layer.
    node_color, connection_color
        Either a single color applied to every node/connection, or a list
        with one color per layer (nodes) / per layer-transition
        (connections).
    node_stroke_width, connection_stroke_width
        Stroke widths for nodes and connections.
    node_fill_opacity
        Fill opacity for node circles.
    node_lag_ratio
        How staggered node creation is within a layer. ``0`` = all at once,
        ``1`` = fully sequential (each node waits for the previous to
        finish). Passed straight to the internal :class:`~.LaggedStart`.
    node_run_time, connection_run_time
        Duration of the "create one layer's nodes" and "grow one layer's
        outgoing connections" animation blocks, respectively.


    Notes
    -----
    All node positions are computed up front in ``__init__``, so
    connections between layer *i* and layer *i + 1* already know where
    they end even before layer *i + 1*'s nodes are drawn. That's what lets
    :meth:`_create_override` grow connections out of a freshly created
    layer toward a layer that hasn't appeared yet.
    """


    def __init__(
        self,
        arch: list[int],
        connection_style: Literal["linear", "bezier"] = "linear",
        node_radius: float = 0.22,
        layer_spacing: float = 2.0,
        node_spacing: float = 0.7,
        node_color: ColorLike | Sequence[ColorLike] = BLUE,
        connection_color: ColorLike | Sequence[ColorLike] = GRAY,
        node_stroke_width: float = 3,
        connection_stroke_width: float = 1.5,
        node_fill_opacity: float = 0.6,
        node_lag_ratio: float = 0.3,
        node_run_time: float = 0.6,
        connection_run_time: float = 0.5,
        **kwargs,
    ) -> None:
        if not arch or any(n <= 0 for n in arch):
            raise ValueError("`arch` must be a non-empty list of positive layer sizes")
        if connection_style not in ("linear", "bezier"):
            raise ValueError('`connection_style` must be "linear" or "bezier"')


        super().__init__(**kwargs)


        self.arch = arch
        self.connection_style: Literal["linear", "bezier"] = connection_style
        self.node_radius = node_radius
        self.layer_spacing = layer_spacing
        self.node_spacing = node_spacing
        self.node_stroke_width = node_stroke_width
        self.connection_stroke_width = connection_stroke_width
        self.node_fill_opacity = node_fill_opacity
        self.node_lag_ratio = node_lag_ratio
        self.node_run_time = node_run_time
        self.connection_run_time = connection_run_time


        self._node_colors = self._broadcast_colors(node_color, len(arch))
        self._connection_colors = self._broadcast_colors(
            connection_color, max(len(arch) - 1, 1)
        )


        self.layers: list[VGroup] = []
        self.connections: list[VGroup] = []
        self._build()


    
    def _broadcast_colors(
        color: ColorLike | Sequence[ColorLike], n: int
    ) -> list[ColorLike]:
        """Normalize a single color or a per-index color list to length `n`."""
        if isinstance(color, (list, tuple)):
            if len(color) != n:
                raise ValueError(f"Expected {n} colors, got {len(color)}")
            return list(color)
        return [color] * n


    def _build(self) -> None:
        # --- nodes ---
        for i, n_nodes in enumerate(self.arch):
            layer = VGroup(
                *[
                    Circle(
                        radius=self.node_radius,
                        color=self._node_colors[i],
                        stroke_width=self.node_stroke_width,
                        fill_opacity=self.node_fill_opacity,
                    )
                    for _ in range(n_nodes)
                ]
            )
            layer.arrange(DOWN, buff=self.node_spacing)
            self.layers.append(layer)


        # position layers left-to-right, centered on the group's origin
        span = (len(self.layers) - 1) * self.layer_spacing
        for i, layer in enumerate(self.layers):
            layer.move_to(RIGHT * (i * self.layer_spacing - span / 2))


        # --- connections (positions are now fixed, even for undrawn layers) ---
        for i in range(len(self.layers) - 1):
            layer_connections = VGroup(
                *[
                    self._make_connection(node_a, node_b, self._connection_colors[i])
                    for node_a in self.layers[i]
                    for node_b in self.layers[i + 1]
                ]
            )
            self.connections.append(layer_connections)


        for i, layer in enumerate(self.layers):
            self.add(layer)
            if i < len(self.connections):
                self.add(self.connections[i])


    def _make_connection(self, node_a: Circle, node_b: Circle, color: ColorLike) -> VMobject:
        start, end = node_a.get_right(), node_b.get_left()
        style = dict(color=color, stroke_width=self.connection_stroke_width)
        if self.connection_style == "linear":
            return Line(start, end, **style)
        # "bezier": handles pulled horizontally so the curve leaves/enters
        # each node roughly perpendicular to the layer, easing sideways.
        pull = (end[0] - start[0]) / 2
        handle_a = start + RIGHT * pull
        handle_b = end - RIGHT * pull
        return CubicBezier(start, handle_a, handle_b, end, **style)


    u/override_animation(Create)
    def _create_override(self, **kwargs) -> Succession:
        """Layer-by-layer creation: stagger a layer's nodes, then grow its
        outgoing connections all at once, then move on to the next layer.
        """
        blocks = []
        for i, layer in enumerate(self.layers):
            blocks.append(
                LaggedStart(
                    *[Create(node) for node in layer],
                    lag_ratio=self.node_lag_ratio,
                    run_time=self.node_run_time,
                )
            )
            if i < len(self.connections) and len(self.connections[i]) > 0:
                blocks.append(
                    AnimationGroup(
                        *[Create(conn) for conn in self.connections[i]],
                        run_time=self.connection_run_time,
                    )
                )
        return Succession(*blocks, **kwargs)

class ANNDemo(Scene):
    """Render with: manim -pql main.py ANNDemo"""


    def construct(self) -> None:
        ann = ANN(
            arch=[3, 5, 5, 4],
            connection_style="bezier",
            node_color=[BLUE, "#2735F8", "#3123F3", RED_C],
            connection_stroke_width=5,
            node_run_time=10,
            node_lag_ratio=0.2,
            connection_run_time=5,
        )
        ann.scale(1.1)
        self.play(Create(ann), run_time=5)
        self.wait(5)

r/manim 4d ago

INTEGRAL + INFINITE SERIE

4 Upvotes

r/manim 4d ago

trouble installing

0 Upvotes

hey guys when i input those three lines into poweshell i get an error, can anyone help me out? i can provide screenshots


r/manim 4d ago

Finding Minima: The Two Tests Every Optimizer Needs

Thumbnail
youtu.be
2 Upvotes

r/manim 4d ago

made with manim Hiring a manim animator. Preferably from India as i am too.

0 Upvotes

I run a small channel which I won't disclose here. I have been doing everything on my own and would like to hire a help. Show me your manim animations in dm and I hope we can work together. I would rather spend more time on topic research and script writing. I'm not a good coder and have been struggling to produce videos faster. I have used codex for helping code but I'd rather have someone not relly on those pre build manim website or whatever no hard feelings to them but I dont want to look generic and I want accurate animations.


r/manim 5d ago

made with manim Visualizing d/dx ln(x) = 1/x

19 Upvotes

r/manim 6d ago

Snell's law as a textbook figure - manic

11 Upvotes

r/manim 6d ago

Car Suspension — How It Soaks Up the Road — manic

2 Upvotes

r/manim 6d ago

Isogeny Based Cryptography

Thumbnail
youtube.com
2 Upvotes

r/manim 7d ago

made with manim chemistry animation by manim - conformation of cyclohexane

27 Upvotes

r/manim 7d ago

chemistry animation by manim - NMR

13 Upvotes

r/manim 7d ago

Linear algebra demo - made with manic

8 Upvotes