Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

I have a simple interactive plot in Mathematica, with controls for various parameters. The point the plot is simply that, by manipulating these controls users can explore how the plot changes as the parameters change. 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.)

share|improve this question
4  
There's no built-in functionality for export of dynamic SVG + JS or HTML5. The best high-level way to create HTML + JS in my opinion is Processing/JS - in particular, look at the reference for processing.js which exports interactive pages without requiring Java applets. Their older web export is Java applet based, but is very capable. Here is an example of an added library for plotting, combined with interactivity. –  Jens Jan 22 '13 at 19:42
3  
There's also geogebra, if you're especially interested in math visualization. –  Jens Jan 22 '13 at 19:44
1  
webMathematica might be the most straightforward for this, though not ideal. –  amr Jan 22 '13 at 20:34
1  
When you create interactive demonstrations in Mathematica, computations are typically done live. For any non-trivial problem you won't have access to all the function needed for this in JavaScript, so a more practical solution would be to pre-compute everything for a set of values of a manipulated parameter and export the results (say, as images). If we could find some existing browser based solution which allows dynamically seeking anywhere in an animation (e.g. animated GIF), we could use that to play back the results. Perhaps yo can try to find something like this. –  Szabolcs Jan 22 '13 at 20:43
2  
See also my related question that focuses on D3 viz: mathematica.stackexchange.com/questions/5416/… –  alancalvitti Jan 22 '13 at 22:58
show 10 more comments

2 Answers 2

