#!/usr/bin/env python # coding: utf-8 # # A Javascript Viewer for Matplotlib Animations # *This notebook was originally a post by Jake Vanderplas on* # [*Pythonic Perambulations*](http://jakevdp.github.io) # *The code here is BSD licensed: see* # [*http://github.com/jakevdp/JSAnimation*](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[1]: get_ipython().run_line_magic('pylab', 'inline') # In[2]: # get the JSAnimation import at https://github.com/jakevdp/JSAnimation from JSAnimation import examples examples.basic_animation() # 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](http://escience.washington.edu/). 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](http://jakevdp.github.io/blog/2013/05/12/embedding-matplotlib-animations/) # to embed mp4 movies in IPython, which works quite well: # Michael Kuhlen of Berkeley ran with the idea and made # [this](http://nbviewer.ipython.org/urls/bitbucket.org/mqk/public-ipython-notebooks/raw/e75599b4fa636f9964388142fff8af012f418917/VL2_subhalo_orbits.ipynb) # 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](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](http://jakevdp.github.io/blog/2013/05/12/embedding-matplotlib-animations/). # 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] # 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(''.format(data)) # 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](http://jakevdp.github.io/blog/2012/08/18/matplotlib-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) # ## Remaining Issues # One of my goals in this was to make it relatively lightweight: # For this reason, it doesn't depend on JQuery, JQuery-UI, and other # nice packages that might be suited for this type of application. # # For that reason, the frame slider uses the HTML5 slider element, which # is [not yet supported](http://caniuse.com/input-range) by all browsers. # In particular, if you're using older versions of IE or Firefox, # the frame dragger will appear as # an ugly numerical input box. Making this compatible with non-HTML5-compliant # browsers would be possible, but would require a lot more javascript hacking. # # Second, this does not scale well to large animations. Because each frame is # individually embedded in the document, the size of the notebook can become # very large very quickly. Typical web-ready video formats involve a lot of # image compression. This tends to be easy for video: most frames look very similar # to the last, with just a few changes. Implementing this sort of compression # in Javascript would certainly be possible, but is well beyond my minimal Javascript # hacking abilities. It would be very cool if someone could run with this idea # and create what would amount to a Javascript video codec to reduce the size of # these embedded animations. I think it could be done with some effort. # # As it is, though, I think this is a pretty neat widget and will definitely find good uses. # I hope you've found this useful, and thanks for reading! This was a fun one. # # *This post was written in the IPython notebook: The notebook can be downloaded* # [*here*](http://jakevdp.github.io/downloads/notebooks/JSAnimation.ipynb), # *or viewed statically* # [*here*](http://nbviewer.ipython.org/url/jakevdp.github.io/downloads/notebooks/JSAnimation.ipynb)