Skip to content

Together and in turn

The film's code
import manimgx as m


class TogetherHero(m.Scene):
    def construct(self) -> None:
        dots = m.VGroup(*[m.Dot(radius=0.2, color=m.YELLOW) for _ in range(6)]).arrange(
            buff=0.8
        )
        self.play(
            m.LaggedStart(*[m.GrowFromCenter(dot) for dot in dots], lag_ratio=0.3)
        )
        self.play(m.Succession(*[dot.animate.shift(m.UP) for dot in dots], run_time=2))
        self.play(m.AnimationGroup(*[dot.animate.shift(m.DOWN) for dot in dots]))
        self.wait()

self.play(a, b) plays a and b together. A group plays animations as one: together, one after another, or each a little after the one before it. A group is an animation itself, so groups nest, and a group plays inside a larger play.

AnimationGroup

Code
import manimgx as m


class AnimationGroupExample(m.Scene):
    def construct(self) -> None:
        shapes = (
            m.VGroup(
                m.Square(color=m.BLUE, fill_opacity=0.5),
                m.Circle(color=m.YELLOW, fill_opacity=0.5),
                m.Triangle(color=m.GREEN, fill_opacity=0.5),
            )
            .scale(1.2)
            .arrange(buff=1)
        )
        self.play(
            m.AnimationGroup(
                m.Create(shapes[0]),
                m.FadeIn(shapes[1], shift=m.UP),
                m.GrowFromCenter(shapes[2]),
                lag_ratio=0.5,
            )
        )

Play animations together, each in its own window of the group's time.

The parts are laid out in time: each begins when the part before it has played lag_ratio of its run time (all together with the default 0, one after another at 1, overlapping in between), and plays for its own run time. The group lasts until its last part ends, unless given a run_time, which stretches or squeezes the whole layout to fit. Its rate function (by default linear) warps the group's time, and each part still eases its own window with its own.

