Animations
Animating Time-Varying Phenomena
Gradient descent converging to a minimum, a constellation rotating
due to frequency offset, BER improving as you add more training
symbols β these are all time-varying processes best understood
through animation. Matplotlib's FuncAnimation creates MP4 and
GIF files; Plotly's animation_frame does the same in HTML.
Definition: FuncAnimation β Frame-by-Frame Animation
FuncAnimation β Frame-by-Frame Animation
matplotlib.animation.FuncAnimation calls an update function for
each frame:
from matplotlib.animation import FuncAnimation
fig, ax = plt.subplots()
line, = ax.plot([], [])
def init():
ax.set_xlim(0, 2*np.pi)
ax.set_ylim(-1.5, 1.5)
return line,
def update(frame):
x = np.linspace(0, 2*np.pi, 200)
line.set_data(x, np.sin(x + frame * 0.1))
return line,
anim = FuncAnimation(fig, update, frames=100,
init_func=init, blit=True, interval=50)
anim.save('wave.gif', writer='pillow', fps=20)
Definition: ArtistAnimation β Pre-Rendered Frames
ArtistAnimation β Pre-Rendered Frames
ArtistAnimation takes a list of pre-rendered artist lists:
from matplotlib.animation import ArtistAnimation
fig, ax = plt.subplots()
frames = []
for i in range(50):
line, = ax.plot(x, np.sin(x + i*0.1), color='blue')
frames.append([line])
anim = ArtistAnimation(fig, frames, interval=50, blit=True)
Use FuncAnimation for memory-efficient animations (one frame at a
time); use ArtistAnimation when all frames are already computed.
Theorem: Blitting for Smooth Animations
With blit=True, Matplotlib only redraws the changed artists each
frame, not the entire figure. For static
elements and animated elements:
Blitting gives 5-20x speedup when backgrounds are complex (grids, legends, annotations).
Blitting saves a snapshot of the static background and only re-renders the moving elements on top.
Example: Animating Gradient Descent Convergence
Animate gradient descent on the Rosenbrock function, showing the path converging to the minimum.
Implementation
from matplotlib.animation import FuncAnimation
import numpy as np
import matplotlib.pyplot as plt
def rosenbrock(x, y):
return (1 - x)**2 + 100*(y - x**2)**2
# Gradient descent path
lr = 0.001
pos = np.array([-1.5, 1.5])
path = [pos.copy()]
for _ in range(500):
gx = -2*(1-pos[0]) - 400*pos[0]*(pos[1]-pos[0]**2)
gy = 200*(pos[1] - pos[0]**2)
pos = pos - lr * np.array([gx, gy])
path.append(pos.copy())
path = np.array(path)
# Plot
x = np.linspace(-2, 2, 200)
X, Y = np.meshgrid(x, x)
Z = rosenbrock(X, Y)
fig, ax = plt.subplots(figsize=(6, 5))
ax.contour(X, Y, np.log10(Z + 1), levels=30, cmap='viridis')
point, = ax.plot([], [], 'ro', ms=8, zorder=5)
trail, = ax.plot([], [], 'r-', lw=1, alpha=0.5)
def update(frame):
i = frame * 5
point.set_data([path[i, 0]], [path[i, 1]])
trail.set_data(path[:i, 0], path[:i, 1])
return point, trail
anim = FuncAnimation(fig, update, frames=100, interval=50, blit=True)
anim.save('gd_rosenbrock.gif', writer='pillow', fps=20)
Example: Animating BER Estimate Convergence
Animate how the BER estimate stabilizes as more bits are simulated.
Implementation
rng = np.random.default_rng(42)
N_total = 100_000
snr_lin = 10 # ~10 dB
bits = rng.integers(0, 2, N_total)
noise = rng.standard_normal(N_total) / np.sqrt(2 * snr_lin)
rx = (2*bits - 1) + noise
errors_cum = np.cumsum((rx > 0) != bits.astype(bool))
n_range = np.arange(1, N_total + 1)
ber_running = errors_cum / n_range
fig, ax = plt.subplots(figsize=(8, 4))
ax.axhline(0.5 * erfc(np.sqrt(snr_lin)), color='red',
ls='--', label='Theory')
line, = ax.plot([], [], color='#2563EB')
ax.set(xlabel='Number of Bits', ylabel='BER Estimate',
xlim=[1, N_total], ylim=[0, 0.1])
def update(frame):
n = int(10 ** (2 + frame * 3 / 60))
line.set_data(n_range[:n], ber_running[:n])
return line,
anim = FuncAnimation(fig, update, frames=60, interval=100)
anim.save('ber_convergence.gif', writer='pillow')
Wave Propagation Animation
Watch a sinusoidal wave propagate through space, reflecting the physical wave phenomena in wireless channels.
Parameters
Convergence Rate Visualizer
Compare how fast different optimization algorithms converge on a 2D cost function.
Parameters
Common Mistake: Animations Not Displaying in Jupyter
Mistake:
Creating a FuncAnimation in Jupyter without setting the HTML
representation, resulting in just a static first frame.
Correction:
Use %matplotlib widget or convert to HTML:
from IPython.display import HTML
HTML(anim.to_jshtml())
Key Takeaway
Use animations sparingly but effectively. They are best for showing convergence processes, time-varying signals, and parameter sweeps. Always provide a static "final frame" alternative for papers. Save as GIF for presentations, MP4 for quality.
Quick Check
What does blit=True do in FuncAnimation?
Adds blur to smooth the animation
Only redraws changed artists each frame, skipping static elements
Doubles the frame rate
Compresses the output file
Blitting caches the static background and only re-renders dynamic elements.
FuncAnimation
A Matplotlib class that creates animations by calling an update function for each frame, supporting blitting for performance.
Blitting
An animation optimization technique that caches the static background and only redraws dynamic elements each frame.