Skip to main content

Turn any static matplotlib script into an animated GIF by sweeping a variable

Project description

mpl-animator

Turn any static matplotlib script into an animated GIF or MP4 by sweeping a variable - no rewriting required.

Features

  • Zero boilerplate - point it at an existing script, it figures out the rest
  • AST-based dependency tracking - automatically identifies which variables and calculations need to update each frame
  • Parallel rendering - renders frames across all CPU cores via multiprocessing, falls back to sequential automatically
  • GIF and MP4 output - export as animated GIF (Pillow) or MP4 (ffmpeg); just add --format mp4
  • Axis rotation - animate azim with ax.view_init to rotate 3D plots; animate angle with ax.set_theta_offset to spin polar plots
  • Math expressions in ranges - --range "0,2*pi" just works
  • 2D, 3D, and polar plots - handles plot_surface, scatter3D, subplots, polar axes, and more
  • Single-file, standalone - mpl_animator.py can be dropped into any project with no install required; only depends on matplotlib, numpy, and Pillow (no exotic dependencies)
  • Library API - importable as a Python module for use in notebooks or pipelines

Install

pip install mpl-animator

Or just copy the file - no install needed:

# copy mpl_animator.py into your project, then use it directly
python mpl_animator.py my_plot.py --var t --range "0,1"

Usage

If installed via pip, use the mpl-animator command. If using the file directly, replace mpl-animator with python mpl_animator.py - everything else is identical.

# Basic: animate variable `f` from 3 to 60 (outputs GIF by default)
mpl-animator wave_static.py --var f --range "3,60"

# Export as MP4 instead of GIF (requires ffmpeg)
mpl-animator plot.py --var t --range "0,2*pi" --format mp4

# Math expressions in range, custom frame count and FPS
mpl-animator plot.py --var t --range "0,2*pi" --frames 60 --fps 30

# Rotate a 3D plot by animating the camera azimuth
mpl-animator my_3d_plot.py --var azim --range "0,360" --frames 72 --fps 20

# Control output quality and parallelism
mpl-animator plot.py --var alpha --range "0,1" --dpi 150 --workers 8

# Custom output filename
mpl-animator plot.py --var t --range "0,1" --out my_animation.gif

This generates a <script>_animated.py file. Run it to produce the output:

python wave_static_animated.py              # parallel (default)
python wave_static_animated.py --sequential # single-threaded fallback

Examples

Example 1 - Wave & spectrum (2D)

wave_static.py plots a signal and its frequency spectrum for a fixed frequency f:

# wave_static.py  (key lines)
f   = 10.0                          # <- variable to animate
t   = np.linspace(0, 1, 1000)
y   = np.sin(2*np.pi*f*t) + 0.4*np.sin(2*np.pi*2*f*t)

freqs    = np.fft.rfftfreq(len(t), d=t[1]-t[0])
spectrum = np.abs(np.fft.rfft(y))

fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(9, 6))
ax1.plot(t, y, 'royalblue', lw=1.5)
ax2.plot(freqs, spectrum, 'tomato', lw=1.5)
plt.show()

Animate f from 3 Hz to 60 Hz:

python mpl_animator.py examples/wave_static.py --var f --range "3,60" --frames 60 --fps 20
python wave_static_animated.py

The animator detects that y, spectrum depend on f, moves them into the per-frame update(), and keeps the figure/axes creation static - so only the data redraws each frame.

wave animation


Example 2 - 3D Lissajous curve

lissajous_3d_static.py draws a 3D Lissajous figure for fixed frequency ratio a:

# lissajous_3d_static.py  (key lines)
a  = 3.0          # <- variable to animate
b  = 2.0
c  = 1.0

t = np.linspace(0, 2 * np.pi, 1000)
x = np.sin(a * t + delta)
y = np.sin(b * t)
z = np.sin(c * t)

fig = plt.figure(figsize=(8, 6))
ax  = fig.add_subplot(111, projection='3d')
ax.scatter(x, y, z, c=colors, s=2, alpha=0.8)
ax.set_title(f"3D Lissajous  a={a:.1f}, b={b:.1f}, c={c:.1f}")
plt.show()

Animate a from 1 to 6, sweeping through different curve topologies:

python mpl_animator.py examples/lissajous_3d_static.py --var a --range "1,6" --frames 80 --fps 20
python lissajous_3d_static_animated.py

For 3D plots the animator calls fig.clear() and recreates the axes each frame (required to preserve the projection='3d' state), then re-runs all drawing commands with the new value of a.

lissajous animation


How it works

  1. Parses your script's AST to find which variables depend on the animated one
  2. Splits code into static (run once) and dynamic (recalculated per frame)
  3. Generates a new script with FuncAnimation (sequential 2D GIF), PNG+stitch (sequential MP4 or 3D), and multiprocessing (parallel) renderers

Library usage

from mpl_animator import animate

src = open("my_plot.py").read()
animated_code = animate(src, var="t", range_str="0,6.28", frames=60, fps=25)
open("my_plot_animated.py", "w").write(animated_code)

# Export as MP4
animated_code = animate(src, var="t", range_str="0,6.28", fmt="mp4")

Supported plot types

2D (plot, scatter, bar, hist, contour, imshow, ...), 3D (plot_surface, scatter3D, ...), polar, and anything else matplotlib draws.

Tests

pytest tests/ -v              # fast tests
pytest tests/ -v -m slow      # slow tests that generate actual GIFs/MP4s

Author: Basem Rajjoub

Built with the assistance of Claude Code

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

mpl_animator-0.1.3.tar.gz (17.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

mpl_animator-0.1.3-py3-none-any.whl (13.4 kB view details)

Uploaded Python 3

File details

Details for the file mpl_animator-0.1.3.tar.gz.

File metadata

  • Download URL: mpl_animator-0.1.3.tar.gz
  • Upload date:
  • Size: 17.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.9

File hashes

Hashes for mpl_animator-0.1.3.tar.gz
Algorithm Hash digest
SHA256 119e5d03c3b254f699236df9e560c373cdaae25c5ecf645eb35c6d4cce5b9fce
MD5 9755d90d74a964925577dc5bd5a354e1
BLAKE2b-256 4fd00bfd36391d47dffe56c5f9a4878616bf85930fe2d58f52caaa5a2d2db633

See more details on using hashes here.

File details

Details for the file mpl_animator-0.1.3-py3-none-any.whl.

File metadata

  • Download URL: mpl_animator-0.1.3-py3-none-any.whl
  • Upload date:
  • Size: 13.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.9

File hashes

Hashes for mpl_animator-0.1.3-py3-none-any.whl
Algorithm Hash digest
SHA256 5f52a69acfb9dd07aea5e7b80286d197a6ae16fa6f7e0e70e3974fce5a8ebbcd
MD5 69613d1753209cb5e84faae96e278661
BLAKE2b-256 33173b142ac23260db1d8112f7a7cdc0a8a81d46471622f54a6a282ebaf613e9

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page