Clik here to view.

Authored by Steven Hall
I'm back again this week with another fun experiment with D3.js and THREE.js. This time we're adding a physics engine into the mix! In this post I present some examples of simulating a scene in the DOM that use CSS 3D transforms, but a JavaScript physics engine is used for determining the movement and rotation of the individual elements (in this case D3 charts, but it could be any HTML element really).
Examples for this Post
The examples presented here use: THREE.js, D3.js, and the Physijs 3D JavaScript physics engine that is tightly integrated with THREE.js.
You can look at the examples in your browser and "view source" to see the code or you can download all the code from the GitHub repository. All the main code is in the viz.js file and then there's a few lines of JavaScript in each HTML file in a script tag.
Download all the code from GitHub
Take a look at these examples in your browser:
Example with 10 charts
Example with 20 charts
Example with 50 charts
Example with 100 charts
The examples are all the same except for the number of charts that get generated at each iteration (that seemed to be the driving factor on performance). On my Mac laptop Chrome and Safari can easily handle 100 charts and maintain a steady 60 frames per second (fps). Firefox seems to lag on this demo on my machine. If you are on a slower device you may want to try out the 10 chart example first and see how that works. Undoubtedly there is some low hanging fruit for optimization here.
TIP: Even if you are on a desktop (or other fast machine) it's worth checking out the 10 and 20 chart examples as well because you can see how the charts react as rigid bodies and bounce off each other more clearly in those examples.
Also note:
- The charts have events - click on a chart and the color changes and the data gets console logged.
- The charts have mass - the mass is equal to 10 times the number of investments (the big white number on each chart). Discussed more fully below.
Why a Physics Engine?
Image may be NSFW.Clik here to view.

