Skip to content

D3.js Force Layout — Network Graphs and Node Layout

DodaTech Updated 2026-06-28 5 min read

In this tutorial, you will learn about D3.js Force Layout. We cover key concepts, practical examples, and best practices to help you master this topic.

D3.js force layout positions nodes in a network graph by simulating physical forces such as attraction, repulsion, and collision for interactive node-link diagrams.

What You'll Learn

By the end of this guide, you will create a force simulation, add nodes with links, configure charge, link, and collision forces, implement drag behavior, color-code node groups, and animate the simulation.

Why Force Layout Matters

Network visualization reveals relationships. Durga Antivirus Pro uses force layout to map malware propagation paths, showing how an infection spreads from node to node across a network.

flowchart LR
    A[Node Data] --> B[Force Simulation]
    C[Link Data] --> B
    B --> D[Charge Force - Repulsion]
    B --> E[Link Force - Attraction]
    B --> F[Center Force - Gravity]
    B --> G[Collision Force]
    D --> H[Animated SVG]
    E --> H
    F --> H
    G --> H

Basic Force Simulation

var nodes = [
    { id: 'A' }, { id: 'B' }, { id: 'C' },
    { id: 'D' }, { id: 'E' }
];

var links = [
    { source: 'A', target: 'B' },
    { source: 'A', target: 'C' },
    { source: 'B', target: 'D' },
    { source: 'C', target: 'E' },
    { source: 'D', target: 'E' }
];

var simulation = d3.forceSimulation(nodes)
    .force('link', d3.forceLink(links).id(function(d) { return d.id; }))
    .force('charge', d3.forceManyBody().strength(-100))
    .force('center', d3.forceCenter(300, 200));

Rendering the Graph

var svg = d3.select('#graph')
    .append('svg')
    .attr('width', 600)
    .attr('height', 400);

var link = svg.append('g')
    .selectAll('line')
    .data(links)
    .enter()
    .append('line')
    .attr('stroke', '#999')
    .attr('stroke-width', 2);

var node = svg.append('g')
    .selectAll('circle')
    .data(nodes)
    .enter()
    .append('circle')
    .attr('r', 8)
    .attr('fill', '#4ecdc4');

node.append('title')
    .text(function(d) { return d.id; });

simulation.on('tick', function() {
    link.attr('x1', function(d) { return d.source.x; })
        .attr('y1', function(d) { return d.source.y; })
        .attr('x2', function(d) { return d.target.x; })
        .attr('y2', function(d) { return d.target.y; });

    node.attr('cx', function(d) { return d.x; })
        .attr('cy', function(d) { return d.y; });
});

Expected output: 5 circles connected by lines, automatically positioning themselves with physics-based layout.

Dragging Nodes

var drag = d3.drag()
    .on('start', function(event, d) {
        if (!event.active) simulation.alphaTarget(0.3).restart();
        d.fx = d.x;
        d.fy = d.y;
    })
    .on('drag', function(event, d) {
        d.fx = event.x;
        d.fy = event.y;
    })
    .on('end', function(event, d) {
        if (!event.active) simulation.alphaTarget(0);
        d.fx = null;
        d.fy = null;
    });

node.call(drag);

Configuring Forces

simulation
    .force('link', d3.forceLink(links)
        .id(function(d) { return d.id; })
        .distance(100)
        .strength(0.5)
    )
    .force('charge', d3.forceManyBody()
        .strength(-200)
        .distanceMin(20)
        .distanceMax(500)
    )
    .force('center', d3.forceCenter(300, 200))
    .force('collision', d3.forceCollide(15));

Common Mistakes

1. Not Calling simulation.on('tick')

Without the tick handler, nodes and links never update their positions.

Link source and target must reference node IDs or indices. Wrong references cause NaN positions.

3. Infinite Simulation

The simulation stops when alpha reaches 0. For continuous interaction, restart with .alphaTarget(0.3).

4. Nodes Overlapping

Add d3.forceCollide(radius) to prevent nodes from overlapping visually.

Links need an id accessor to match string IDs to node objects. Without it, links fail silently.

Practice Questions

Q1: What forces are commonly used in force simulation? A: Link (connects nodes), charge (repulsion/attraction), center (gravity toward center), and collision (prevents overlap).

