Embedding Matplotlib Animations in IPython Notebooks

This notebook first appeared as a blog post on Pythonic Perambulations.

License: BSD (C) 2013, Jake Vanderplas. Feel free to use, distribute, and modify with the above attribution.

I've spent a lot of time on this blog working with matplotlib animations (see the basic tutorial here, as well as my examples of animating a quantum system, an optical illusion, the Lorenz system in 3D, and recreating Super Mario). Up until now, I've not have not combined the animations with IPython notebooks. The problem is that so far the integration of IPython with matplotlib is entirely static, while animations are by their nature dynamic. There are some efforts in the IPython and matplotlib development communities to remedy this, but it's still not an ideal setup.

I had an idea the other day about how one might get around this limitation in the case of animations. By creating a function which saves an animation and embeds the binary data into an HTML string, you can fairly easily create automatically-embedded animations within a notebook.

The Animation Display Function

As usual, we'll start by enabling the pylab inline mode to make the notebook play well with matplotlib.

In [1]:
%pylab inline
Welcome to pylab, a matplotlib-based Python environment [backend: module://IPython.kernel.zmq.pylab.backend_inline].
For more information, type 'help(pylab)'.

Now we'll create a function that will save an animation and embed it in an html string. Note that this will require ffmpeg or mencoder to be installed on your system. For reasons entirely beyond my limited understanding of video encoding details, this also requires using the libx264 encoding for the resulting mp4 to be properly embedded into HTML5.

In [2]:
from tempfile import NamedTemporaryFile

VIDEO_TAG = """<video controls>
 <source src="data:video/x-m4v;base64,{0}" type="video/mp4">
 Your browser does not support the video tag.

def anim_to_html(anim):
    if not hasattr(anim, '_encoded_video'):
        with NamedTemporaryFile(suffix='.mp4') as f:
            anim.save(f.name, fps=20, extra_args=['-vcodec', 'libx264'])
            video = open(f.name, "rb").read()
        anim._encoded_video = video.encode("base64")
    return VIDEO_TAG.format(anim._encoded_video)

With this HTML function in place, we can use IPython's HTML display tools to create a function which will show the video inline:

In [3]:
from IPython.display import HTML

def display_animation(anim):
    return HTML(anim_to_html(anim))

Example of Embedding an Animation

The result looks something like this -- we'll use a basic animation example taken from my earlier Matplotlib Animation Tutorial post:

In [4]:
from matplotlib import animation

# First set up the figure, the axis, and the plot element we want to animate
fig = plt.figure()
ax = plt.axes(xlim=(0, 2), ylim=(-2, 2))
line, = ax.plot([], [], lw=2)

# initialization function: plot the background of each frame
def init():
    line.set_data([], [])
    return line,

# animation function.  This is called sequentially
def animate(i):
    x = np.linspace(0, 2, 1000)
    y = np.sin(2 * np.pi * (x - 0.01 * i))
    line.set_data(x, y)
    return line,

# call the animator.  blit=True means only re-draw the parts that have changed.
anim = animation.FuncAnimation(fig, animate, init_func=init,
                               frames=100, interval=20, blit=True)

# call our new function to display the animation

Making the Embedding Automatic

We can go a step further and use IPython's display hooks to automatically represent animation objects with the correct HTML. We'll simply set the _repr_html_ member of the animation base class to our HTML converter function:

In [5]:
animation.Animation._repr_html_ = anim_to_html

Now simply creating an animation will lead to it being automatically embedded in the notebook, without any further function calls:

In [6]:
animation.FuncAnimation(fig, animate, init_func=init,
                        frames=100, interval=20, blit=True)

So simple! I hope you'll find this little hack useful!

This post was created entirely in IPython notebook. Download the raw notebook here, or see a static view on nbviewer.