A part begins when its window opens, taking its mobjects as they are then (and bringing its mobject into the scene, if it introduces it), and finishes when its window closes, whatever else is still playing, leaving the scene as it leaves it (a remover's mobject leaves it). A part whose window ends with the group's finishes with the group, where the group's rate function ends its time. Before its window a part has touched nothing; after it, what it did stays done. The group itself is not a mobject of the scene: it adds nothing of its own. Scene.play plays several animations as such a group.

m.AnimationGroup(*animations, group=None, **kwargs)
*animations

The animations, or iterables of them.

group

The mobject the group animates; by default, a group of its parts' mobjects, but those that parts introduce.

run_time

How long the animation plays, in seconds (default 1).

lag_ratio

How the parts of the mobject are staggered: each begins this fraction of its run after the one before it begins (default 0: all together; 1: one after another).

rate_func

How the animation's progress runs with time: a function from [0, 1] to [0, 1] (default smooth; see rate functions).

reverse_rate_function

Whether to run the animation backward (default False).

name

A name for the animation.

remover

Whether the mobject leaves the scene when the animation finishes (default False).

suspend_mobject_updating

Whether the mobject's updaters run beneath the animation (default True): they keep acting on the mobject, and each frame shows the animation applied to the result. If False, they act on the animated mobject itself.

introducer

Whether the mobject joins the scene when the animation begins (default False); otherwise the play brings it in when the play begins, if the scene lacks it.

use_override

Whether a mobject whose class plays another animation in place of this one does so (default True).

It also takes the animation keywords.

Source

src/manimgx/animation/timeline.py

def __init__(
    self,
    *animations: Animation
    | Iterable[Animation],  # of any mobjects (Animation is invariant in its)
    group: Group | VGroup | None = None,
    **kwargs: Unpack[AnimationOptions],
):
    self.animations = [prepare(a) for a in _flatten(animations)]
    self.group = group or Group(
        *remove_list_redundancies(
            [a.mobject for a in self.animations if not a.is_introducer()]
        )
    )
    super().__init__(self.group, **kwargs)
    self.scene: Scene | None = None
    self.begun: set[int] = set()
    self.done: set[int] = set()
    self._fresh: set[int] = set()  # the parts begun at the instant being computed
    # a run time given; None: the layout's own, where its last part ends
    self._given: float | None = (animation_defaults(type(self)) | kwargs).get(
        "run_time"
    )
    self._layout: tuple[tuple[object, ...], float, list[Placed]] = ((), 0.0, [])
    self._playing = False  # between `begin` and `finish`: its layout fixed
    self._placed: tuple[tuple[object, ...], list[Placed]] = ((), [])

run_time

How long the group plays, in seconds: the run time it was given, else where its last part ends.

animation_group.run_time
Source

src/manimgx/animation/timeline.py

def run_time(self) -> float:
    """How long the group plays, in seconds: the run time it was given, else where its
    last part ends."""
    return self.max_end_time if self._given is None else self._given

Succession

Code
import manimgx as m


class SuccessionExample(m.Scene):
    def construct(self) -> None:
        square = m.Square(side_length=2, color=m.BLUE, fill_opacity=0.5)
        square.shift(3 * m.LEFT)
        self.play(
            m.Succession(
                m.Create(square),
                square.animate.shift(6 * m.RIGHT),
                m.Rotate(square, m.PI / 4),
                square.animate.set_color(m.YELLOW),
            )
        )

Play animations one after another.

An AnimationGroup whose lag_ratio is 1: each part begins when the one before it ends, from the state it left.

m.Succession(*animations, group=None, **kwargs)
*animations

The animations, in order, or iterables of them.

group

The mobject the group animates; by default, a group of its parts' mobjects, but those that parts introduce.

run_time

How long the animation plays, in seconds (default 1).

lag_ratio

How the parts of the mobject are staggered: each begins this fraction of its run after the one before it begins (default 0: all together; 1: one after another).

rate_func

How the animation's progress runs with time: a function from [0, 1] to [0, 1] (default smooth; see rate functions).

reverse_rate_function

Whether to run the animation backward (default False).

name

A name for the animation.

remover

Whether the mobject leaves the scene when the animation finishes (default False).

suspend_mobject_updating

Whether the mobject's updaters run beneath the animation (default True): they keep acting on the mobject, and each frame shows the animation applied to the result. If False, they act on the animated mobject itself.

introducer

Whether the mobject joins the scene when the animation begins (default False); otherwise the play brings it in when the play begins, if the scene lacks it.

use_override

Whether a mobject whose class plays another animation in place of this one does so (default True).

It also takes the animation keywords.

Source

src/manimgx/animation/timeline.py

def __init__(
    self,
    *animations: Animation
    | Iterable[Animation],  # of any mobjects (Animation is invariant in its)
    group: Group | VGroup | None = None,
    **kwargs: Unpack[AnimationOptions],
):
    self.animations = [prepare(a) for a in _flatten(animations)]
    self.group = group or Group(
        *remove_list_redundancies(
            [a.mobject for a in self.animations if not a.is_introducer()]
        )
    )
    super().__init__(self.group, **kwargs)
    self.scene: Scene | None = None
    self.begun: set[int] = set()
    self.done: set[int] = set()
    self._fresh: set[int] = set()  # the parts begun at the instant being computed
    # a run time given; None: the layout's own, where its last part ends
    self._given: float | None = (animation_defaults(type(self)) | kwargs).get(
        "run_time"
    )
    self._layout: tuple[tuple[object, ...], float, list[Placed]] = ((), 0.0, [])
    self._playing = False  # between `begin` and `finish`: its layout fixed
    self._placed: tuple[tuple[object, ...], list[Placed]] = ((), [])

LaggedStart

Code
import manimgx as m


class LaggedStartExample(m.Scene):
    def construct(self) -> None:
        dots = m.VGroup(*(m.Dot(radius=0.25, color=m.YELLOW) for _ in range(8)))
        dots.arrange(buff=0.8).shift(2 * m.UP)
        self.add(dots)
        self.play(
            m.LaggedStart(
                *(dot.animate.shift(4 * m.DOWN) for dot in dots), lag_ratio=0.2
            )
        )

Play animations one shortly after another, overlapping.

An AnimationGroup whose lag_ratio is small (0.05 by default): each part begins when the one before it has played that fraction of its run time.

m.LaggedStart(*animations, group=None, **kwargs)
*animations

The animations, in order, or iterables of them.

group

The mobject the group animates; by default, a group of its parts' mobjects, but those that parts introduce.

run_time

How long the animation plays, in seconds (default 1).

lag_ratio

How the parts of the mobject are staggered: each begins this fraction of its run after the one before it begins (default 0: all together; 1: one after another).

rate_func

How the animation's progress runs with time: a function from [0, 1] to [0, 1] (default smooth; see rate functions).

reverse_rate_function

Whether to run the animation backward (default False).

name

A name for the animation.

remover

Whether the mobject leaves the scene when the animation finishes (default False).

suspend_mobject_updating

Whether the mobject's updaters run beneath the animation (default True): they keep acting on the mobject, and each frame shows the animation applied to the result. If False, they act on the animated mobject itself.

introducer

Whether the mobject joins the scene when the animation begins (default False); otherwise the play brings it in when the play begins, if the scene lacks it.

use_override

Whether a mobject whose class plays another animation in place of this one does so (default True).

It also takes the animation keywords.

Source

src/manimgx/animation/timeline.py

def __init__(
    self,
    *animations: Animation
    | Iterable[Animation],  # of any mobjects (Animation is invariant in its)
    group: Group | VGroup | None = None,
    **kwargs: Unpack[AnimationOptions],
):
    self.animations = [prepare(a) for a in _flatten(animations)]
    self.group = group or Group(
        *remove_list_redundancies(
            [a.mobject for a in self.animations if not a.is_introducer()]
        )
    )
    super().__init__(self.group, **kwargs)
    self.scene: Scene | None = None
    self.begun: set[int] = set()
    self.done: set[int] = set()
    self._fresh: set[int] = set()  # the parts begun at the instant being computed
    # a run time given; None: the layout's own, where its last part ends
    self._given: float | None = (animation_defaults(type(self)) | kwargs).get(
        "run_time"
    )
    self._layout: tuple[tuple[object, ...], float, list[Placed]] = ((), 0.0, [])
    self._playing = False  # between `begin` and `finish`: its layout fixed
    self._placed: tuple[tuple[object, ...], list[Placed]] = ((), [])

LaggedStartMap

Code
import manimgx as m


class LaggedStartMapExample(m.Scene):
    def construct(self) -> None:
        dots = m.VGroup(*(m.Dot(radius=0.2) for _ in range(35)))
        dots.arrange_in_grid(rows=5, cols=7, buff=0.6)
        self.play(m.LaggedStartMap(m.GrowFromCenter, dots, lag_ratio=0.1))

Play an animation on each submobject of a mobject, one shortly after another.

Each submobject's animation is animation_class(*arg_creator(submobject), **kwargs) (by default, animation_class(submobject, **kwargs)), and they play as a LaggedStart. run_time (2 seconds by default) and lag_ratio time the whole; every other option goes to each animation.

m.LaggedStartMap(animation_class, mobject, arg_creator=None, **kwargs)
animation_class

The animation to play on each submobject: a class, or any function returning an animation.

mobject

The mobject whose submobjects are animated.

arg_creator

A function from a submobject to the animation's positional arguments; None for the submobject alone.

run_time

How long the animation plays, in seconds (default 1).

lag_ratio

How the parts of the mobject are staggered: each begins this fraction of its run after the one before it begins (default 0: all together; 1: one after another).

rate_func

How the animation's progress runs with time: a function from [0, 1] to [0, 1] (default smooth; see rate functions).

reverse_rate_function

Whether to run the animation backward (default False).

name

A name for the animation.

remover

Whether the mobject leaves the scene when the animation finishes (default False).

suspend_mobject_updating

Whether the mobject's updaters run beneath the animation (default True): they keep acting on the mobject, and each frame shows the animation applied to the result. If False, they act on the animated mobject itself.

introducer

Whether the mobject joins the scene when the animation begins (default False); otherwise the play brings it in when the play begins, if the scene lacks it.

use_override

Whether a mobject whose class plays another animation in place of this one does so (default True).

It also takes the animation keywords.

Source

src/manimgx/animation/timeline.py

def __init__(
    self,
    animation_class: Callable[
        ..., Animation
    ],  # given each part's arguments (`arg_creator`)
    mobject: Mobject,
    arg_creator: Callable[[Mobject], Iterable[object]] | None = None,
    **kwargs: Unpack[AnimationOptions],
):
    timing: AnimationOptions = {}
    if "run_time" in kwargs:
        timing["run_time"] = kwargs.pop("run_time")
    if "lag_ratio" in kwargs:
        timing["lag_ratio"] = kwargs.pop("lag_ratio")
    arguments = arg_creator or (lambda part: (part,))
    super().__init__(
        *(animation_class(*arguments(part), **kwargs) for part in mobject), **timing
    )

ChangeSpeed

Code
import manimgx as m


class ChangeSpeedExample(m.Scene):
    def construct(self) -> None:
        steady = m.Dot(5 * m.LEFT + m.UP, radius=0.25, color=m.BLUE)
        slowed = m.Dot(5 * m.LEFT + m.DOWN, radius=0.25, color=m.YELLOW)
        self.add(steady, slowed)
        self.play(
            steady.animate(run_time=2, rate_func=m.linear).set_x(5),
            m.ChangeSpeed(
                slowed.animate(run_time=2, rate_func=m.linear).set_x(5),
                speedinfo={0.3: 1, 0.4: 0.2, 0.6: 0.2, 0.7: 1},
            ),
        )

Play an animation faster or slower along the way: at speeds that change as it plays.

speedinfo gives speeds at points of the animation's progress: {0.5: 2} plays it at its own speed at the start, speeding up to twice as fast at its middle, and twice as fast from there on. Between two points the speed changes steadily (at a constant acceleration, in the scene's time), so the stretch from progress a to b, at speeds v and w, takes (b - a) · 2 / (v + w) of the animation's run time; the play lasts as long as its stretches take. Unless given, the speed at the start is 1 and the speed at the end the last point's.

