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 |
|---|---|
Briefly scales the Mob up and tints its color. The standard “look here” effect. |
|
Draws an animated bounding outline around the Mob. |
|
Emits a quick radial burst of short rays from a point or Mob. |
|
Dims the rest of the scene and targets a specific point. |
|
Wiggles the Mob back and forth. |
|
Rapidly toggles the Mob’s visibility. |
Example: AnimationsWiggleBlink ¶
from algan import *
square = Square(color=BLUE).scale(0.6).move(LEFT * 2).spawn()
circle = Circle(color=YELLOW).scale(0.6).move(RIGHT * 2).spawn()
with Sync():
Wiggle(square)
Blink(circle, blinks=2)
Scene.save_video()
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 |
|---|---|
Applies a 2×2 or 3×3 transformation matrix. |
|
Applies any point-to-point function. |
|
Treats the xy-plane as the complex plane and applies a complex function. |
|
A continuous time-dependent deformation |
|
Time-dependent deformation on the complex plane. |
|
Integrates a vector field to flow points along it over time. |
|
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¶
Updaters – animations that run indefinitely rather than for a fixed runtime.
Grouping Mobs – applying an animation to a whole hierarchy at once.
Combining Animations – the contexts that decide when these run.
Custom Animations – writing an animation of your own with
animated_function().Animating Out of Order – scheduling these animations at times you compute yourself.
The Mob Gallery – the Mobs to apply them to.