Python Charts

Python plotting and visualization demystified

How to Animate Charts and Save as GIF/MP4 in Matplotlib

A practical Matplotlib workflow for creating animations and exporting them as GIF or MP4 files.

Animations are a great way to show change over time without forcing readers to mentally connect a long series of still plots. In Matplotlib, the workflow is straightforward once you think in terms of a function that updates the chart one frame at a time.

The most common pattern is to use FuncAnimation, then save that animation as a GIF or MP4 file. This is useful for dashboards, presentations, and blog posts where a short loop adds a lot of clarity.

Quick Example

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation, PillowWriter

x = np.linspace(0, 2 * np.pi, 200)

fig, ax = plt.subplots(figsize=(8, 5))
line, = ax.plot(x, np.sin(x), lw=2, color='steelblue')
ax.set_xlim(0, 2 * np.pi)
ax.set_ylim(-1.2, 1.2)
ax.set_xlabel('x')
ax.set_ylabel('sin(x)')
ax.set_title('Animated sine wave')


def update(frame):
    line.set_ydata(np.sin(x + frame / 10))
    return line,


ani = FuncAnimation(fig, update, frames=90, interval=30, blit=True)
ani.save('animated-sine.gif', writer=PillowWriter(fps=30))

Matplotlib animated sine wave preview

This creates a compact animation loop where the phase of the sine wave changes frame by frame. That is the core pattern behind most Matplotlib animations.

The basic animation pattern

The key idea is to define a function that updates the plotted objects on every frame. In practice, that usually means updating a line, scatter points, bars, or a marker position.

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation

x = np.linspace(0, 2 * np.pi, 200)
fig, ax = plt.subplots()
line, = ax.plot(x, np.sin(x))


def update(frame):
    line.set_ydata(np.sin(x + frame / 20))
    return line,


ani = FuncAnimation(fig, update, frames=100, interval=20, blit=True)

A few details matter here:

  • frames controls how many times the update function runs.
  • interval sets the delay in milliseconds between frames.
  • blit=True can improve animation performance when your plot is not too complex.

Save as a GIF

To save an animation as a GIF, use PillowWriter. This is usually the easiest route if you want a lightweight, browser-friendly output.

from matplotlib.animation import PillowWriter

ani.save('animated-sine.gif', writer=PillowWriter(fps=30))

This only requires the Pillow dependency that Matplotlib already uses for image output. It is a good default choice when you want a quick animated file for a blog or a presentation.

GIF example:

Animated sine wave saved as a GIF in Matplotlib

Save as an MP4

If you want a video file instead, save the animation with the FFmpeg writer. This requires a working ffmpeg installation on your machine.

ani.save('animated-sine.mp4', writer='ffmpeg', fps=30)

If Matplotlib cannot find ffmpeg, you will usually get a clear error telling you the writer is missing. On many systems, installing ffmpeg through a package manager solves that quickly.

MP4 example:

A slightly more realistic example

A common use case is animating a line that moves across time. Here is a small example with a moving dot and a line trace.

import matplotlib.pyplot as plt
import numpy as np
from matplotlib.animation import FuncAnimation

n = 200
x = np.linspace(0, 10, n)
y = np.sin(x)

fig, ax = plt.subplots(figsize=(8, 5))
(line,) = ax.plot(x, y, color="royalblue", lw=2)
(point,) = ax.plot([], [], "o", color="tomato", markersize=8)

ax.set_xlim(0, 10)
ax.set_ylim(-1.5, 1.5)
ax.set_title("Animated line and point")


def update(frame):
    idx = frame
    # Calculate the updated line y-values for this frame
    current_y = np.sin(x + frame / 15)

    # Update line values
    line.set_ydata(current_y)

    # Point tracks the moving y-value at index `idx`
    point.set_data([x[idx]], [current_y[idx]])

    return line, point


# Create and hold the animation instance
ani = FuncAnimation(fig, update, frames=n, interval=20, blit=True)

# Save the video
ani.save("animated-sine-point.mp4", writer="ffmpeg", fps=30)

# Clean up the figure explicitly to free memory
plt.close(fig)

This is the same idea as the simpler example, but it makes the animation more visually intuitive because the point tracks the line as it changes.

Animated MP4 example:

Saving a static preview image

If this is going into a blog or documentation, it helps to include a still preview alongside the animation. You can save the frame at any point like this:

fig.savefig('matplotlib-animation.png', dpi=200, bbox_inches='tight')

That gives you a PNG version for the article thumbnail while the GIF or MP4 handles the motion.

Practical tips

  • Use fps=24 or fps=30 for smooth playback.
  • Keep the animation short. A few seconds is often enough.
  • Reduce the figure size when you are creating a GIF for the web.
  • For large animations, blit=True can reduce rendering cost.
  • If you need a video of a longer process, ffmpeg is usually the better output format.

Similar topics