Animations Added to Web-Based Haskell/Gloss

The Feature

I’ve added animations to the web-based Gloss/Haskell environment at http://dac4.designacourse.com:8000/anim. You should export a symbol

animation :: Float -> Picture

and you’ll see it in modern web browsers. By “modern” we mean:

Firefox 6.0 or higher (yes, the one released just a couple days ago)

Chrome, Safari, or Opera, any recent version

The iOS browser should work (untested).

Internet Explorer and the built-in Android browser are known not to work. On both operating systems, Firefox 6 is available and does work.

The feature we need is called “server sent events”, an HTML 5 extension that makes “comet” style web applications work without a whole tool shed of browser-specific kludges. So, if you are curious about another browser, see if it implements server-sent events. Or just try it.

Edit: As of Sunday, you can now do “simulations” as well. The URL for simulations is http://dac4.designacourse.com:8000/sim. These are a little more involved. You need to define three symbols sharing a common data type of your choosing:

initial :: StdGen -> a

step :: ViewPort -> Float -> a -> a

draw :: a -> Picture

Here, initial returns the starting value of your chosen data type. (Its parameter is a pseudo-random number generator, in case you want random numbers.) The step function is responsible for “advancing” the simulation by some amount of time. Finally, the draw function is reponsible for converting the current simulation to a picture to draw on the screen. The difference between simulations and animations is that animations calculate the picture from scratch each time: you give it a time since the start, and get back a picture. Simulations, on the other hand, play out incrementally: each new state depends only on the most recent state and the amount of time that’s passed since then. You can always turn an animation into a simulation by defining initial = 0 and step = const (+) where the type “a” is Float. But not all simulations can be efficiently written as animations.

Under the Hood

The previous version of the application (which still works just fine) displayed only static pictures, and did so by loading some JSON directly into the document and then having JavaScript draw it on a canvas. This new animation mode is a little different. The steps are:

You submit your function. The server compiles your code (using SafeHaskell, of course), and stores your function along with the start time in a lookup table. The animation page loads, and has a key to that lookup table. The animation page connects back to the server using the JavaScript EventSource object (that’s the server-sent events feature). The server then streams newly updated pictures as JSON along that connection.

Incidentally, if you’re thinking of implementing a “comet”-style web application with Snap, you might be interested in the code at https://github.com/cdsmith/gloss-web/blob/master/src/EventStream.hs which implements the server side of the event stream format. It’s a generic module that you can transplant into your own code. In the future, I’ll look at the best way to get it packaged either as part of a snap package or as a stand-alone library. As short as it is, though, I hope to avoid adding that dependency weight.

Anyway, that’s animations for you. Have fun!

Examples

The following program draws the solar system, representing the distances of all the planets from the sun, their orbital periods, their relative sizes. The one thing that’s not accurate is the relationship between the sizes of the planets and their distance from the sun… something had to give, since a completely to-scale representation of the solar system would just look empty! Also, I didn’t bother representing the eccentricities of orbits.

import Graphics.Gloss animation t = pictures [ color black (rectangleSolid 500 500), sun, rotate (60 * t / 0.24) (translate 4 0 mercury), rotate (60 * t / 0.62) (translate 7 0 venus), rotate (60 * t / 1.00) (translate 9 0 earth), rotate (60 * t / 1.88) (translate 14 0 mars), rotate (60 * t / 11.86) (translate 48 0 jupiter), rotate (60 * t / 29.46) (translate 89 0 saturn), rotate (60 * t / 84.32) (translate 178 0 uranus), rotate (60 * t / 164.80) (translate 279 0 neptune) ] sun = color orange (circle 69) mercury = color (dark white) (circle 0.25) venus = color (light yellow) (circle 0.6) earth = color (light blue) (circle 0.63) mars = color (dark orange) (circle 0.34) jupiter = color (light orange) (circle 7.1) saturn = color (light yellow) (circle 6.0) uranus = color azure (circle 2.6) neptune = color blue (circle 2.5)

Here’s the tree example from the the gloss-examples package. Unfortunately, it pegs the server’s CPU at 50%… so I’ll have to work on what’s making it run so slowly.

import Graphics.Gloss animation :: Float -> Picture animation time = Scale 0.8 0.8 $ Translate 0 (-300) $ tree 4 time (dim $ dim brown) -- Basic stump shape stump :: Color -> Picture stump color = Color color $ Polygon [(30,0), (15,300), (-15,300), (-30,0)] -- Make a tree fractal. tree :: Int -- Fractal degree -> Float -- time -> Color -- Color for the stump -> Picture tree 0 time color = stump color tree n time color = let smallTree = Rotate (sin time) $ Scale 0.5 0.5 $ tree (n-1) (- time) (greener color) in Pictures [ stump color , Translate 0 300 $ smallTree , Translate 0 240 $ Rotate 20 smallTree , Translate 0 180 $ Rotate (-20) smallTree , Translate 0 120 $ Rotate 40 smallTree , Translate 0 60 $ Rotate (-40) smallTree ] -- A starting colour for the stump brown :: Color brown = makeColor8 139 100 35 255 -- Make this color a little greener greener :: Color -> Color greener c = mixColors 1 10 green c