With affects_speed_updaters, the updaters added with ChangeSpeed.add_updater run at the same speeds while it plays, so that whatever they move keeps pace with the animation.

The speeds change only the animation's timing: it keeps its own rate function (and its parts' stagger), so at a speed of 1 throughout it is the animation itself.

m.ChangeSpeed(anim, speedinfo, affects_speed_updaters=True, **kwargs)
anim

The animation to play.

speedinfo

Speeds at points of the animation's progress, from 0 to 1: a speed of 2 plays it twice as fast, 0.5 half as fast.

affects_speed_updaters

Whether the updaters added with ChangeSpeed.add_updater follow the speeds while it plays.

run_time

How long the animation plays, in seconds (default 1).

lag_ratio

How the parts of the mobject are staggered: each begins this fraction of its run after the one before it begins (default 0: all together; 1: one after another).

rate_func

How the animation's progress runs with time: a function from [0, 1] to [0, 1] (default smooth; see rate functions).

reverse_rate_function

Whether to run the animation backward (default False).

name

A name for the animation.

remover

Whether the mobject leaves the scene when the animation finishes (default False).

suspend_mobject_updating

Whether the mobject's updaters run beneath the animation (default True): they keep acting on the mobject, and each frame shows the animation applied to the result. If False, they act on the animated mobject itself.

