I have a simple interactive plot in Mathematica, with controls for various parameters. The point of the plot is simply that users can explore how the plot changes as the parameters change by manipulating controls. IOW, this is Dynamic
-101 stuff (which is my level at the moment).
I would like to convert this to (non-proprietary) HTML + SVG (+ CSS, JavaScript, etc., as needed).
Is there a straightforward path for doing this? I know that one can export Mathematica graphics to SVG, but I'm not sure how one would export the dynamic behavior described above.
(BTW, I've looked for ways to do this starting from JavaScript to begin with, but I have found that JavaScript support for plotting is rather primitive. JavaScript graphics seem to be mostly about Illustrator-type stuff, and more recently, "data visualization". In comparison, there's almost nothing in the JavaScript graphics universe for plotting functions, the way Plot
et al. do.)
Answer
Animations as interactive visualizations
The simplest form of interactive graphics is an animation in which the play head can be moved by the user. That doesn't sound very interactive, but in terms of functionality the play head is nothing but a type of Slider
.
With this simple interpretation of interactivity, any movie format supported by Export
would be a way to create a standalone "interactive" visualization.
The starting point for this approach would be to generate a list of graphics that will form the frames of the animation:
frames = Table[
Plot[Sin[φ x]/(φ x), {x, -1, 1}, Background -> White, Frame -> True,
PlotRange -> {{-1, 1}, {-.5, 1}}], {φ, Pi,10 Pi, Pi/2}];
I've added a Background
to the plot because it will be needed to make the SVG
version of the movie come out properly below.
Frame based animations with SVG
But the other part of your question is specifically asking about SVG
and HTML
format. In SVG
, you can create animations by moving elements around using JavaScript
. But that's not something you can easily automate in exporting a Mathematica dynamic object - it would require case-by-case fine tuning.
So instead, I pursued a totally different way to combine the first point (the movie paradigm) with SVG
: export an animation in which each frame is a static SVG
vector graphic. Then the interactive element is again realized by the mere presence of the play head as a slider.
To make this a little more interesting than a typical movie player, I also added the ability to export a sequence of N
graphics but create a larger number M > N
of frames from them in the animation. This is achieved by allowing an indexed list to specify the movie frames, so the M
frames can walk through the N
graphics in any order with arbitrary repetitions during playback.
The Javascript movie player
The whole thing is based on a JavaScript
movie player I had written earlier, so you also get the ability to encode your frames as standard PNG
bitmaps instead of SVG
.
The special thing about the player is that it's a single standalone HTML
file. All movie frames are embedded in the HTML
using base64
encoding, so the animation remains just as portable as a normal movie, GIF
etc.
But the SVG
playback ability is what makes this most relevant to your question. Since SVG
takes more resources to store and interpret during the display, one can notice that the player is somewhat slower to start up when you choose SVG
format instead of the default PNG
format.
However, the nice thing is that SVG
animations can be enlarged without loss of quality, even while the movie is running.
I'm putting this out there for experimentation, and SVG
may not turn out to be the best choice for your application. But then you can still go with PNG
and get a smooth frame animation with full slider control.
The JavaScript
player has some additional features in addition to a draggable play head. Since it's frame-based, you can interactively change the frame delay, press a
to display all frames side-by-side, and change the looping behavior.
htmlTemplate =
Import["http://pages.uoregon.edu/noeckel/jensPlayer/jensPlayerTemplate.js", "Text"];
jensPlayer[name_?StringQ, a_?ListQ, opts : OptionsPattern[]] := Module[
{delay = 50, dataType = "img", htmlString, htmlBody,
scaledFrames, n, i, movieFrames, dimensions, frameStartTag,
frameEndTag, exportFormat, imgSizeRule,
loopOptions = {"Loop" -> "rightLoopButton",
"None" -> "noLoopButton", "Palindrome" -> "palindromeButton"},
toolHeight = 25},
n = Range[Length[a]];
imgSizeRule = FilterRules[{opts}, ImageSize];
If[imgSizeRule == {}, imgSizeRule = (ImageSize -> Automatic)];
scaledFrames = Map[Show[#, imgSizeRule] &, a];
dimensions = ImageDimensions[Rasterize[scaledFrames[[1]]]];
With[{del = ("Delay" /. {opts})}, If[NumericQ[del], delay = del]];
With[{ind = ("Indices" /. {opts})},
If[ListQ[ind], i = ind - 1, i = n - 1]];
Which[("SVG" /. {opts}) === True,
dataType = "object", ("SVGZ" /. {opts}) === True,
dataType = "embed"];
If[dataType == "embed",
frameStartTag = "