The goal of this post is to take what we covered earlier and make a simple working example of a force-directed graph.

We’ll start with just getting the nodes working, adding the links afterwards to complete the example. Find what we’ll be building here.

So open up a text file in your favourite editor. Fire up a local server.  Find yourself a cool drink or two.

Ready? Let’s begin.

Nodes

First thing we’ll do is set up a basic skeleton for our force directed graph. This will look familiar to you if you’ve played around with d3 before.

We also add some CSS to make our nodes and lines look nice.

Start your force-graph journey by creating some data for the nodes. We’re only going to go with six nodes.

Then set up the simulation with the nodes data.

Next we’ll add two forces to the simulation: a centering force and a charge force.

We’ll use the centering force to drive all the nodes towards the centre of the svg element. The force is implemented for us with a prepacked function called d3.forceCenter(), so we don’t really have to give this too much thought. We can just feed in the x and y coordinates of the centre of the svg element as function parameters and let it do its stuff.

The charge force treats each node as a charged particle, so that nodes will repel each other when they get too close together. The idea is that they’ll end up spaced apart naturally. Again there’s an already-written function available to implement this: d3.forceManyBody().

It’s easy to add these forces to the simulation through simulation.force(). The first argument names the force (make it whatever name you like) and the second argument specifies what force you want.

Next thing to do is to draw the circles inside our svg element. There’ll be one circle for each node, and by default they’ll start off all bunched up in the top left corner.

There’s just one thing missing before we’re done with this stage.

What’s lacking is a way to update the locations of the circles after every tick. You see, the simulation updates the position of nodes, but it doesn’t translate that across to svg circle representations. That’s our job.

We can solve this problem by writing some code to update circle locations in a function, and then call this function on every tick of the simulation.

Let’s write this function and call it tickActions().

We instruct the simulation to apply our function tickActions() on every tick through the simulation.on() method.

 

You should be able to see something happening now on your force directed graph – six red dots in a rough pentagon shape.  It’s strangely satisfying.

No links yet – we’ll add those now!

Links

Start by specifying the link data:

Next create the link force with the d3.forceLink() function. Add the link data to the link force by putting it as the argument to the function, so we’ll do that.

We also need to specify an accessor function through link.id(). Basically this is a function that tells the simulation which variable in the nodes_data array corresponds to the text in the links_data array.

Because our nodes each have a “name” attribute that we’re using to create the links, we use the link.id() function to tell the accessor function to use the “name” attribute to create our links for us.

For a better explanation of that read the documentation of link.id(). I tried.

Anyway, let’s create the link force:

then add it to the simulation:

Similar to the nodes, we need to physically draw the links on the page.

We also need to update their positions in the tickActions function we created earlier.

Say you have a line that links together two nodes. In the tickActions we’d instruct one end of the line to follow one node around (the “source” node), and the other end to follow around the other node (the “target” node). The names come from our links data (stored in links_data) – whatever you called it in that applies to the tickActions() function.

You’ll be able to see the complete simulation now – nodes and links. Here’s the link to what it should look like.

It’d be nice to be able to drag it round, though.

You’ll learn how to do that in the next part!

 

Hope you found that useful! Click here to view to the rest of the force directed graph series.

7 thoughts on “Force directed graph minimal working example

  1. Nice tutorial, thank you!
    There is a typo in line 18, ‘script’ does have a closing tag, this make Chrome to fail.

  2. Thank you for these tutorials…they are invaluable. One issue I ran into is that

    simulation.on(“tick”, tickActions );

    did not work for me, but

    force.on(‘tick’, tickActions); .. which is from a v3 script I am updating..

    did work. Any ideas why?

    Thanks

    1. Hi Andy, thanks for reading. It’s hard to tell what’s up based on what you’ve written alone, but feel free to email me a code sample and I’ll take a look at the problem, and hopefully with some context around it I’ll be able to help out.

  3. Hi Thanks for the tutorial .

    In this line ,

    var link_force = d3.forceLink(links_data)
    .id(function(d) { return d.name; })

    The parameter d is the node_data ? If it is , does it come here from the nodes defined before ? I am not clear about this . I saw the link that you mentioned , It is still confusing. can you help me out?

    1. The parameter d will refer to the node_data after we add it to our simulation.

      Suppose we set up three simulations called sim1, sim2 and sim3, and set each one up with its own set of nodes – nodes1, nodes2 and nodes3. The d in link_force will depend on what simulation you add it to – if you add it to sim2, then d will refer to nodes2.

      What I found really helpful is inserting lots of debugger statements throughout the code, and then stepping through it line by line. You’ll be able to see the value of each variable at each step.

Leave a Reply

Your email address will not be published. Required fields are marked *