Along with d3, the three.js library is one of the most powerful javascript libraries available for creating slick visualizations. I wondered if it would be possible to create data visualizations in threejs as easily as I had done using d3. One thing that d3 does very well is to take your data and apply a layout algorithm to it for use in drawing visualizations such as treemaps and piecharts. While I’m sure there are other ways to generate this layout data, the d3 approach is the one that I’m most comfortable with and I wondered if it would be possible to use this layout data to render 3D versions of those same visualizations.
This first demo shows a WebGL (or Canvas if you’ve got a crappy browser like IE) rendered treemap that was created using the d3 treemap layout: (I had to use JSBin instead of CodePen for the demos because the embedded CodePen snippets kept locking a few seconds after loading; no idea why)
See the Pen d3 3D Treemap by Bill White (@billdwhite) on CodePen.
The results work pretty well. The Tweening is handled using TweenJS which is what the threejs folks use. I wanted to do the updates using d3 transitions but using the Tween code from the examples I had seen worked straight away so maybe I can swithc over to d3 transitions in the future. Also, when you set the zscale to 0, you see a bit of color bleeding and I have no idea why. (Let me know if you have a solution.)
First, I must give credit to Steve Hall who has also investigated this concept. Last year I explored ways to create 3D visualizations with threejs using layout data from d3, but I hit a wall when I was unable to get the coordinates to translate between the two worlds correctly. Steve also worked on this idea and wrote some fantastic blog entries about it. His work focused more on created mini-visualizations, attaching them to DOM nodes and then having threejs render those nodes in 3D. After reading his work, I contacted him to ask if he knew how to correctly translate the coordinates and to my relief, he gladly provided the solution. So in the interests of full disclosure, my work here was greatly facilitated by his assistance. Thanks Steve!
The solution I landed on is relatively straightforward: Take a dataset, run it through d3 to apply layout positioning information to each data item and then render those items in threejs rather than the normal DOM-oriented CSS styling that we use with d3. In a typical d3 visualization, the data is bound to the DOM nodes and you use the enter/update/exit lifecycle to add, modify and remove those nodes. With threejs, we are no longer adding stuff to the DOM tree (unless we use the CSS3DRenderer, but more on that in a second) because the 3D world is rendered using WebGL (or Canvas when WebGL is unsupported).
So I am taking the dataset that has been processed by d3 and then adding each item to the threejs scene. I add, update and remove items from the scene in reaction to the add/update/remove calls from the d3 general update lifecycle. Interestingly, I’m actually adding DOM nodes to the tree using d3 but they are empty and unstyled so they end up only serving as placeholders for the current set of rendered data in the threejs scene. If the data changes, the d3-generated empty nodes are added/updated/removed and then, for each change, a corresponding change is made to the objects in the threejs scene.
Steve’s demos are implemented using the CSS3DRenderer which actually displays the 3D objects using only CSS3 transforms. Because of this, you are actually still in the d3 data-bound-to-the-DOM-node situation. It is really the best of both d3 and threejs because you can take full advantage of d3 selections and still have threejs display the 3D results using those same nodes. However, if you want your 3D visualization to look, well, three-dimensional, you have to bridge the gap between the d3 generated DOM nodes and the corresponding objects in the threejs scene yourself.
This second demo uses the CSS3DRenderer and uses d3 to create the DOM elements which are then manipulated by threejs. You can see that while it is nice, it is not quite as fancy as the first demo:
See the Pen CSS3DRenderer Treemap by Bill White (@billdwhite) on CodePen.
This was very cool. I think it has applications in education, perhaps the periodic table but showing atom’s size or other metrics.
Very good!
Micah @entrebot
Ok, your treemap just blew my mind. The coolness factor is through the roof.
Other than 3-d scatter plots and such, do you think that the added 3rd dimension is actually better for conveying information? In general, I feel that the 3rd dimension distorts things, or ends up being a super shiny object that diverts the viewer from your main point.
I did some D3/WebGL integration, but other than a Parallel Coordinates to 3D scatterplot integration, I didn’t find that the 3rd dimension helped my visualizations convey information any better than their 2d counterparts.
I would love to see examples where the 3rd dimension is used to convey more information in the same amount of pixels more effectively.
Truly awesome Treemap, best D3/ThreeJS integration I have ever seen.
I think you are absolutely correct that 3D adds very little benefit in most situations. A case can be made for its use in a treemap or a scatter plot (as you mentioned) and perhaps even a pie chart. But it also adds to the visual complexity and clutter that the viewer must sift through to discern valuable information. As a general rule, simpler is usually better 🙂
Imagine a treemap with depth. From a straight on view, it looks like a regular treemap, however, it also has depth which is revealed when you change the point of view. When you click on the particular treemap element, it reveals the components underneath, again with an extra depth dimension added.
It’s a fairly complex way to add an extra dimension, but your existing treemap is close to what I am talking about. You appear to render the treemap cells as planes off the z-axis. Modify them to render as cubes/boxes. Almost like a bar chart with bars laid out in the x/y dimension with some drilldown capability (ie: click a treemap cube and the others disappear and you see the sub-components of that cube).
That would be very visually appealing, though again, not sure if it is the most effective way to convey that information probably not, but very very cool and likely to inspire someone else to do something even better.
Keep up the awesome work!
– Pat
This is really cool but can you elaborate on converting the coordinates from D3 to Threejs . how do you do the projection ?
Cheers.
Is it possible to work with SVG elements instead of DIVs? I have many error using SVGRenderer and SVGObject.
Thanks in advance