Creating animations with Python’s Matplotlib is quick and easy once you know how to do it. However, when learning I found the tutorials and examples online either daunting, overly sophisticated, or lacking explanation. In many cases all I need is a quick-and-dirty script that works, rather than longer code that adheres to best practices.
See here for a follow up post with more elaborate animation examples.
Why animate?
Exploring datasets is a big part of what many scientists do these days. In many cases these datasets will have more than two dimensions; for example, temperature or salinity in an ocean circulation model has four dimensions: x, y, z, t. It’s futile to try and display these in a single plot. That’s where animation can help.
An animated line in six steps
This example walks through how to create the animation below in six steps. However, the first four steps will involve nothing new to anyone who has made a plot using Matplotlib.
Each step contains a few lines of code that you can copy and paste, but a script with all the code for all examples can be found here.

Step one: import the necessary modules
import numpy as np import matplotlib.pyplot as plt from matplotlib.animation import FuncAnimation # plt.style.use('ggplot')
The first two lines will be familiar to anyone who has used Python for science, and the third line is obviously specific to animation. I’ve left the final line commented as it isn’t necessary and will not work if your matplotlib version is <1.5.
Step two: set up the plotting area
fig, ax = plt.subplots(figsize=(5, 3)) ax.set(xlim=(-3, 3), ylim=(-1, 1))
The first line sets up the figure and its axis, and the second line fixes the axis limits. Setting the limits in advance stops any rescaling of the limits that may make the animation jumpy and unusable.
Step three: create some data to plot
x = np.linspace(-3, 3, 91) t = np.linspace(1, 25, 30) X2, T2 = np.meshgrid(x, t) sinT2 = np.sin(2*np.pi*T2/T2.max()) F = 0.9*sinT2*np.sinc(X2*(1 + sinT2))
F is a 2D array comprising some arbitrary data to be animated. Well, it’s not actually arbitrary, it’s set up to produce a perfectly looping gif.
Step four: plot the first line
line = ax.plot(x, F[0, :], color='k', lw=2)[0]
This sets up a line object with the desired attributes, which in this case are that it’s coloured black and has a line weight of 2. Note the [0] at the end. This is necessary because the plot command returns a list of line objects. Here, we are only plotting a single line, so we simply want the first (i.e., zeroth) object in the list of lines.
Be sure to use ax.plot(…), not plt.plot(…)
Step five: create a function to update the line
def animate(i): line.set_ydata(F[i, :])
This function needs one argument. The only command in the function is to change the object’s y data. See later for other things that can be changed.
Step six: call FuncAnimation and show
anim = FuncAnimation( fig, animate, interval=100, frames=len(t)-1) plt.draw() plt.show()
FuncAnimation requires two arguments: the figure object ‘fig’ and animation function ‘animate’. The interval argument is optional and sets the interval between frames in milliseconds. The frames argument is needed only if the animation is to be exported. The final two lines may or may not be necessary depending on whether you’re working interactively (e.g. with IPython).
Be sure to assign FuncAnimation to a variable (here it’s anim).
Optional step: export the animation
Exporting the animation should be as simple as
anim.save('filename.mp4')
or, say
anim.save('filename.gif', writer='imagemagick')
However, depending on your computer and file type, some configuration may be required to ensure the export works correctly.
Beyond a line plot
Animating any other type of plot is as simple as the example above. Typically, the only differences involve getting the equivalent of the line object, and changing the animate function. Here are examples of steps four and five for the most common types of plots.
We’ll be needing a three dimensional array G for some of these examples.
x = np.linspace(-3, 3, 91) t = np.linspace(0, 25, 30) y = np.linspace(-3, 3, 91) X3, Y3, T3 = np.meshgrid(x, y, t) sinT3 = np.sin(2*np.pi*T3 / T3.max(axis=2)[..., np.newaxis]) G = (X3**2 + Y3**2)*sinT3
Pcolor
cax = ax.pcolormesh(x, y, G[:-1, :-1, 0], vmin=-1, vmax=1, cmap='Blues') fig.colorbar(cax) def animate(i): cax.set_array(G[:-1, :-1, i].flatten())
Note that the length of G in each dimension must be one shorter than x and y, and that a flattened array must be passed to set_array.
Scatter
ax.set(xlim=(-3, 3), ylim=(-1, 1)) scat = ax.scatter(x[::3], F[0, ::3]) def animate(i): y_i = F[i, ::3] scat.set_offsets(np.c_[x[::3], y_i])
Note the set_offsets must be passed an N×2 array. Here we use np.c_[] for this purpose. The use of [::3] reduces the density of scatter points.
Quiver
ax.set(xlim=(-4, 4), ylim=(-4, 4)) # Plot every 20th arrow step = 20 x_q, y_q = x[::step], y[::step] # Create U and V vectors to plot U = G[::step, ::step, :] V = np.roll(U, shift=3, axis=2) qax = ax.quiver(x_q, y_q, U[..., 0], V[..., 0], scale=100) def animate(i): qax.set_UVC(U[..., i], V[..., i])
Contour
contour_opts = {'levels': np.linspace(-9, 9, 10), 'cmap':'RdBu', 'lw': 2} cax = ax.contour(x, y, G[..., 0], **contour_opts) def animate(i): ax.collections = [] ax.contour(x, y, G[..., i], **contour_opts)
Contour plots are a little different. We don’t use a set_… method, but instead simply redraw the contour plot for each frame. The first line of the function gets rid of existing contours before plotting new ones.
Using the contour_opts dict is a handy trick to avoid specifying the same keyword arguments in both calls to contour.
Changing labels and text
ax.set(xlim=(-1, 1), ylim=(-1, 1)) string_to_type = 'abcdefghijklmnopqrstuvwxyz0123' label = ax.text(0, 0, string_to_type[0], ha='center', va='center', fontsize=12) def animate(i): label.set_text(string_to_type[:i+1]) ax.set_ylabel('Time (s): ' + str(i/10)) ax.set_title('Frame ' + str(i))
Changing other properties
With the exception of contours, every animate function requires finding the appropriate set_… method. The following list comprehension will give you all the potential set methods for, say, a line object. (You have to create this first.)
[x for x in dir(line) if 'set_' in x]
This often gives you a long list of potential values to set, but usually a quick scan through the list is all that is needed to find the method you want.
Many thanks for this helpful post!
Hi, thanks for the nice tutorial. Is there a way to animate on the same plot a line (using plot) and a dot (using scatter) ?
Simply put both the line.set_data and scat.set_offsets methods in the animate function.
Hi there, thank you for such a nice explanation! It almost worked for me except for a few hurdles…
1. In your animation function, you didn’t return anything; did this cause trouble for you? In some of the tutorials, such as this one https://matplotlib.org/examples/animation/simple_anim.html , the animation returns something. If I didn’t return anything in the animation function, I get this error: ` The animation function must return a sequence of Artist objects.`
2. I was trying to animate changes of a matrix, as well as the title of the plot. I was using `ax.imshow( )`. However, the animation only changes the image of the matrix; the title remains the same. Below is a block of code in python3 that reproduces my problems:
import matplotlib
matplotlib.use(‘tkagg’)
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import numpy as np
fig, ax = plt.subplots(figsize=(5, 5))
im = ax.imshow(np.zeros((5,5)), aspect=’auto’, cmap=’Spectral’, animated=True)
plt.yticks(np.arange(5), (‘a’, ‘b’, ‘c’, ‘d’, ‘e’))
im.set_clim(vmin=0, vmax=15)
plt.colorbar(im)
def init():
im.set_array(np.ma.array(np.ones((5,5)), mask=True))
return im,
def update(i):
im.set_array(np.ones((5,5)) + i + 1)
ax.set_title(‘title is: ‘ + str(i))
# print(i)
return im,
ani = animation.FuncAnimation(fig, update, frames=range(10), init_func=init, interval=1000, blit=True)
# set_trace()
plt.draw()
plt.show()
Don’t use the ‘init_func’ or ‘blit’
You only receive an error about not returning a sequence of artist objects if you include an ‘init_func’ as used in the example you link to. I’ve never seen the need to use an ‘init_func’. It may be the proper way to do things, but I’m doing them ‘the easy way’.
In summary, your example works for me if the ani = … line instead reads
ani = animation.FuncAnimation(fig, update, frames=range(10), interval=1000)
Wow, works like charm and saved my day! Thanks so much!!!!!
Thanks a lot for your answer!
Wow. I haven’t seen such a simple explanation ever. I just wish to know the logic behind:
sinT2 = np.sin(2*np.pi*T2/T2.max())
F = 0.9*sinT2*np.sinc(X2*(1 + sinT2))
Could you please explain …
F is created so that I have some ‘data’ to animate in the examples. The use of sin and sinc functions simply makes attractive curves and plots
Please help me with my animation. I have followed all the steps yet its not working.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
%matplotlib notebook
Qy = np.load(“Qy.npy”) #2 D Array
def HydrographAnimation(a):
fig, ax = plt.subplots(figsize=(5,3))
graph, = ax.plot(a[0,:])
def animate(i):
graph.set_ydata(a[i,:])
anim = FuncAnimation(fig,animate,interval = 10) #interval = 200,repeat = True
HydrographAnimation(Qy)
It’s not working because certain variables are described within individual functions, not the overall workspace. Although it is good practice to use functions, you have to take care to get outputs from functions where necessary. Here’s how I would fix it.
Qy = np.load(‘/home/hugke729/Downloads/Qy.npy’) #2 D Array
fig, ax = plt.subplots(figsize=(5,3))
def HydrographAnimation(Qy):
graph, = ax.plot(Qy[0,:])
return graph
def animate(i):
graph.set_ydata(Qy[i,:])
anim = FuncAnimation(fig,animate,interval = 10, repeat=True, frames=192)
graph = HydrographAnimation(Qy)
https://1drv.ms/u/s!Anpzx-FMfFQmwW2h2geto9uYZIJQ
the 2d Array(Qy) can be accessed via above link
Hi I’m trying to animate two points moving on a unit circle for some work I’m doing. I am able to animate the points but the points from the previous steps are still present as it evolves hence it is a smudge of points being animated rather than two points moving about the circle. Please advise how I could remedy this? Thanks for your time.
I recommend using the ax.plot with a marker (no line) and then using the set_data method:
t = np.linspace(0, 2*np.pi, 100)
x = np.sin(t)
y = np.cos(t)
fig, ax = plt.subplots()
ax.set(xlim=[-1, 1], ylim=[-1, 1])
dot, = ax.plot(x[0], y[0], ‘ro’)
def animate(i):
dot.set_data(x[i], y[i])
anim = FuncAnimation(fig, animate, 100)
Thanks for your response. I am still having some difficulty with this animation problem. I have posted a working example of my code here (https://stackoverflow.com/questions/56042262/animation-using-matplotlib-query). Have a look if you have a chance.
Hi. Im triying to animate a quiver, but i need to also update the x and y vectors. Is there a way to do it? Im triying to represent moving particles, so the position needs to change as does the arrow indicating velocity
You’ll need to use the set_offsets method for qax, which wants the new positions as an N × 2 array. Something like qax.set_offsets(np.c_[new_X.flatten(), new_Y.flatten()])
Many thanks for the helpful post.
I got the code working for contourf for a 3d array.
but i need a common colorbar which shows the min and max for the entire 3d array
here is the code can you help ? when I put a colorbar i see only a single frame, the first one
w_t = np.loadtxt(‘w_t.dat’)
w_t = np.reshape(w_t, (t, nx,nx))
print w_t.shape
fig, ax = plt.subplots()
contour_opts = {}
cax = ax.contourf(w_t[0,:,:], **contour_opts)
#cbar = ax.colobar(cax)
def animate(i):
ax.collections = []
ax.contourf(w_t[i,:,:], **contour_opts)
ax.set_title(‘time=’+str(i))
ax.colorbar(i)
anim = animation.FuncAnimation(fig, animate)#, interval=100, frames=len(t)-1)
plt.draw()
plt.show()
You only see the first frame because you have a couple of errors. First, at least in your code above, you’ve misspelt colorbar (missing the r). Second, it should be fig.colorbar, not ax.colorbar
Thanks a lot !
fig.colorbar did it.
Thank you very much for this comment, it helped me a lot!
hi, i’m trying to animate a contourf plot in polar system, is it possible? i tried many way without results.. Do you have any indication ?
Thank you
That moving scatter plot is just mmmmhhh… Hits the right spot.
does anyone have any ideas on how I could do something like what is depicted at 0:19 of this video? https://www.youtube.com/watch?v=rB83DpBJQsE&t=312s
I want to plot some vector field (it can be a static one) and view an animation of the path a particle would take if it was within in that vector field just like what is shown in the video above.
What you want to do is (1) create a 3D array with positions in terms of (x, y, t), then (2) initialise a list containing lots of line objects for, say, t = 0-5 (a time range gives the particles a length effectively). Easiest way to set up this list would be a loop. Then in the animate function, use another loop to iterate over all the lines to update the data with line.set_data(…). The first call to animate would update to t = 1-6, then 2-7.