Turn a matplotlib figure and a per-frame update function into one self-contained, looping, animated SVG.
Why svganim
- A real image. One SVG file that works in an
<img>tag and in GitHub READMEs. No JavaScript, no HTML page, no player. - Vector. Sharp at any size, unlike a GIF.
- Only what changes is animated. Axes, labels and static data are written once, so files stay small.
- Reproducible. Same code and matplotlib version, same bytes.
What you give up: playback controls (see Limitations). If you need
those, use HTMLWriter or to_jshtml.
Install
pip install svganim
Usage
import matplotlib.pyplot as plt
import numpy as np
from svganim import anim_to_svg
fig, ax = plt.subplots()
x = np.linspace(0, 2 * np.pi, 200)
(line,) = ax.plot(x, np.sin(x))
def update(i):
line.set_ydata(np.sin(x + i / 10))
anim_to_svg(fig, update, n_frames=60, fps=20, hold=1.0, path="wave.svg")
<img src="wave.svg" alt="A moving sine wave">
In Jupyter and Quarto, end a cell with the call and the animation is displayed,
also in the rendered HTML. Call plt.close(fig) first, or the notebook also
shows matplotlib's static figure.
update changes the artists (set_data, set_offsets, set_color, ...). A line
whose data grows is fine, so draw trails and curves with set_data on a single
line. Artists can also come and go: create them once and show or hide them with
set_visible.
anim_to_svg(fig, update, n_frames, fps=20, hold=1.0, path=None, *, precision=3, interpolate=False)
| Argument | Description |
|---|---|
fig |
The matplotlib figure to render. |
update |
Called as update(i) before frame i is rendered. |
n_frames |
Number of frames. |
fps |
Frames per second. |
hold |
Seconds to hold the last frame before looping. |
path |
If given, the SVG is also written to this file. |
precision |
Decimals kept in coordinates. Lower means smaller files. |
interpolate |
If True, shapes glide and colours fade between frames instead of switching. See the k-means example. |
Returns the SVG as a string, which displays itself as an animated image in Jupyter
and Quarto. Raises ValueError for an invalid argument or when an element changes
kind (see Limitations); the message names the element.
How it works
Each frame is rendered to SVG and the first one becomes the base document. Later frames are compared with it, and every attribute that changes gets a SMIL animation. Elements that never change are left untouched, and those that only some frames have, such as the letters of a text that changes length, are shown only in those.
Limitations
- An element cannot change kind, which matplotlib sometimes makes it do when a style
changes (a scatter plot whose face colour goes through
none, in recent versions: use a transparent colour instead). - There are no playback controls, only a loop.
- The animations are checked against Chromium. SMIL is supported by all current browsers, but other engines are not part of the tests yet.
Examples and docs
The documentation has a gallery of notebooks that explain each example, plus an API reference built from the docstrings. It also shows how animations display in Jupyter and Quarto.
Development
uv run pytest
# the browser tests render every frame in Chromium, and are skipped without it
uv run --group browser playwright install chromium-headless-shell
uv run --group browser pytest tests/test_browser.py
uv run sphinx-build -W docs docs/_build/html
uv run ruff check && uv run ruff format --check && uv run mypy src
License
MIT
Metadata
Release files for svganim 0.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| svganim-0.3.0.tar.gz | 12.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| svganim-0.3.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 28.1 kB
Release files / svganim-0.3.0.tar.gz
| Download URL | svganim-0.3.0.tar.gz |
|---|---|
| Size | 12.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
082718a94daeeb917b0ad7c672de76a05bceb7e660f7d2ea2cfc15638234f5f8
|
|
BLAKE2b-256 checksum How to use checksums |
645b01d6f006618530161940c5e4a5c8303ea9efba493ff1e974d6baf2d72f27
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 6, 2026.
Transparency logRelease files / svganim-0.3.0-py3-none-any.whl
| Download URL | svganim-0.3.0-py3-none-any.whl |
|---|---|
| Size | 15.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
a07869f9508b0aef5fe05fa50a0643019f2be17fa49ea419733e729e3122d118
|
|
BLAKE2b-256 checksum How to use checksums |
6dc4520e0c636389faebd4fd2e8f433f25ffde62f1faaac8fc5669413e64b166
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 6, 2026.
Transparency log