introducer

Whether the mobject joins the scene when the animation begins (default False); otherwise the play brings it in when the play begins, if the scene lacks it.

use_override

Whether a mobject whose class plays another animation in place of this one does so (default True).

It also takes the animation keywords.

Source

src/manimgx/animation/timeline.py

def __init__(
    self,
    anim: Animation,
    speedinfo: dict[float, float],
    affects_speed_updaters: bool = True,
    **kwargs: Unpack[AnimationOptions],
) -> None:
    self.anim = prepare(anim)
    speeds = dict(sorted(({0: 1} | speedinfo).items()))
    speeds.setdefault(1, speeds[max(speeds)])
    self.speedinfo = speeds
    self.affects_speed_updaters = affects_speed_updaters
    # each stretch: (from, to, speed at from, speed at to, where it starts in real time)
    self._stretches: list[tuple[float, float, float, float, float]] = []
    self._total = 0.0  # real time, per second of the wrapped animation
    for (a, v), (b, w) in pairwise(speeds.items()):
        self._stretches.append((a, b, v, w, self._total))
        self._total += 2 / (v + w) * (b - a)
    self._before = kwargs.pop("rate_func", linear)
    kwargs["rate_func"] = self._rate
    kwargs["run_time"] = self._total * self.anim.run_time
    super().__init__(self.anim, **kwargs)