up vote 12 down vote accepted

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}, Frame -> True, 
    PlotRange -> {{-1, 1}, {-.5, 1}}], {φ, Pi, 
    10 Pi, Pi/2}];

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://uoregon.edu/~noeckel/jensPlayer/jensPlayerTemplate.js", "Text"];
jensPlayer[name_?StringQ, a_?ListQ, opts : OptionsPattern[]] := Module[
  {delay = 50, dataType = "img", htmlString,
   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 = "<embed src=\"";
   frameEndTag = 
    "\" width=\"" <> ToString[dimensions[[1]]] <> "\"height=\"" <> 
     ToString[dimensions[[2]]] <> "\">";
   htmlString = "";
   movieFrames =
    Table[
     With[{svgName = 
        name <> ToString[
          PaddedForm[i, Total@DigitCount[Length[scaledFrames]], 
           NumberPadding -> {"0", " "}]] <> ".svgz"},
      Export[svgName, scaledFrames[[i]], "SVGZ"]; 
      frameStartTag <> svgName <> frameEndTag], {i, 
      Length[scaledFrames]}];
   htmlString = StringJoin[movieFrames],
   If[dataType == "img",
    frameStartTag = "<img src=\"data:image/png;base64,";
    frameEndTag = "\">";
    movieFrames = Apply[If[("Parallel" /. {opts}) === False,
       Map, ParallelMap], {ExportString[#, "PNG"] &, scaledFrames}],
    frameStartTag = "<object data=\"data:image/svg+xml;base64,";
    frameEndTag = "\" type=\"image/svg+xml\"></object>";
    movieFrames = Apply[If[("Parallel" /. {opts}) === False,
       Map, ParallelMap], {ExportString[#, "SVG"] &, scaledFrames}]
    ];
   htmlString = 
    StringJoin@
     If[("Parallel" /. {opts}) === False, 
      Map[(StringJoin[frameStartTag, 
          StringReplace[ExportString[#, "Base64"], "\n" -> ""], 
          frameEndTag]) &, movieFrames],
      DistributeDefinitions[frameStartTag]; 
      DistributeDefinitions[frameEndTag]; 
      ParallelMap[(StringJoin[frameStartTag, 
          StringReplace[ExportString[#, "Base64"], "\n" -> ""], 
          frameEndTag]) &, movieFrames]
      ]
   ];
  htmlBody = 
   StringReplace[
     htmlTemplate, {"**DATE**" -> DateString[], 
      "**DATATYPE**" -> dataType, 
      "**WIDTH**" -> ToString[dimensions[[1]]], 
      "**HEIGHT**" -> ToString[dimensions[[2]]], 
      "**TOOLHEIGHT**" -> ToString[toolHeight], 
      "**DELAY**" -> ToString[delay], 
      "**COMBINEDWIDTH**" ->  ToString[Length[a]*dimensions[[1]]], 
      "**USERNAME**" -> $UserName, 
      "**LOOPCONTROL**" -> 
       With[{loopOption = ("Looping" /. {opts}) /. loopOptions}, 
        If[MemberQ[loopOptions[[All, 2]], loopOption], loopOption, 
         "None" /. loopOptions]], 
      "**INDICES**" -> StringJoin@Riffle[Map[ToString, i], ","], 
      "**AUTOPLAY**" -> 
       If[("AutoPlay" /. {opts}) === True, "true", "false"], 
      "**TITLE**" -> name}] <> htmlString <> "</div></body></html>";
  Export[name, htmlBody, "TEXT"];
  ]

Examples

With this function you can now export the frames created earlier:

jensPlayer["sombrero.html", frames]

SystemOpen["sombrero.html"]

jensPlayer["sombreroIndexed.html", frames, "Delay" -> 60, 
 "AutoPlay" -> True, "Looping" -> "Palindrome", "Parallel" -> True, 
 "Indices" -> {19, 18, 17, 16, 15, 15, 17, 18, 19, 19, 18, 17, 16, 15,
    14, 13, 11, 9, 7, 5, 4, 3, 2, 2, 1, 1, 1, 1, 1, 2, 2, 3, 4, 5, 7, 
   9, 11, 13, 15, 16, 17, 18, 19, 19, 19, 19}]

SystemOpen["sombreroIndexed.html"]

jensPlayer["sombreroSVG.html", frames, "SVG" -> True]

SystemOpen["sombreroSVG.html"]

Each of the export commands illustrates a different export format.

Options

By default, the images in list images are displayed sequentially with a constant time delay, in a single playback run. If the option "Indices" is specified, frames are played back in the order determined by that option, see below.

Options (except for ImageSize, all options are strings requiring quotation marks):

  • ImageSize
    • number of pixels in the horizontal direction. If unspecified, will be determined by size of the graphics in images.
  • "Looping"
    • "None" (default),
    • "Loop"
    • "Palindrome"
  • "Delay"
    • number of milliseconds between frames (default 50)
  • "Indices"
    • {i1, i2,... iN}, a list of (not necessarily different) integers between 1 and Length[images]. The number of frames in the movie is N. in (n = 1,...N) is the index in list a of the image to be displayed as frame n.
  • "AutoPlay"
    • True
    • False (default)
    • sets whether the movie should start playing as soon as loaded.
  • "Parallel"
    • True (default)
    • False
    • switches between parallel/sequential image conversion. Parallel speeds up the export if more than one Mathematica Kernel is available.
  • "SVG"
    • True
    • False (default)
    • switches between SVG or PNG format for the frame export. SVG format is provided on an experimental basis because browser support is evolving slowly. It is a resolution-independent graphics format that can be resized seamlessly in Firefox. SVG requires larger startup time, so it should not be used for large movies. With SVG you should also specify an explicit background for the frames, e.g. by doing images=Map[Show[#,Background->White]&,originalImages]. Although SVG movies yield much larger HTML files, gzip compression can make file sizes comparable to those of PNG movies (or even smaller). Since web servers increasingly are configured to transmit HTML files using on-the-fly compression, SVG does not necessarily increase download times.

Additional notes

Because the JavaScript file is too unwieldy for this post, I put it on my web site and use Import in the jensPlayer function to load it. I would suggest that you download the file

http://uoregon.edu/~noeckel/jensPlayer/jensPlayerTemplate.js

and copy and paste it as a string into the htmlRemplate = assignment. Then you can use the function offline.

Also, I had tried SVGZ instead of SVG, and the function allows that as an option. However, this compressed SVG format doesn't play back so well apparently. Maybe the decompression is causing problems - I haven't had time to debug that yet. So I'd recommend you not use SVGZ at the moment, even though you may have seen it in the code above.

Illustrated example

To show what the movie should look like, here is a sequence of images that represent an electric field distribution for a range of separations d between two charged objects. The parameter d is then controlled with the movie slider:

field[a_, d_, x_, y_] = 
  D[Log[((x - d)^2 + y^2)/((x - a)^2 + y^2)], {{x, y}}];
images = With[{r = 1.}, Table[
    Show[
     StreamPlot[field[1/d, d, x, y], {x, -2, 4}, {y, -3, 3}, 
      FrameLabel -> {"x", "y"}, StreamColorFunctionScaling -> False, 
      StreamColorFunction -> "Rainbow", 
      PlotLabel -> Row[{"d = ", PaddedForm[N@d, {4, 2}]}]], 
     Graphics[{Thickness[.009], White, Circle[{0, 0}, r], 
       PointSize[.03], Point[{d, 0}]}], Background -> GrayLevel[.2], 
     LabelStyle -> White, 
     FrameStyle -> Directive[Lighter@Gray, Thickness[.005]], 
     ImageMargins -> 5],
    {d, 1.5 r, 4 r, r/4}]];

jensPlayer["imageChargesSVG.html", images, "SVG" -> True, 
 "Parallel" -> False]

example movie

The movie is a screen recording of the JavaScript movie player in Google Chrome. The controls at the bottom fade in and out. The left-most control switches between single run, back-and-forth (palindromic) playback and looping. I show how to stop the movie by pressing the red button, and then drag the slider to get to a particular frame. After restarting and stopping agin, I click on the frame counter to get a dialog that allows interactive adjustment of the frame delay. After changing the delay to 250 milliseconds, the playback is slower.

There are several other keyboard shortcuts that are explained if you click the question mark at the bottom right. In particular, you can use arrow keys to move from frame to frame while the movie is stopped.

share|improve this answer
add comment

The short answer is no, there is no straightforward (built-in) way to convert Mathematica's dynamic objects to non-proprietary HTML+SVG/JS. To see why, consider how you might try ti represent the following very simple example in HMTL/SVG?

Manipulate[With[{pts = {#, Sin[a*#]} & /@ (x /.
  Quiet[Solve[Sin[a*x] == b*AiryAi[-x] && 0 < x < 10, x],
    {Solve::nint, Solve::ratnz}])},
  Plot[{Sin[a*x], b*AiryAi[-x]}, {x, 0, 10}, Epilog -> 
   {PointSize[Large], Point[pts]}, PlotRange -> {-1.1, 1.1}]],
  {{a, 1}, 1, 10}, {{b, 2}, 1, 3}]

enter image description here


Edit

Of course, the point is that most interesting dynamic illustrations in Mathematica will make use of Mathematica's extensive library of mathematical functionality - functionality not generally available in GeoGebra, D3, or javascript libraries such as processing.js or numeric.js. This exact point was made in the comments, but without example.

It is not my point to demean those other tools at all - simply to point out that their emphasis is different. If your objective to create broadly accessible (i.e., without plugins) illustrations based on simple mathematics, then perhaps you should focus on those types of tools. If you need to create illustrations that require more advanced (mathematical) functionality, then it will be hard to beat Mathematica. Clearly, Wolfram Research is aware of the need to disseminate material created with Mathematica and have released web Mathematica and CDF for exactly this purpose.

The particular example that I gave is very easy in Mathematica precisely because it has an excellent library of special functions and well integrated tools for solving equations involving those functions. (Any number of other examples could be given.) Implementing this example in javascript would be challenging, since you'd have to implement the fundamentals yourself.

It's amusing that some anonymous user chose to downvote this answer without accepting that challenge.

share|improve this answer
    
"Whatever your approach, you'd certainly need a good library of special functions and tools to solve equations involving these over entire intervals" Well, a point to such a library was one of the types of answers I was hoping for... Thanks for the nice example, BTW! –  kjo Jan 22 '13 at 20:02
    
I don't know much about SVG yet, but my impressions about it so far are consistent with your comments. Namely, SVG provides the graphics primitives, but there do not seem be any decent libraries (in JavaScript or anything else) to support higher-level programming of SVG-based plots. –  kjo Jan 22 '13 at 20:08
2  
@kjo, The D3 API developed at Stanford by Heer and others, has been used extensively by NYTimes among others for interactive viz (see the link in my comment above for examples) –  alancalvitti Jan 22 '13 at 23:03
    
I think the question is valid because CDF isn't viable in all scenarios where HTML + SVG is available. So I still want to post an alternative answer - but I didn't downvote yours because it's certainly a correct descriptin of the difficulties. And a constructive answer unfortunately takes a little longer... –  Jens Jan 24 '13 at 18:02
    
@Jens Clearly, the question is valid - I don't mean to imply otherwise. It's just that the short answer is no, there is no straightforward (built-in) way to convert Mathematica's dynamic objects to non-proprietary HTML+SVG/JS. The longer answer illustrates why. I wouldn't think you're the down-voter, by the way! –  Mark McClure Jan 24 '13 at 18:39
show 1 more comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.