Runawaycoder

Blog Posts | Wiki | Projects| Braindumps | About

arbor.js (or how we did those cute graphs!)


We recently released a tool at http://cc.thinkst.com to capture and collect infosec conference details. We commented on it [here]. One of the cooler components of it, is the ability to view the relationships between speakers/researchers who have collaborated. This post is a quick introduction to the library we used to build our graphs, with enough info to get you up and running in minutes.

As I mentioned, we use ArborJS library which is a a graph visualization library using web workers and the popular jQuery.

The API is really well documented [here] but like most people, I learn best by example (and there are precious few of these). Hopefully this post will fill that niche, and by the end of it you should have a basic understanding of how to use arbor.js in your project.

Our Aim:


End Result

We will be building a simple contrived example as seen in the image above.

Project setup :


Create a new html page and include script references to the following libraries (Download them from the links provided and host locally):

jquery : googleapis
arbor.js : github
graphics.js : github
renderer : github

(Feel free to download the arbor.js repository and investigate the source to poke at its innards.)

Next add a canvas element to the body of the page:

<canvas id="viewport" width="800" height="600"></canvas>

Finally (below your canvas) create a script tag:

<script language="javascript" type="text/javascript"></script>

At this point you are now setup and ready to start creating your own graph in arbor.js.

Your page source should look like this at the moment:

Step1 Page

Getting started - Creating a node:


In the script tag below the canvas element add the following code :

var sys = arbor.ParticleSystem(1000, 400,1);
sys.parameters({gravity:true});

This does 2 things: - Initializes the arbor system; - Sets up the renderer to use the canvas element we provided;

The particle system tells arbor how much you want things to move around. (If you set the 3rd parameter to 1, nodes will not move around at all unless you drag and drop them). You can play around with values between 0 and 1 to see how flexible or rigid the graph becomes.

Next add :

sys.renderer = Renderer("#viewport") ;

This initializes the renderer which is used by arbor.js to draw the nodes on screen. If you want more advanced shapes or pictures you need to edit the renderer file to provide this functionality. The file that we linked to provides 2 basic shapes the dot and the rectangle to get you started off.

Next add :

var animals = sys.addNode('Animals',{'color':'red','shape':'dot','label':'Animals'});

In the line above we added a node to the graph, this is the first node and will be our parent node (to which all other nodes are connected to).

The first parameter is the node name and is what is used to reference a node at any later stage. The second parameter is a JS object that describes how the node should look.

In this case we are saying the Animals node should be red in color drawn as a dot and have the label ‘Animals’ as its label. If you save the changes and refresh the page you will see the following image on screen.

First Node

Congratulations you have created your first node! At this point your page source should look like this :

Step2 Page

Linking Nodes:


While creating nodes is useful, it is the links between nodes that give these graphs meaning. We will now introduce relationships between the nodes.

Start by adding two more nodes to the graph as seen below :

var dog = sys.addNode('dog',{'color':'green','shape':'dot','label':'dog'});
var cat = sys.addNode('cat',{'color':'blue','shape':'dot','label':'cat'});

We added a cat and dog with different colors in the code above. If you save refresh the page you will see the following image.

Second Node

To indicate that the dog and cat are animals we connect them using edges as seen in the code below.

sys.addEdge(animals, dog);
sys.addEdge(animals, cat);

If you save your changes and refresh you will now see that the 3 nodes are connected by edges. When calling sys.addEdge you are specifying which 2 nodes you want to connect. If you wish to customize how the line is drawn you can edit the renderer to draw custom code.

Third Node

At this point your page source should look like this :

Step3 Page

Getting things done faster and easier


Our methods so far have been very manual and error prone. Arbor provides a method called grafting help automate this.

To use this method you provide arbor with a json object that contains all the nodes and edges that you want to add to screen. Arbor then adds them to the graph correctly.

(In our example we are hardcoding the input data. The graphs on http://cc.thinkst.com achieve this with an ajax call to our backend that returns the data in the correct format) :

Add the following json to the script tag in your page:

var data = {
nodes:{
    animals:{'color':'red','shape':'dot','label':'Animals'},
    dog:{'color':'green','shape':'dot','label':'dog'},
    cat:{'color':'blue','shape':'dot','label':'cat'}
},
edges:{
    animals:{ dog:{}, cat:{} }}
};

As you can see the nodes are created the same way as before with all their data attributes. The edges are represented slightly differently: they are structured in such a way that the parent is at the top and all children are contained within. If it had to be show in a tree it would look like this :

animals                      The Parent node
dog               Child node
cat    Child node

To use this json in the code we need to do the following :

Replace all the code in your script tag with :

var sys = arbor.ParticleSystem(1000, 400,1);
sys.parameters({gravity:true});
sys.renderer = Renderer("#viewport") ;
var data = {
    nodes:{
        animals:{'color':'red','shape':'dot','label':'Animals'},
        dog:{'color':'green','shape':'dot','label':'dog'},
        cat:{'color':'blue','shape':'dot','label':'cat'}
    },
    edges:{
        animals:{ dog:{}, cat:{} }
    }
};
sys.graft(data);

As you can see we have still have to initialize our arbor system and setup the renderer but instead of manually creating each node and linking them with edges we simply call sys.graft with our json object.

Extending Existing data:


Finally it would be nice to hook in more data onto our existing graph as it becomes available. To simulate this we will set a timeout in javascript for 10 seconds , after this our code will be called to add more data to the graph.

Add the code below after your sys.graft(data) :

setTimeout(function(){
    var postLoadData = {
        nodes:{
            joe:{'color':'orange','shape':'dot','label':'joe'},
            fido:{'color':'green','shape':'dot','label':'fido'},
            fluffy:{'color':'blue','shape':'dot','label':'fluffy'}
        },
        edges:{
            dog:{ fido:{} },
            cat:{ fluffy:{} },
            joe:{ fluffy:{},fido:{} }
        }
    };
    sys.graft(postLoadData);
},10000);

What we are doing above is adding 3 new nodes and then setting up new edges for these nodes. We indicate this in the edges section.

If we didn’t have the sys.graft we would have to iterate over every node that’s currently on screen and check if we need to add new nodes and edges to it. If you save and refresh the page you see a image similar to the one below.

Last Node

Finally at this point your page source should look like this :

Final Page

The project itself is worth checking out and goes much deeper. Check out the code they have provided on github.

If you find any issues please catch me in #zacon on atrum or on twitter(@RC1140).