add_updater

Add an updater to a mobject whose time follows the speed of any ChangeSpeed playing.

A time-based updater added this way is handed, as dt, the time of a clock that runs with the scene's, except while a ChangeSpeed with affects_speed_updaters plays: then it runs at that ChangeSpeed's speeds (its speedinfo, whatever its rate function eases), and while several play at once, at the speeds of the one that began last. The clock never runs backward. It keeps its kind: a flow stays a flow, and any other time-based updater still steps on the simulation clock. A per-frame updater has no time, and is added as it is.

ChangeSpeed.add_updater(mobject, update_function, index=None, call_updater=False)
mobject

The mobject to add it to.

update_function

The updater: a function of the mobject, or of the mobject and dt (see add_updater).

index

Where it goes among the mobject's updaters, which run in order; None for last.

call_updater

Whether to run it once right away; a time-based one is handed a dt of 0.

Source

src/manimgx/animation/timeline.py

@classmethod
def add_updater(
    cls,
    mobject: Mobject,
    update_function: Updater[Mobject],
    index: int | None = None,
    call_updater: bool = False,
) -> None:
    """Add an updater to a mobject whose time follows the speed of any `ChangeSpeed`
    playing.

    A time-based updater added this way is handed, as `dt`, the time of a clock that
    runs with the scene's, except while a `ChangeSpeed` with
    `affects_speed_updaters` plays: then it runs at that `ChangeSpeed`'s speeds (its
    `speedinfo`, whatever its rate function eases), and while several play at once,
    at the speeds of the one that began last. The clock never runs backward. It
    keeps its kind: a [flow][manimgx.mobject.flow] stays a flow, and any other
    time-based updater still steps on the simulation clock. A per-frame updater has
    no time, and is added as it is.

    Args:
        mobject: The mobject to add it to.
        update_function: The updater: a function of the mobject, or of the mobject
            and `dt` (see [`add_updater`][manimgx.Mobject.add_updater]).
        index: Where it goes among the mobject's updaters, which run in order; None
            for last.
        call_updater: Whether to run it once right away; a time-based one is handed
            a `dt` of 0.
    """
    if not _per_frame(update_function):
        on_clock = _OnClock(update_function, clock.speed)
        update_function = on_clock if simulated(update_function) else flow(on_clock)
    mobject.add_updater(update_function, index=index, call_updater=call_updater)

Wait

Code
import manimgx as m


class WaitExample(m.Scene):
    def construct(self) -> None:
        finish = m.Line(3 * m.UP, 3 * m.DOWN, color=m.RED).shift(3 * m.RIGHT)
        dot = m.Dot(4 * m.LEFT, radius=0.3, color=m.YELLOW)
        dot.add_updater(lambda mob, dt: mob.shift(3 * dt * m.RIGHT))
        self.add(finish, dot)
        self.play(m.Wait(10, stop_condition=lambda: dot.get_x() >= 3))