Q2: What does the tick event do? A: It fires on every simulation step (typically 60fps), allowing you to update node and link positions in the SVG.

Q3: How do you make nodes draggable? A: Call .call(d3.drag()) on the node selection and handle start, drag, and end events.

Q4: What is alpha in the simulation? A: Alpha is the simulation's energy level. Higher values cause more movement. It decays to 0 as the simulation stabilizes.

Q5: How do you restart a stopped simulation? A: Call simulation.alpha(0.3).restart() to inject energy and resume.

Challenge: Build a network graph representing a social network with 20 nodes and 30 links. Color nodes by community (use groups). Allow dragging. Show node names on hover.

FAQ

What is the difference between forceLink and forceManyBody?

forceLink creates attraction between connected nodes. forceManyBody creates repulsion (or attraction) between all nodes regardless of connection.

How do I stop the simulation?

Call simulation.stop(). The nodes freeze at their current positions.

Can I use images as nodes?

Yes. Use SVG image elements instead of circles in the node selection.

How do I add directional arrows on links?

Define an SVG marker element and reference it with marker-end='url(#arrow)' on the link lines.

What is forceCenter?

It applies a gentle pull toward the specified center point, preventing the graph from drifting off-screen.

Try It Yourself

Build an interactive network graph with drag, hover labels, and color-coded groups.

<!DOCTYPE html>
<html>
<head>
    <title>Force Layout Demo</title>
    <style>
        body { font-family: sans-serif; padding: 20px; background: #1a1a2e; }
        svg { background: #16213e; border-radius: 12px; display: block; margin: auto; }
        .link { stroke: #555; stroke-opacity: 0.6; }
        .node { stroke: #fff; stroke-width: 2; cursor: grab; }
        .label { fill: #ccc; font-size: 11px; pointer-events: none; }
    </style>
</head>
<body>
<svg width="700" height="500" id="graph"></svg>
<script src="https://d3js.org/d3.v7.min.js"></script>
<script>
var nodes = d3.range(15).map(function(i) {
    return { id: 'Node ' + i, group: i % 3 };
});

var links = [];
for (var i = 0; i < 15; i++) {
    for (var j = i + 1; j < 15; j++) {
        if (Math.random() < 0.15) {
            links.push({ source: i, target: j });
        }
    }
}

var svg = d3.select('#graph');
var width = +svg.attr('width');
var height = +svg.attr('height');
var colors = ['#ff6b35', '#4ecdc4', '#45b7d1'];

var link = svg.append('g')
    .selectAll('line').data(links).enter()
    .append('line').attr('class', 'link');

var node = svg.append('g')
    .selectAll('circle').data(nodes).enter()
    .append('circle')
    .attr('class', 'node')
    .attr('r', 10)
    .attr('fill', function(d) { return colors[d.group]; });

var labels = svg.append('g')
    .selectAll('text').data(nodes).enter()
    .append('text')
    .attr('class', 'label')
    .text(function(d) { return d.id; });

var sim = d3.forceSimulation(nodes)
    .force('link', d3.forceLink(links).distance(80))
    .force('charge', d3.forceManyBody().strength(-120))
    .force('center', d3.forceCenter(width/2, height/2))
    .force('collision', d3.forceCollide(15));

sim.on('tick', function() {
    link.attr('x1', function(d) { return d.source.x; })
        .attr('y1', function(d) { return d.source.y; })
        .attr('x2', function(d) { return d.target.x; })
        .attr('y2', function(d) { return d.target.y; });
    node.attr('cx', function(d) { return d.x; }).attr('cy', function(d) { return d.y; });
    labels.attr('x', function(d) { return d.x + 14; }).attr('y', function(d) { return d.y + 4; });
});

var drag = d3.drag()
    .on('start', function(e, d) { if (!e.active) sim.alphaTarget(0.3).restart(); d.fx = d.x; d.fy = d.y; })
    .on('drag', function(e, d) { d.fx = e.x; d.fy = e.y; })
    .on('end', function(e, d) { if (!e.active) sim.alphaTarget(0); d.fx = null; d.fy = null; });
node.call(drag);
</script>
</body>
</html>

What's Next

Visualize hierarchical data with tree layouts.

Hierarchies — Tree and cluster layouts. Geo Maps — Geographic map visualization.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro