Speeding Up Live Plots in Matplotlib with Object‑Oriented API and Blitting
When you need to stream thousands of data points per second, a naive Matplotlib update stalls. This post shows how to use the object‑oriented API and blitting to cut redraw time by an order of magnitude, with a full example, trade‑offs, and next steps.
29 Jan 2026, 17:23 UTC

Why the Classic Update Stalls
Most Python developers start a live plot by creating a Figure and Axes, then looping over plt.plot() or ax.plot() each frame. Each call rebuilds the entire canvas: the axes, grid, tick labels, and every artist. When you push thousands of points per second, that full redraw becomes the bottleneck, dropping frame rates below 5 fps.
In many engineering scenarios—sensor monitoring, simulation dashboards, or real‑time signal analysis—you need smooth motion, usually 20–30 fps. The key to achieving that is to avoid redrawing everything. Matplotlib’s object‑oriented API lets you keep the same artist objects alive, and the blitting technique lets you redraw only the changed portion of the canvas.
Section 1 – What Blitting Actually Does
Blitting is short for “block transfer.” The idea is simple: you capture the static background once, then overlay only the dynamic artists on top of that cached image each frame. Matplotlib implements this by storing a CanvasAgg snapshot of the background in the _background attribute of a FigureCanvasBase instance.
- Full redraw: every artist is re‑rendered and the entire canvas is flushed to the screen.
- Blit redraw: the background is restored, only the moved or updated artists are drawn, and the result is composited back onto the screen.
Because the background is static, the expensive part of the rendering pipeline—layout, text rendering, and rasterization—runs only once. Subsequent frames skip those steps, yielding a dramatic speedup.
Section 2 – Setting Up the Canvas
To use blitting you must keep the same Figure, Axes, and Line2D objects alive during the loop. Here’s a minimal skeleton that works in a standalone script using the TkAgg backend (the most widely supported interactive backend).
import matplotlib
matplotlib.use('TkAgg') # Ensure a blitting‑friendly backend
import matplotlib.pyplot as plt
import numpy as np
# Create the figure and axes once
fig, ax = plt.subplots()
line, = ax.plot([], [], lw=2) # Empty line; the comma unpacks the tuple
ax.set_xlim(0, 2*np.pi)
ax.set_ylim(-1.5, 1.5)
# This is the key: store the canvas for later
canvas = fig.canvas
# Prepare the background (captured after the first draw)
canvas.draw() # Draw the static parts
background = canvas.copy_from_bbox(ax.bbox)
# Now we can start updating the line data in a loop
Notice that we call draw() once to render the static elements (axes, grid, labels) and capture the background. The line object is created once and will be updated each frame.
Section 3 – The Blitting Workflow
Inside the update loop you perform the following steps for each frame:
- Restore the background:
canvas.restore_region(background) - Update the artist’s data:
line.set_data(x, y) - Redraw the artist:
ax.draw_artist(line) - Composite the updated region:
canvas.blit(ax.bbox)
Below is a concrete example that animates a sine wave, updating every 50 ms. The code is written as a simple while loop; in practice you could hook this into a GUI event loop or matplotlib.animation.FuncAnimation for more control.
import time
import numpy as np
# Parameters
freq = 1.0 # Hz
sample_rate = 1000 # samples per second
interval = 0.05 # seconds between frames (20 fps)
def get_sine(t):
x = np.linspace(0, 2*np.pi, sample_rate)
y = np.sin(freq * t + x)
return x, y
# Main loop
start = time.time()
while True:
t = time.time() - start
x, y = get_sine(t)
# 1. Restore background
canvas.restore_region(background)
# 2. Update line data
line.set_data(x, y)
# 3. Redraw line
ax.draw_artist(line)
# 4. Composite
canvas.blit(ax.bbox)
# Simple pacing: sleep until next frame
time.sleep(interval)
Running this script should produce a smoothly moving sine wave at around 20 fps on a typical laptop. If you remove the blitting steps (i.e., call plt.draw() each frame) you’ll notice a sharp drop in frame rate—often below 5 fps for the same data volume.
Trade‑Offs and Limitations
- Backend support: Blitting only works reliably with interactive backends such as TkAgg, Qt5Agg, or Qt5Cairo. The default Agg backend used in scripts or Jupyter notebooks ignores the
blitflag. - Artist complexity: Blitting is most effective with simple artists like
Line2DorPolyCollection. Complex collections (e.g., scatter plots with thousands of markers) may still trigger full redraws if their state changes. - Dynamic layout changes: If you resize the figure or modify the axes layout during the loop, you must recapture the background. Otherwise artifacts appear.
- Jupyter notebooks: Interactive backends are not fully supported in notebook cells. Use a standalone script or a GUI event loop for best results.
Actionable Next Steps
- Profile your loop: Use
%timeitorcProfileto measure the time spent indraw_artistversusblit. A typical gain is 5–10× faster updates. - Verify the backend: Add
print(matplotlib.get_backend())before the loop to confirm you’re on TkAgg or Qt5Agg. - Experiment with
FuncAnimation: Matplotlib’s animation module can automatically handle blitting if you passblit=Trueand return the updated artists. - Consider alternative libraries: For extremely high‑frequency data (>10 kHz) or 3‑D plots, look into libraries like
pyqtgraphorbokeh, which are designed for real‑time rendering. - Test on target hardware: Performance can vary across CPUs and GPUs. Run the example on the same machine that will host the production dashboard to get realistic numbers.
By keeping the canvas and artists alive, capturing the background once, and restoring it each frame, you can transform a sluggish live plot into a responsive, real‑time visual. The trade‑offs are manageable: stick to simple line or scatter artists, avoid dynamic layout changes, and use a supported interactive backend. When those conditions hold, blitting delivers the performance boost you need for engineering tasks that demand real‑time insight.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.