Let the scene's time run on, animating nothing.

The scene's updaters and its mobjects' keep running through it; Scene.wait plays one. Played alone, it can end early, at stop_condition, or freeze the frame; in a composition, it only takes up its time.

m.Wait(run_time=1, stop_condition=None, frozen_frame=None, **kwargs)
run_time

How long it lasts, in seconds.

stop_condition

A function checked at every frame: the wait ends at the first frame at which it returns True. None: it lasts its whole run time.

frozen_frame

Whether time stands still: one frame is held, and the updaters do not run, then go on as if no time had passed. It cannot be combined with a stop_condition (a ValueError).

lag_ratio

How the parts of the mobject are staggered: each begins this fraction of its run after the one before it begins (default 0: all together; 1: one after another).

rate_func

How the animation's progress runs with time: a function from [0, 1] to [0, 1] (default smooth; see rate functions).

reverse_rate_function

Whether to run the animation backward (default False).

name

A name for the animation.

remover

Whether the mobject leaves the scene when the animation finishes (default False).

suspend_mobject_updating

Whether the mobject's updaters run beneath the animation (default True): they keep acting on the mobject, and each frame shows the animation applied to the result. If False, they act on the animated mobject itself.

introducer

Whether the mobject joins the scene when the animation begins (default False); otherwise the play brings it in when the play begins, if the scene lacks it.

use_override

Whether a mobject whose class plays another animation in place of this one does so (default True).

It also takes the animation keywords.

Source

src/manimgx/animation/timeline.py

def __init__(
    self,
    run_time: float = 1,
    stop_condition: Callable[[], bool] | None = None,
    frozen_frame: bool | None = None,
    **kwargs: Unpack[Untimed],
) -> None:
    if stop_condition and frozen_frame:
        raise ValueError("A static Wait animation cannot have a stop condition.")
    self.duration = run_time
    self.stop_condition = stop_condition
    self.is_static_wait = frozen_frame
    super().__init__(None, run_time=run_time, **kwargs)

Add

Code
import manimgx as m


class AddExample(m.Scene):
    def construct(self) -> None:
        words = m.VGroup(
            *(m.Text(word, font_size=96) for word in ("one", "two", "three"))
        ).arrange(m.DOWN, buff=0.5)
        box = m.SurroundingRectangle(words, buff=0.5, color=m.BLUE)
        self.play(
            m.Create(box, run_time=3),
            m.Succession(*(m.Add(word, run_time=1) for word in words)),
        )

Add mobjects to the scene at a moment of a composition.

It adds its mobjects when it begins and, by default, takes no time: in a Succession, they appear when the animations before it have finished. With a run_time, it holds that long after adding them.

m.Add(*mobjects, **kwargs)
*mobjects

The mobjects to add; several are added as one group.

run_time

How long the animation plays, in seconds (default 1).

lag_ratio

How the parts of the mobject are staggered: each begins this fraction of its run after the one before it begins (default 0: all together; 1: one after another).

rate_func

How the animation's progress runs with time: a function from [0, 1] to [0, 1] (default smooth; see rate functions).

reverse_rate_function

Whether to run the animation backward (default False).

name

A name for the animation.

remover

Whether the mobject leaves the scene when the animation finishes (default False).

suspend_mobject_updating

Whether the mobject's updaters run beneath the animation (default True): they keep acting on the mobject, and each frame shows the animation applied to the result. If False, they act on the animated mobject itself.

introducer

Whether the mobject joins the scene when the animation begins (default False); otherwise the play brings it in when the play begins, if the scene lacks it.

use_override

Whether a mobject whose class plays another animation in place of this one does so (default True).

It also takes the animation keywords.

Source

src/manimgx/animation/timeline.py

def __init__(self, *mobjects: Mobject, **kwargs: Unpack[AnimationOptions]) -> None:
    super().__init__(
        mobjects[0] if len(mobjects) == 1 else _gathered(*mobjects), **kwargs
    )