I have a 3D plot I produced in Mathematica and I would like to share it with the world in a way that allows my audience to rotate it and interact with it in the broadest possible way. I would like this to be:

  1. In an open format that does not require a special player.
  2. Ideally something that can be opened directly in a browser or similarly common utility, and
  3. Something I can package in a single file, or set of files, without depending on a server.

The files could be distributed both directly to a contact, or as e.g. supplementary information to a journal article, uploaded on a web server. I would like to avoid solutions which depend on Wolfram servers or demand the viewer to have WRI software installed.

Solutions need not satisfy all of the above criteria, but they are all desirable:

  • The entry barrier for the user should be as low as possible. Saying "here, click this and it will open" will result in a lot more views than will "so yeah, you need to download X plugin and have Y browser, and then go to Z and select 'save link', ...".
  • If the plot is to be deployed as supplementary information for a journal article, it is important that the author be able to give the journal a self-contained implementation which the journal itself can host itself. For long-term durability reasons, it is desirable that this implementation doesn't have external dependencies on servers which may later move or go down.
  • Similarly, being able to send a zip file to a contact and tell them "unzip it and open X file" without needing to upload files to a server widens the user base of people who can do the sending to those that don't have easy access to a web server.
  • There are indeed technologies which lend themselves much more easily to non-proprietary deployment. However, it is desirable to be able to build the graphics in Mathematica without worrying about having to rebuild every part of the computation in an alternate system.

Meeting most of these requirements is definitely achievable. My favourite example is the manipulatable 3D graphics produced by NIST for the Digital Library of Mathematical Functions. These are amazingly simple to use and visualize and are well worth a look; for an example see their rendering of the gamma function:

NIST's implementation is a WebGL (a Javascript API for browsers which is widely supported) framework based on X3DOM and which implements the X3D standard; for more information see their documentation. While NIST explicitly refrains from endorsing the technology as a standard, it is a good sign that the standards are relatively mature and a good choice of technology.

I would like to replicate this type of behaviour. Is there some in-built or third-party functionality that allows it?

Ideally this thread should contain as many different approaches as possible - diversity is probably a good thing here.

  • $\begingroup$ WebGL is not an export format but, as the Wikipedia link states, a JavaScript API. You can use it to implement viewers for the export formats that Mathematica supports. There already are many such viewers. E.g. VRML has been around for so long that you can easily find viewers with Google. Then there are solutions based on three.js - but with all those options it's hard to know what kind of answer you're really looking for. Too many options... maybe look into processing.js, it's easy to learn and can load 3D graphics. $\endgroup$
    – Jens
    Apr 28 '15 at 19:32
  • $\begingroup$ TBH I think you're asking for the moon on a stick, by piling on too many arbitrary restrictions. You are better off working with another language in its entirety. If you dont want to live with WRI's restrictions use R or similar. Unlike R if people dont pay good money to Wolfram Mathematica won't exist. What you're actually asking for is fundamentally orthogonal to Wolfram's properietary, revenue driven approach and IMO they are actually pretty reasonable restrictions compared to some other packages. $\endgroup$ Apr 28 '15 at 22:16
  • $\begingroup$ I'm aware that this is a tall order and I'm not necessarily hoping for everything to be fulfilled - tools that tick several but not all of the boxes are still useful. However, I do feel there is a need for tools which satisfy those conditions so I chose to keep the question more general. I do not feel the restrictions are arbitrary. The difference between saying "here, click this and your browser will do it" and saying "here, go download X, Y and Z and then you can see it" is a huge reduction in the number of people that will actually click. $\endgroup$ Apr 28 '15 at 22:48
  • $\begingroup$ Similarly, if preparing graphics as supplementary information for a journal article it is often a deal killer to depend on external servers which may go down or change addresses without control from the journal, and if that comes at the cost of more work on the author's end that seems like an acceptable compromise. I also don't think shipping the entire procedure to other languages is a particularly smart choice - there are certain types of graphics the generation of which is miles easier in MM. This is about making those accessible to others in the cleanest way possible. $\endgroup$ Apr 28 '15 at 22:50

