5.4.18Scientific Computing (Python)

Animation — FuncAnimation

1,747 words8 min readdifficulty · medium

WHY does this tool exist?

WHAT is it, precisely?

HOW the frame loop actually runs (derive the timing)

There is no equation to "prove," but we can derive the frame schedule from first principles so nothing is magic.

Figure — Animation — FuncAnimation

Worked Example 1 — a travelling sine wave (the canonical pattern)

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
 
fig, ax = plt.subplots()
ax.set_xlim(0, 2*np.pi)         # Why: set limits ONCE so blitting can cache background
ax.set_ylim(-1.1, 1.1)
x = np.linspace(0, 2*np.pi, 400)
line, = ax.plot([], [], lw=2)   # Why: create the artist ONCE, empty for now
 
def init():
    line.set_data([], [])       # Why: clean background; defines what "before frame 0" looks like
    return line,                # Why: blit needs the changed artists
 
def update(frame):              # frame = 0,1,2,... from frames=...
    y = np.sin(x - 0.1*frame)   # Why: shift phase by frame → wave moves right
    line.set_data(x, y)         # Why: MUTATE existing artist, don't create a new one
    return line,                # Why: tell blit what to redraw
 
anim = FuncAnimation(fig, update, frames=200, init_func=init,
                     interval=20, blit=True)   # 1000/20 = 50 FPS
plt.show()
# anim.save("wave.mp4", fps=50)  # or "wave.gif"

Why the comma in line, = ... and return line,? ax.plot returns a list of lines; line, unpacks the single element. return line, makes a 1-tuple — blit expects an iterable of artists.

Worked Example 2 — frames as a generator (variable data per frame)

def frame_gen():                 # Why: when each frame needs custom values, not just 0..N
    t = 0.0
    while t < 6.28:
        yield t                  # this value becomes `frame` in update
        t += 0.05
 
def update(t):
    line.set_data(x, np.sin(x - t))
    return line,
 
anim = FuncAnimation(fig, update, frames=frame_gen,
                     init_func=init, interval=30, blit=True, save_count=200)

Why save_count? A generator has unknown length; when saving, matplotlib needs to know how many frames to grab — save_count caps it.

Worked Example 3 — animating MORE than one artist (e.g. a moving dot + trail)

line, = ax.plot([], [], 'b-')
dot,  = ax.plot([], [], 'ro')
 
def update(i):
    xi = x[:i]
    line.set_data(xi, np.sin(xi))    # growing trail
    dot.set_data([x[i]], [np.sin(x[i])])  # Why: lists, because set_data wants sequences
    return line, dot                 # Why: return ALL changed artists for blit

Why return both? Blit redraws exactly what you return; forget one and it'll appear frozen.

Recall Feynman: explain to a 12-year-old

Imagine a flip-book. Every page is almost the same drawing, but the ball is nudged a little. Flip the pages fast and the ball looks like it's flying. FuncAnimation is a robot that holds your flip-book. You hand it ONE rubber-stamp function that says "for page number n, put the ball here," and a stopwatch that says "flip every 20 milliseconds." The robot does all the flipping. The trick: you don't draw a new ball each time — you just slide the same ball, which is why it's so fast.

Active recall

What does FuncAnimation repeatedly call to produce each frame?
The update function func, once per frame, on the same persistent figure.
With blit=True, what must init_func and func return?
An iterable of the changed artists (e.g. return line,).
Convert interval=25 ms to FPS.
FPS = 1000/25 = 40 frames per second.
Why create the line artist OUTSIDE the update function?
So you mutate one artist with set_data each frame instead of piling up new artists (keeps blitting valid and memory flat).
Why must you keep a reference like anim = FuncAnimation(...)?
Otherwise it's garbage-collected, the timer stops, and the animation freezes/blanks.
What does the trailing comma do in line, = ax.plot(...)?
Unpacks the single-element list ax.plot returns into the variable line.
When frames is a generator, why pass save_count?
The generator's length is unknown; save_count tells matplotlib how many frames to capture when saving.
How do you save an animation to mp4?
anim.save("out.mp4", fps=...) (needs ffmpeg) or .gif (needs pillow/imagemagick).
Total time for N=200 frames at interval=20ms (one pass)?
T = 200 × 20 ms = 4000 ms = 4 s.

Connections

  • Matplotlib Figure and Axes — the canvas FuncAnimation updates.
  • Artists in MatplotlibLine2D, set_data, blitting.
  • Generators in Python — used for the frames argument.
  • Event Loops / GUI backends — the timer that drives frames.
  • NumPy linspace and broadcasting — generating per-frame arrays.
  • Saving figures and ffmpeg — exporting to mp4/gif.

Concept Map

blocks and flickers

solved by

owns

repeatedly calls

updates same

feeds values to

sets FPS 1000/interval

draws clean

must mutate not recreate

redraws only changed

reuses cached

piles up artists

Naive loop plus plt.show

Need fast responsive animation

FuncAnimation

GUI timer loop

func frame

Persistent Figure

frames N or iterable

interval ms

Frame rate

init_func

Background

Artist via set_data

blit True

plt.plot inside func

Hinglish (regional understanding)

Intuition Hinglish mein samjho

Socho ek flip-book — har page pe drawing thodi si change hoti hai, aur fast flip karo to motion dikhta hai. FuncAnimation bilkul wahi karta hai: tum ek hi update(frame) function dete ho, aur matplotlib usko baar-baar call karta hai, har baar ek naya frame. Ek hi figure update hoti hai, naya figure har baar nahi banta — isliye fast aur smooth.

Sabse important pattern: artist (jaise line, = ax.plot([], [])) ko bahar ek hi baar banao, aur update ke andar sirf line.set_data(x, y) se uska data badlo. Galti ye hoti hai ki log ax.plot() ko update ke andar likh dete hain — isse har frame pe naya line ban jaata hai, memory badhti hai, aur blit toot jaata hai. interval milliseconds me hota hai: interval=20 matlab 1000/20 = 50 FPS.

blit=True use karo speed ke liye — par tab init aur update dono ko changed artists return karne padte hain (return line,). Ek classic bug: agar tum FuncAnimation(...) ko kisi variable me store nahi karte (anim = ...), to Python use garbage-collect kar deta hai aur animation freeze/blank ho jaata hai. Save karne ke liye anim.save("wave.mp4", fps=50) ya .gif. Bas itna yaad rakho: Figure, Init, Blit-return, Store — aur tumhari animation chal padegi.

Go deeper — visual, from zero

Test yourself — Scientific Computing (Python)

Connections