Built-in Animations

Beyond standard Mob methods, Algan ships with a collection of ready-made animations for common video patterns: drawing attention to elements, moving objects along paths, and applying mathematical transformations across entire diagrams.

These are standard functions that take a Mob and record animations on the timeline, so they compose seamlessly with animation contexts:

Example: AnimationsCompose

from algan import *

circle = Circle(color=BLUE).scale(0.6).move(LEFT * 2).spawn()
square = Square(color=YELLOW).scale(0.6).move(RIGHT * 2).spawn()

with Sync():          # Both at the same time
    Indicate(circle)
    Indicate(square)

with Lag(0.3):        # Cascading ripple across elements
    for shape in (circle, square):
        Indicate(shape)

Scene.save_video()

Most animation functions take an optional runtime argument, which overrides the enclosing context’s timing for that specific animation.

Drawing Attention

Here are the built-in ways to draw the viewer’s eye to a specific Mob:

Example: AnimationsIndication

from algan import *

grid = Group([Square(color=BLUE).scale(0.35) for _ in range(9)])
grid.arrange_in_grid(3, row_buffer=0.5).spawn()

target = grid[4]
Indicate(target)
Circumscribe(target)
Flash(target)
FocusOn(target)

Scene.save_video()

Animation

What it does

Indicate()

Briefly scales the Mob up and tints its color. The standard “look here” effect.

Circumscribe()

Draws an animated bounding outline around the Mob.

Flash()

Emits a quick radial burst of short rays from a point or Mob.

FocusOn()

Dims the rest of the scene and targets a specific point.

Wiggle()

Wiggles the Mob back and forth.

Blink()

Rapidly toggles the Mob’s visibility.

Highlighting an Outline

ShowPassingFlash() sends a bright segment travelling along a Mob’s outline; the standard way to trace a path or emphasise the boundary of a region:

Example: AnimationsPassingFlash

from algan import *

outline = Circle(radius=2, color=BLUE, stroke_width=6).spawn()
ShowPassingFlash(outline, runtime=2)
ShowPassingFlash(outline, runtime=2)

Scene.save_video()

time_width controls how long the travelling segment is, as a fraction of the whole outline. ShowPassingFlashWithThinningStrokeWidth() does the same with a tapering stroke.

To draw a shape on screen as if by hand, use DrawBorderThenFill(). It first traces the outer border and then animates the fill:

Example: AnimationsDrawBorderThenFill

from algan import *

circle = Circle(color=BLUE).scale(0.8).move(LEFT * 2).spawn(False)
square = Square(color=YELLOW).scale(0.8).move(RIGHT * 2).spawn(False)

DrawBorderThenFill([circle, square], runtime=2)

Scene.save_video()

Notice that we called spawn(False) so the shapes don’t play their default fade-in before being drawn.

For text and LaTeX, write() provides the convenient glyph-by-glyph handwriting equivalent (see Text and Mathematics).

An indefinitely repeating version of the same idea is AnimatedBoundary, which keeps redrawing an outline around a Mob for as long as you leave it there. Unlike everything else on this page it is a Mob rather than a function, so spawn it, and call stop() to freeze it:

Example: AnimationsAnimatedBoundary

from algan import *

with Off():
    square = Square(color=TRANSPARENT, stroke_width=0).scale(1.5).spawn()
    boundary = AnimatedBoundary(square, max_stroke_width=5, cycle_rate=1.0).spawn()

square.wait(3)

Scene.save_video()

Give the source Mob no border of its own (as above), or the travelling highlight is drawn over the top of it and you will not see it. cycle_rate sets how fast the outline is traced and colors the palette it cycles through.

Moving Along a Path

MoveAlongPath() moves a Mob along the trajectory of another Mob’s outline. Any curve, line, or polygon can serve as the path:

Example: AnimationsMoveAlongPath

from algan import *

path = Circle(radius=2, color=GREY).spawn()
dot = Dot(color=YELLOW).spawn()
MoveAlongPath(dot, path, runtime=3)

Scene.save_video()

If you want the path itself to be invisible, you can leave it unspawned, the geometry is read directly from the Mob object.

Transforming Whole Diagrams

These functions apply spatial or mathematical mappings across all points of a Mob, making it easy to illustrate linear transformations, coordinate changes, or vector flows:

Animation

What it does

ApplyMatrix()

Applies a 2×2 or 3×3 transformation matrix.

ApplyPointwiseFunction()

Applies any point-to-point function.

ApplyComplexFunction()

Treats the xy-plane as the complex plane and applies a complex function.

Homotopy()

A continuous time-dependent deformation (x, y, z, t) -> (x, y, z).

ComplexHomotopy()

Time-dependent deformation on the complex plane.

PhaseFlow()

Integrates a vector field to flow points along it over time.

ApplyWave()

Propagates a wave distortion across a Mob.

Example: AnimationsApplyMatrix

from algan import *
import torch

grid = Group([Square(color=BLUE).scale(0.45) for _ in range(16)])
grid.arrange_in_grid(4, row_buffer=0.1).spawn()

ApplyMatrix(grid, torch.tensor([[1.0, 0.6], [0.0, 1.0]]), runtime=2)

Scene.save_video()

A homotopy receives the coordinates and the animation’s progress, so it can deform continuously rather than just interpolate between two states:

Example: AnimationsHomotopy

from algan import *
import torch

grid = Group([Square(color=BLUE).scale(0.3) for _ in range(16)])
grid.arrange_in_grid(4, row_buffer=0.15).spawn()

def swirl(x, y, z, t):
    angle = t * 1.5 * torch.exp(-(x ** 2 + y ** 2) / 6)
    return (x * torch.cos(angle) - y * torch.sin(angle),
            x * torch.sin(angle) + y * torch.cos(angle),
            z)

Homotopy(grid, swirl, runtime=3)

Scene.save_video()

PhaseFlow() takes a vector field instead and integrates it, which is the natural way to visualise a differential equation:

Example: AnimationsPhaseFlow

from algan import *
import torch

dots = Group([Dot(color=YELLOW).scale(1.5) for _ in range(25)])
dots.arrange_in_grid(5, row_buffer=1.0).spawn()

def rotation_field(points):
    return torch.stack((-points[..., 1], points[..., 0],
                        torch.zeros_like(points[..., 2])), -1) * 0.5

PhaseFlow(dots, rotation_field, runtime=3, virtual_time=2.0)

Scene.save_video()

virtual_time is how much of the field’s own time to integrate over, and integration_steps how finely. Both are independent of runtime, which is only how long the viewer watches it.

Important

All of these functions receive batched torch tensors, not individual points, and must return tensors of the same shape. Write them with torch operations (torch.cos, torch.exp, …) rather than the math module, and do not loop over points.

ApplyWave() is the simplest of the family and needs no function at all:

Example: AnimationsApplyWave

from algan import *

text = Text("wave me", font_size=72).spawn()
ApplyWave(text, runtime=2)

Scene.save_video()

Custom Animations

If you need an animation that isn’t covered here, you can write your own using animated_function() (see Custom Animations), or attach a continuous updater (see Updaters).

Where To Next