Creating An Interactive Data Visualization with A-frame and D3.js

Photo by Hammer & Tusk on Unsplash

Intro

The way we access to the information has diversified over time. It has shifted from text to images, physical to digital and offline to online. Now, many designers, innovators, and design/IT industry gurus expect VR to be the most common way to access, share and exchange the information on the web. Then how the next generation web platform will affect the way we perceive information in UX perspective? Through my project, I am exploring the potential of web VR in data visualization. The most popular framework to create interactive data visualization is D3.js. it is a JavaScript library for manipulating documents based on data. D3 helps you bring data to life using HTML, SVG, and CSS. D3’s emphasis on web standards gives you the full capabilities of modern browsers without tying yourself to a proprietary framework, combining powerful visualization components and a data-driven approach to DOM manipulation. For web VR, there are several frameworks helps developers to build one. The liberary I will be using for my project is A-frame, which is a HTML based framework enables people to build quick VR sketch on the web without any sophisticated scripting, which can be easily combined with other frameworks and APIs to create dynamic visualization effortlessly.

Experiment with D3.js

Last year, I created Texas Death Row, which is an interactive data visualization exploring various feelings of executed inmates by analyzing emotions found in their last statements. Each particle represents an individual inmate. Upon clicking one, it will show you the information of executed inmates along with their final statements. The information can be reorganized by various filters at the top left and provides viewers with an immersive experience triggering viewer’s curiosity. By providing users with an intuitive way to collect and analyze information, viewers can play around and discover narratives of their own from the data at hand.

As a first step, I decided to explore D3.js by recreating my project in Javascript. My experience of building an interactive visualization in Processing3 was somewhat challenging, because I had to build the whole system from the scratch. The entire visualization is a combination of a particle array, physics simulation, and HUD. On the other hand, D3.js has pre-built JavaScript functions to select elements, create and style SVG objects, add transitions, dynamic effects and tool tips to them.

My very first attempt to create data visualization morphs from one to another.

This is my very first sketch to create an interactive visualization switching from one to another. As you can see what’s very efficient about creating data visualization with D3.js is that it only takes several lines to create a particle based visualization. It can be done by just simply selecting an element group, appending data and returning values for each data point.

svg.append("g").selectAll("circle") //select element tagged circle 
.data(dataArray) //append data in a variable "dataArray"
.enter().append("circle") //append circles
        //assign values to each attributes
.attr("cx",function(d,i){
return centerX + 200 * Math.cos(2 * Math.PI * i / 90);
})
.attr("cy",function(d,i){
return centerY + 200 * Math.sin(2 * Math.PI * i / 90);
})
.attr("r",function(d){ return d * 3; });

The final result of the sketch turned out like this.

The look of the visualization changes based on filters activated by the user. The next thing I did on my next sketch was embedding D3.js data to A-frame objects. Here is the result of my experiment:

As seen above, D3 works splendidly well with A-frame! The line of spheres change its vertical position every time a mouse pointer touches one of them.


Bringing Data to the VR World

Now, it is time to build an interesting data visualization. but before that, what is the real advantage of creating data visualization in VR space? VR can give high sense of presence through simulation of the real world. Let’s think about how digital 3D objects are displayed. We perceive these three dimensional data through a flat screen monitor, and often it requires a good ability to convert its volume and size in real life scale. However in VR, these become more straightforward. Being in VR environment helps people to have the better sense of scale, volume and depth of the objects by allowing them to observe them in multiple angles. Before I start creating a VR data visualization application, I wanted to visualize something that people are familiar with but only exist conceptually in their mind, but it is important to measure its physical volume and size to understand: a pile of dollar stacks. Large amount of money only exist as a number in most people’s mind, especially for those who have never seen in real life. At least for me it is quite hard to come up with a vivid image when I read articles about celebrities’ last year net worth. Therefore, I decided to build a dollar bills generator to help people(including myself) to imagine what it is like when rappers are talking about “stacking ‘em papers.”

Real life scale of 300,000 dollars with 10,000 dollars stacks.

Creating a VR Environment

Building a VR scene with A-frame is easy and simple. A-frame has what is called A-frame Registry. This is similar to the collection of components and modules on the Unity Asset Store, but free and open source. These pre-built customized components can be loaded in the scene by dropping in a <script> tag. The code below is an example.

<script src="https://aframe.io/releases/0.7.0/aframe.js"></script> //loading A-frame
<script src="https://cdn.rawgit.com/zcanter/aframe-gradient-sky/master/dist/gradientsky.min.js"></script>
//loading a gradient sky component