This is mainly just a fun experiment. There is a lot of upfront cost in taking on 3D graphics and incorporating physics into your work flow, but the payoff is the ability to create fantastic interfaces that react to user input in a much more intuitive way. Because elements are modeled on real world phenomena where items can have mass, velocity and friction the physics engine can determine how things move without hard coding complex rules.
3D or Not 3D?
THAT is a good question. When you start looking at physics engines you will have to choose between a number of 2D and 3D options. I first went out and looked at the various JavaScript physics engines out there to make a choice for this demo. Some of the most popular engines out there are really physics engines used for native Android and iOS development that have been converted to JavaScript.
A quick survey of the engines I looked at:
Box2DJS
This is a JavaScript version of the popular 2D open-source C++ engine called Box2D. It's the engine that is used in Angry Birds for example. There is also a port of this engine in JavaScript on GitHub. The code gets converted automatically i.e. without human intervention, so you end up with some pretty ugly code. I also have to say the documentation on this one is a bit rough.
Ammo.js
This is a 3D engine with a lot of features. It's a JavaScript port of the Bullet physics engine used in games like Grand Theft Auto (IV & V). Like Box2D the code is auto-generated so what you end up with is some pretty terse JavaScript.
Physics.JS
This is a newer and very popular 2D engine that is written in JavaScript from scratch. There's some pretty impressive demos that you can take look at here. This is a relatively new project, so it may need some time to develop, but I found that some of the examples were jittery on a laptop and performed pretty poorly on a tablet. It does include a DOM renderer in the GitHub repository and an example of it in action.
Physijs
Physijs is a 3D physics plugin for THREE.js that acts as a wrapper around the ammo.js engine described above. It gives you a more approachable API to work with and it also handles running the physics in a separate process called a "web worker." This is important because it leaves the main thread open to handle input while the physics continues to run in the background. Some pretty impressive examples of Physijs in action out there. My favorite has to be this version of Jenga (WebGL).
Choosing 3D and Physijs
In the end I decided, for me, it doesn't make sense to solve for 2D only to have to revisit the issue again when the need to develop in 3D arises. Developing in 3D ups the complexity of your work, but once you are comfortable with tools you can do some pretty amazing things and you can always work in 2D if needed. I am also in interested in working more with THREE.js and WebGL, so Physijs, which is tightly integrated with THREE.js, made a lot of sense in that regard. Let's look at the code!
Looking at the Code
Yes, yes, I know I recycled the charts and some of the code from my last post on D3.js and THREE.js. I did, however, have the decency to change up the CSS a little. I hope that makes up for it. The data again is from TechCrunch's CrunchBase.
A Chart is a Box Not a Plane
Let's talk conceptually here for a moment. We've got a bunch of 2D charts that we want to move around according to the laws of physics. I wanted to have them collide and move apart from each other like real physical objects. In Physijs you have some basic shapes that you can work with to create your objects like planes, spheres, and cubes. In my first attempt at this I made the mistake of creating these charts as "planes" with essentially no thickness and then specifying the height and width. This was a big mistake because Physijs sees those planes (even with a height and width defined) as infinitely large. It took some time to figure this out, so don't make that mistake. You can see a note on this here at the bottom of the page, which I didn't see until much later.
In my experimenting, I found it's best to consider each chart (or whatever HTML element you have) as a very thin box (or cube). So we need to tell the physics engine about each one of our "boxes" and then associate that box with the actual HTML element (chart) that is "in" that box. In Physijs you have to give the box a THREE.js geometry, material and specify a mass. In our case, the actual box is not being rendered to the screen it only exists in the mind of the physics engine (this is tough to conceptualize, but stay with me...it took awhile for me to put all this together, too). So we just give the box the most basic THREE.js material and specify the other parameters needed to set things up. If you look at the viz.js file. Each chart is being generated in D3 and then uses "each" to add the chart to the scene like this:
// From the viz.js file in the examples
// Last line of drawElements function
elements.each(objectify);
// Each chart goes to objectify function
// Note: this = the chart element, d = data for the element
// Very handy!
function objectify(d) {
var rnd = Math.random;
var geom = new THREE.CubeGeometry(elWidth, elHeight, 4); //A
var basic_mtrl = new THREE.MeshBasicMaterial({wireframe: false}); //B
var physi_mtrl = Physijs.createMaterial(basic_mtrl, 0.9, 0.9); //C
var object = new Physijs.BoxMesh(geom, physi_mtrl, d.awards * 10); //D
object.rotation.set(rnd() * 100, rnd() * 100, rnd() * 100);
object.position.set(rnd() * 900 - 450, rnd() * 400, rnd() * 100);
object.element = this; //E
object.name = d.name;
scene.add(object);
}
So, if you are familiar with THREE.js this looks like some pretty standard stuff. At point A above we create a new cube geometry for our thin box element. In my case, to make things easy, all the elements are the same width and height and I declare the "elWidth" and "elHeight" variables at the top of the file. The depth of 4 is just arbitrary, it looked good so I kept it. But what is cool here is that because we are cycling through using D3 we have access to the actual element we are working on (as "this") and also the data for that element (as "d") so you could derive the size of the element however you like and, obviously, they could all be of different sizes (and even shapes if you wanted to get more fancy).
At point B above, we create a THREE.js basic material for our box. I just used the most plain vanilla material because in this case it not being rendered to the screen. At point C, the THREE.js material is then turned into a Physijs material. The point here is to add the two parameters "friction" and "restitution" (both 0.0 to 1.0 values). Here is a little note on this. This step is not necessary, but I wanted to include it. To be honest, I don't see much happening when changing these parameters and it may not be working correctly. I console logged the objects and it is getting set appropriately, but there may be something missing or the effect is just very subtle in this case. I'll have to dig into that later.
Data = Mass
This is one of the more interesting aspects of these examples. The effect is not great here, but I think this could be used to do some interesting things in the future. At point D above, our final Physijs.BoxMesh object gets created by passing in the geometry we created, the Physijs material and a "mass" for our object. I played around with the mass quite a bit, giving some charts extreme mass relative to the others etc. In the examples the mass is set to the number of investments (awards) times 10 - that's the big white number on each chart. If you look at the 20 chart example (all the examples have the mass set in the same way, but I think it's easier to see the effect with this one) you can see how the lower value charts tend to get flung off further from the center (the values range from like 90 to 630). It's subtle in this case, but I thought this was interesting way to tie the data to a physical attribute of the physics simulation. Cool stuff.
After creating the Physijs.BoxMesh, the rotation and position are assigned as random values to start off the simulation. Again, you've got access to your data via D3 here, so these placements could be made according to real data attributes. In these examples the random assignment, clustered close together, starts the charts moving because they are overlapping and want to get away from each other. That gives it the initial "explosion" effect.
Adding the HTML Element
At point E in the code above one of the more important things is happening here. We are adding the chart as the "element" of the object. This is really where the HTML on the screen gets associated with the "box" that the physics engine is working with. This is another case where D3 makes this a pretty elegant process because we just have to say:
// From the objectify function in viz.js
object.element = this;
The element will get picked up by the THREE.CSS3DRenderer that is used to calculate the matrices that actually give the element its position and rotation on the screen. If you look in the dom-renderer.js file you can see the renderer used in the examples. This is adapted from the work of Mr. Doob and handles the really hard part of making all of this work. I took out some extraneous code from Mr. Doob's original version and the key piece to look at is where the objects are rendered:
// From the dom-renderer.js file
var renderObject = function (object, camera) {
if (object instanceof Physijs.BoxMesh) { //A
style = getObjectCSSMatrix(object.matrixWorld);
var element = object.element; //B
element.style.WebkitTransform = style;
element.style.MozTransform = style;
element.style.oTransform = style;
element.style.transform = style;
if (element.parentNode !== cameraElement) {
cameraElement.appendChild(element);
}
}
for (var i = 0, l = object.children.length; i < l; i ++ ) {
renderObject(object.children[i], camera);
}
};
The thing to remember is that there are other things like the camera that get sent to the renderer because in THREE.js they are part of the scene as well. This recursive function steps through all of the elements and make sure the matrices get updated. You can see at point A that I just added an if statement to capture our chart elements that will be instances of Physijs.BoxMesh. And then a few lines down at point B it uses the elements that we put into our object earlier.
Putting It All Together
There's only about 200 lines of JavaScript in the viz.js file and we've looked at the really important pieces already. The viz.js file sets up a little mini API that we can use in each of the HTML files. The data gets loaded and then the first iteration of charts is generated. The charts are then redrawn on an interval (just to keep it entertaining).
Looking at the example HTML files you see...
// From the example HTML files
d3.json("data/investments.json", function (error, data) {
VIZ.drawElements(data);
d3.select("#loading").remove();
VIZ.render();
setInterval(function () {
VIZ.resetScene(data)
}, 15000);
window.addEventListener('resize', VIZ.onWindowResize, false);
});
A couple of other things are worth noting at the top of the file that make everything work.
// From the viz.js file
Physijs.scripts.worker = 'lib/physijs_worker.js'; //A
Physijs.scripts.ammo = 'ammo.small.js'; //B
var scene = new Physijs.Scene; //C
scene.setGravity(new THREE.Vector3(0, 0, -100));
var renderer = new THREE.CSS3DRenderer(); //D
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.domElement.style.position = 'absolute';
document.getElementById('viewport').appendChild(renderer.domElement);
In the code above at points A and B we tell Physijs where to find two other files that it needs. The physijs_worker.js file manages the physics process on a separate thread. This keeps the physics from impacting the performance of the rest of your application. The second file is the ammo.js physics engine. This is a pretty monstrous file (1.6 MBs) and really adds to the payload of your page, so keep that in mind. Other engines will have a smaller footprint, but you are making tradeoffs on functionality and ease of use.
At point C above the Physijs scene is created and we specify the gravity. In this case the charts are generated in the foreground and then fall in the negative Z direction. I thought this looked pretty cool. Lastly, at point D above the CSS3DRenderer is specified and we make the size of the renderer equal to the full width and height of the page.
Summing Up
That about does it. I think this was a pretty good little stress test and I am amazed at how well it performs give then number of elements and the sophistication of these charts. These are not trivial DOM elements. They are some pretty hefty multi-series charts with titles, legends, and axes. I received some really helpful feedback from the last post on D3.js and THREE.js, so feel free to comment here or send me a note via the website contact page. If anyone knows of other similar projects I'd like to hear about them.