One very clean way to do this is via x3dom, which is a javascript framework for deploying the x3d standard. The library is well supported by modern browsers, and the output is an html file with a supporting archive of x3d files. It is generally very clean and fast, and it does not require any external plugins. The library can be called from the x3dom site or included locally, and it is dual MIT/GPL licensed.

To deploy such a document, there's two main options:

  • Zip it and send it to a contact.; once unpacked, the html file can be opened locally. This will work well in Firefox, though Chrome requires the user to allow access to local files, or to use some form of local web server (which can be very easy to set up).

  • Upload it to some web server. This can be done via e.g. github pages or whatever rocks your boat. It can also be deployed directly on a journal website as supplementary information to a paper, if your journal will play ball.

Overall the usage is not too complicated, but it does require one to get used to manipulating the x3d format directly, and this does have a learning curve to it.

I'll give a simple example here, which will produce this rendition of the gamma function:

Start with a simple plot:

plot = Plot3D[
  Abs[Gamma[x + I y]]
  , {x, -4, 4}, {y, -4, 4}
  , PlotRange -> {0, 6}
  , PlotPoints -> 50
  , ColorFunction -> ( Blend[{Darker[Blue], Cyan, Green, Yellow, Red}, #3]&)

This can be exported directly to the x3d format by Mathematica. For more information, see the X3D Export reference page.

Export[NotebookDirectory[] <> "plot.x3d", plot]

As of v10.1.0, the exporter is far from perfect. It will sometimes struggle with coloured surfaces, and it will always introduce a pretty much unwanted preamble to the file:

  <PointLight color='0.9 0.05 0.05'
      location='2. 0. 2.'
      radius='10000' />
  <PointLight color='0.05 0.9 0.05'
      location='2. 2. 2.'
      radius='10000' />
  <PointLight color='0.05 0.05 0.9'
      location='0. 2. 2.'
      radius='10000' />
  <PointLight color='0.9 0.7 0.9'
      location='-2. -2. -2.'
      radius='10000' />
  <Background skyColor='0. 0. 0.' />

The lights are buggy (recognized by WRI), and they are a faulty export of the usual point lights located at ImageScaled[{2,0,2}], ImageScaled[{2,2,2}], etc. (note the x3d lights are not at scaled coordinates). The black background is a complete mystery to me. This whole section should really be removed pretty much every time.

In general, however, it will mostly work OK. You may need to disable the colouring and roll your own <appearance> tags, but that is mostly fine-tuning the presentation. For my purposes I needed to generate the x3d files directly, which offers a lot of flexibility for programmatically generating x3d files (and which is made much easier by the XML package). For further reading on x3d, I would recommend x3dgraphics.com and web3d.org.

To actually view the x3d file, include it inside an x3d scene:


<!-- X3DOM inclusions -->
<script type='text/javascript' 
<link rel='stylesheet' 


<x3d width='800px' height='500px'>
    <Viewpoint position="5 -10 10" orientation="0.9 0.2 0.4 1.0"></Viewpoint>   
    <Inline url="plot.x3d" />

To be honest, I sometimes find the navigation inside the resulting 3D view to be much more flexible than Mathematica's, particularly once one gets used to its various options (double click to change center of rotation, middle-click-drag to pan, wheel or right-click-drag to zoom). X3D was developed partly with immersive walk-in navigation in mind, and the resulting scene is easier and quicker to navigate around.

Having said all of this, it would still be interesting to hear of other ways to deploy this type of content. This solution won't necessarily work for everybody and it would be nice to have alternatives (such as integration with three.js and processing.js) available and well described on this site.


I agree on two counts:

  • X3D is a logical export format, but
  • Mathematica's X3D support is, at best, limited.

Fortunately, the correspondence between Mathematica's GraphicsComplex and X3D is close enough that it is quite easy to roll your own exporter. To do so, let's begin with your own plot. We'll then extract out the primitives and directives that are necessary to generate the image.

pic = Plot3D[Abs[Gamma[x + I y]], {x, -4, 4}, {y, -4, 4},
  PlotRange -> {0, 6}, PlotPoints -> 50,
  ColorFunction -> (Blend[{Darker[Blue], Cyan, Green, Yellow, Red}, #3] &)];
{pts, faces, mesh, {vn}, {vc}} = {
  First[Cases[pic, GraphicsComplex[pts_, ___] :> pts, Infinity]],
  Cases[pic, _Polygon, Infinity],
  Cases[pic, _Line, Infinity],
  Cases[pic, Rule[VertexNormals, vn_] :> vn, Infinity],
  Cases[pic, Rule[VertexColors, vc_List] :> vc, Infinity]
  GraphicsComplex[pts, {EdgeForm[], faces, mesh},
  VertexNormals -> vn, VertexColors -> vc]

enter image description here

Note that the image we just displayed with Graphics3D was not particularly necessary. It's simply there to demonstrate that pts, faces, mesh, vn, and vc are sufficient to construct the image we want. Of course, we want to construct it with X3D. Towards that end, here's an HTML template for an X3D webpage:

template = "<!DOCTYPE html >
  <meta http-equiv='Content-Type' content='text/html;charset=utf-8' />
  <title>X3DOM from MMA</title>
  <script src='https://www.x3dom.org/release/x3dom.js'></script>
  <link rel='stylesheet' href='https://www.x3dom.org/release/x3dom.css'>

<body style='background: #eee'>
  <h1>X3DOM from exported Mathematica</h1>
    Mathematica 3D output rendered by X3DOM.

  <x3d style='background:white; margin-left:50px' width='800px' height='500px'>
        position='6.43873 -11.76285 4.77042'
        orientation='0.91920 0.28510 0.27164 1.40000'>
            diffuseColor='0.880 0.611 0.142' 
            specularColor='1 1 1'>
        <indexedFaceSet solid='false' 
          <Normal vector=$NORMAL$></Normal>
          <Color color=$COLOR$></Color>
          <coordinate point=$FACE_STRING$></coordinate>
          <IndexedLineSet coordIndex=$LINE_INDEX_STRING$>
            <coordinate point=$LINE_STRING$></coordinate>

This is almost complete, working HTML to describe a webpage. There are a few dollar-sign delimited variables that need to be set, however. For example, $COLOR$ needs to be replaced with a string that sets the vertex colors; these are stored in the Mathematica variable vc but need to be formatted in way that X3D will understand. Here's how to translate all those dollar-sign delimited variables:

formatted = Map[ToString[NumberForm[#,{8,4},ExponentFunction->(Null&)]]&, Chop[pts],{2}];
coordString = StringJoin@@Join[{"'"},Riffle[Flatten[Riffle[formatted,","]]," "],{"'"}];
faceIndexString = StringJoin@@Join[{"'"},ToString/@
  Riffle[Flatten[Riffle[Map[#-1&,Apply[Join,First /@ faces],{2}],-1]]," "],{"'"}];
lineIndexString = StringJoin@@Join[{"'"},ToString/@
  Riffle[Flatten[Riffle[Map[#-1&,First /@ mesh,{2}],-1]]," "],{"'"}];
formattedNormal = Map[ToString[NumberForm[#,{8,4},ExponentFunction->(Null&)]]&, Chop[vn],{2}];
normalString = StringJoin@@Join[{"'"},Riffle[Flatten[Riffle[formattedNormal,","]]," "],{"'"}];
formattedColor = Map[ToString[NumberForm[#,{8,4},ExponentFunction->(Null&)]]&, Chop[vc],{2}];
colorString = StringJoin@@Join[{"'"},Riffle[Flatten[Riffle[formattedColor,","]]," "],{"'"}];

Now, let's substitute those values into the template and export the result.

fileString = StringReplace[template, {
  "$LINE_STRING$" -> coordString,
  "$FACE_STRING$" -> coordString,
  "$LINE_INDEX_STRING$" -> lineIndexString,
  "$FACE_INDEX_STRING$" -> faceIndexString,
  "$NORMAL$" -> normalString,
  "$COLOR$" -> colorString}];
Export["X3DScene.html", fileString, "Text"]

After doing so, you should have a file named X3DScene.html in your working directory that displays a rotatable version of the original image right in the webpage. You can have a quick look at the result here.


Naturally, this not exactly complete. It assumes the input is a simple GraphicsComplex and translates polygons and lines that it sees in that GraphicsComplex. It doesn't attempt to deal with any other primitives. Of course, the correspondence between Mathematica's Graphics3D and X3D is bound to be incomplete; Mathematica has primitives that X3D does not and vice-versa. Here's a fun example that consists of 831 tori. While it can certainly be generated in Mathematica, I doubt that it would export well. The X3D version can be rendered more efficiently since X3D has a torus primitive while Mathematica does not.

  • $\begingroup$ Thanks, this is interesting. In my implementation (hosted here, or get the repo here; Mathematica code here) I also ended up rolling my own X3D code. Until the X3D exporting improves significantly, this will probably remain a job-by-job procedure, but it's really nice to have better tools for integrating with GraphicsComplex. I'm really happy this question is still getting attention ;-)!. $\endgroup$ May 4 '16 at 10:52
  • $\begingroup$ @EmilioPisanty Very nice! I almost feel silly for answering the question now. :) $\endgroup$ May 4 '16 at 13:42
  • $\begingroup$ No, this area really benefits from a variety of approaches, and it's here for the benefit of future visitors more than me. So thanks for answering! $\endgroup$ May 4 '16 at 16:43
  • $\begingroup$ Hi Mark - quick ping about the link with the example, which is currently broken. Can this be restored? Should we look for a more stable solution? Maybe ensure it's archived at the Wayback Machine and link there? $\endgroup$ Jul 6 '19 at 16:07
  • 1
    $\begingroup$ @EmilioPisanty Fixed! Note that I also had to add change to http links in the HTML template in the answer to https links. Thanks! $\endgroup$ Jul 7 '19 at 19:51

You can always deploy your plot in the "CloudCDF" format.

    Plot3D[Abs[Gamma[x + I y]], {x, -4, 4}, {y, -3, 3}, 
      PlotRange -> {0, 6}, Mesh -> None, ColorFunction -> "Rainbow", 
      MaxRecursion -> 5, BoxRatios -> {4, 3, 3}
  Permissions -> "Public"


Here is the link: https://www.wolframcloud.com/objects/178fae44-45f8-4219-9650-ecb1e303c3a7

  • $\begingroup$ Could you comment on what system requirements viewers would need to access that CDF? At the moment all I see on that URL is this (Chrome on Ubuntu 14.04). Ideally the plots should be platform-independent and not require Mathematica, or the CDF player, to work. $\endgroup$ Apr 28 '15 at 19:07
  • $\begingroup$ Hmm, it should just work in your browser. I'm not really sure why it's not working to be honest... $\endgroup$
    – Chip Hurst
    Apr 28 '15 at 19:10
  • 1
    $\begingroup$ If in doubt wrap it in a manipulate. (CloudDeploy[ ExportForm[ Manipulate[ Plot[Sin[a x + b], {x, 0, 6}], {{a, 2, "Multiplier"}, 1, 4}, {{b, 0, "Phase Parameter"}, 0, 10}], "CloudCDF" ], Permissions -> "Public" ] Result is here works on an Android phone albeit sluggishly. My company has done some significant testing with WRI around the cloud and its fairly sluggish for UI based stuff but has great promise. $\endgroup$ Apr 28 '15 at 22:07
  • $\begingroup$ Not your fault but this is what I see after it finished loading: i.stack.imgur.com/tG6nS.png $\endgroup$
    – Kuba
    Aug 21 '18 at 13:33
  • $\begingroup$ I noticed that too. I reran the command and updated the post. $\endgroup$
    – Chip Hurst
    Aug 21 '18 at 13:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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