Developers can always add and subtract these components to build a desired VR scene. Once all the neccesary components are loaded, the next step to build a scene is creating a VR scene. A scene is represented by the <a-scene> element. The scene is the global root object, and all elements(objects, skybox, playable character, etc) are contained within the scene. The basic structure of establishing a scene is like this:

<html>
<head>
<script src="https://aframe.io/releases/0.8.0/aframe.min.js"></script>
</head>
<body>
<a-scene>//Add objects within the scene</a-scene>
</body>
</html>

In <a-scene>, developers can add objects using <a-entity> tag. Entities are container objects into which components can be attached. Entities are the base of all objects in the scene. Without components, entities neither do nor render anything, similar to empty <div>s. A-Frame represents an entity via the <a-entity> element. As defined in the entity-component-system pattern, entities are placeholder objects to which we plug in components to provide them appearance, behavior, and functionality. Importing 3D model to the scene is also very convenient. Here is the example that I created for the project.

<a-entity gltf-model="#male_model" // assign ID
position="-13.474 5.683 -18.878"// set position
rotation="0 45 0" // set rotation
scale="10 10 10"> // set scale
</a-entity>

As you can see, 3D human figure model is loaded in the scene. Likewise, I brought UI kit in the same way I brought the human model. For the UI, I imported Material Kit from Github. I set up a search bar in the center of the camera so that users can type the amount of money they want to generate. A virtual keyboard appears as they click the bar. Also there is a checkbox which allows user to compare the size and the volume of the dollar stacks with a real life scale model.


Adding a Physics System

For creating a dynamic scene, I applied real time shadow and a physics system to each dollar bundles. I found Threex.ammo which is a library integrated with ammo.js and three.js. It also supports the physics system for A-frame, and the way it can be applied to objects are the same as how components can be added to entities. With combination of D3.js, this is how I applied physics elements to each object.

var scene = d3.select("a-scene");
var dollars = scene.selectAll("a-scene").data(data)
.enter()
.append('a-entity')
.attr("geometry",{ //assign the collider box.
"primitive: box;
width: 257.9;
height: 25;
depth: 112.16" })
.attr("ammo-controls",{
linearVelocity: "0 0 0",
angularVelocity: "0 60 60",//how much the object will rotate.
restitution: "0.0",//the bounciness of the object.
friction: "2" })
.attr("shadow",{//determine weath
cast: "true",
receive: "true" })
.attr('gltf-model',"#stacks")//assign the 3D model.
.attr('scale',"0.01 0.01 0.01")//scale of the 3d model.
.attr('position', function(d,i){//set x,y,z position.
var height = 20;
var x = i%3 * 3;
var y = parseInt(i/9*0.5+height,10);
var z = parseInt(i/3)%3-22 ;
return x+" "+y+" "+z; });
Generated dollar stacks with D3.js. Attributes are properly assigned in each object.

Adding an Interaction

The overall flow of the interaction in the experience is like this: A user types the number → click enter button → erase the existing dollar stacks previously generated(if there is none, skip this step)→generates number of dollar stacks corresponding to the entered number. Since A-frame uses the HTML tags to build a VR scene, adding and subtracting elements in a <script> tag use the DOM via addEventListener() and querySelector() function. Here is how I add and remove a object when a check box is triggered by a user:

let checkbox = document.querySelector('a-checkbox');          checkbox.addEventListener('change', () => {
var status = checkbox.getAttribute("checked");
if(status === "true") {
var el = document.createElement("a-entity");
el.setAttribute("gltf-model","#female_model");
el.setAttribute("position","-13.474 5.683 -25.878");
el.setAttribute("rotation","0 45 0");
el.setAttribute("scale","10 10 10");
el.setAttribute("id","model");
document.querySelector('a-scene').appendChild(el);
}
else {
var e = document.getElementById("model");
e.parentNode.removeChild(e);
}
});

In the code, I select the <a-checkbox> tag by document.querySelector() fuction and check if the status of the properly changes using addEventListner(). If the status is true, it appends the 3d model to the scene by assigning attributes. If the status is false it removes the element by getElementByID() and removeChild() function.

Conclusion

A data visualization in VR presents a bodily experience, which allows people to have strong physical sensation. The biggest feature of VR is that it can create a simulation which can be as realistic as real life experience. Combining with data visualizations, there are so much potential of presenting an data visualization in a form of immersive experience to people. Throughout this project, I explore the possibility of data visualization in web VR. I will investigate further on building a data visualization in a surrealistic VR environment. Lastly, it was very fun to create the physics system and see how the objects physically interact with each other. You can check out the live demo here(works the best with Oculus Rift) as well!