A Javascript Viewer for Matplotlib Animations

This notebook was originally a post by Jake Vanderplas on Pythonic Perambulations The code here is BSD licensed: see http://github.com/jakevdp/JSAnimation.

I'll cut to the chase: here's what I've created: a javascript-based animation viewer, with hooks to embed it in IPython. It's best viewed in a modern browser (unfortunately Firefox does not currently qualify as "modern" due to its lack of HTML5 support)

In [2]:
%pylab inline
Populating the interactive namespace from numpy and matplotlib

In [3]:
# get the JSAnimation import at https://github.com/jakevdp/JSAnimation
from JSAnimation import examples
examples.basic_animation()
---------------------------------------------------------------------------
ImportError                               Traceback (most recent call last)
<ipython-input-3-1d78e60f94c1> in <module>()
      1 # get the JSAnimation import at https://github.com/jakevdp/JSAnimation
----> 2 from JSAnimation import examples
      3 examples.basic_animation()

ImportError: No module named JSAnimation

I think the result is pretty good, if I do say so myself.

The Background

Last week, Fernando Perez visited UW to give a talk for the eScience institute. Over lunch we were discussing the possibility of building a Javascript-based animation viewer which could be embedded in IPython notebooks. I had written a short hack to embed mp4 movies in IPython, which works quite well: Michael Kuhlen of Berkeley ran with the idea and made this notebook, which embeds a 3D rendering of orbits within an N-body simulation.

The problem with this mp4 approach is that it requires installation of ffmpeg or mencoder with the proper video codec libraries. What we wanted was something that only requires Python and a web browser: something that could use Javascript to display frames rendered by Matplotlib.

Above you see the result of a week's worth of evenings hacking on Python, html, and Javascript -- my first real foray into the latter. The result is a small python package, available on my github page: https://github.com/jakevdp/JSAnimation. See the README and examples on that page for details of how this can be used.

So what's going on here?

You can dig into the code to see how it works, but here's the short version:

The package adds an IPython representation hook to the animation object, similar to the one I showed here. When the animation is displayed, IPython calls the new HTMLWriter to convert the animation to an embeddable html document. This writer is capable of saving any animation to a stand-alone HTML file, with the frames either embedded or in a separate directory: this stand-alone file is created, read-in, and embedded into the document as raw HTML.

For IPython, the animation creates frames that are embedded directly in the HTML source via the base-64 representation. A base-64 representation is a standard way of encoding binary data to a normal string of text, which looks like this:

In [3]:
fig, ax = plt.subplots()
ax.plot(random.rand(100))
# write the figure to a temporary file, and encode the results to base64
import tempfile
with tempfile.NamedTemporaryFile(suffix='.png') as f:
    fig.savefig(f.name)
    data = open(f.name, 'rb').read().encode('base64')
    
# close the figure and display the data
plt.close(fig)
print data[:460]
iVBORw0KGgoAAAANSUhEUgAAAbAAAAEgCAYAAADVKCZpAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz
AAALEgAACxIB0t1+/AAAIABJREFUeJztnXt0XVW977877zZN2iZ9QJNAgYQ+KLRotaCi4VFLKxQF
PNRzREdFrGgH6pFzz7l6zj3gHVaq5w4Pw3od9QEHAQvq8VpQCMgjqC1thVYKtEJ5tKTpO02aNGne
6/7xy+xee+251l6POddj799njIw2yc7ea6+91vrO7/f3m3OlDMMwwDAMwzAJoyjqDWAYhmEYP7CA
MQzDMImEBYxhGIZJJCxgDMMwTCJhAWMYhmESCQsYwzAMk0hYwBiGYZhEwgLGMAzDJBIWMIZhGCaR
sIAxDMMwiYQFjGEYhkkkLGAMwzBMImEBYxiGYRIJCxjDMAyTSFjAGIZhmETCAsYwDMMkEhYwhmE

We only print the first few sections of the data, as it is a rather large string. The magic of this is that contained in that string is all the information needed to reconstruct the original PNG image. We can see that directly by inserting the string into an HTML image tag, which results in a frame embedded in the document itself:

In [4]:
from IPython.display import HTML
HTML('<img src="data:image/png;base64,{0}">'.format(data))
Out[4]:

This sort of thing is similar to what goes on in the background every time you use embedded figures in an IPython notebook.

By embedding all the frames this way, we're able to use Javascript to switch between them at a given frame-rate using the javascript setInterval() function. The rest is just straightforward javascript event handling.

Embedding Your Own Animation

If you'd like to use this to create your own animation, you can follow the suggestions in the animation tutorial. To embed your animation in the notebook, import IPython_display from the JSAnimation package. This will add the _repr_html_ method to the animation class, so that creating the animation will lead to it being displayed via the Javascript embedding.

Here is an example showing how the above animation was created:

In [5]:
from matplotlib import animation
from JSAnimation import IPython_display

fig = plt.figure()
ax = plt.axes(xlim=(0, 10), ylim=(-2, 2))
line, = ax.plot([], [], lw=2)

def init():
    line.set_data([], [])
    return line,

def animate(i):
    x = np.linspace(0, 10, 1000)
    y = np.cos(i * 0.02 * np.pi) * np.sin(x - i * 0.02 * np.pi)
    line.set_data(x, y)
    return line,

animation.FuncAnimation(fig, animate, init_func=init,
                        frames=100, interval=30)
Out[5]:


Once Loop Reflect