Skip to content

D3 Force-Directed Layout

Overview

The D3Force layout is a force-directed layout based on d3-force. It simulates physical forces (such as attraction, repulsion, collision, etc.) to make the graph reach a stable state with minimal energy.

The main features of this layout are:

  1. Automatic arrangement: No need to manually set node positions, the system will automatically find suitable positions
  2. Real-time adjustment: When you drag a node, other nodes will adjust their positions in real time
  3. Flexible configuration:
    • You can adjust the attraction and repulsion between nodes
    • You can set the ideal length of edges
    • You can fix the positions of important nodes
  4. Animation effect: Nodes move with smooth animation, making changes more natural
D3Force layout diagram

Core Concepts

Force System

The D3Force layout simulates five different forces to achieve automatic layout. Imagine a physical world where these forces act simultaneously and eventually reach equilibrium:

force

Note: The arrows of different colors in the diagram represent different types of forces. In the actual layout, these forces are invisible and also affected by other forces.

  • Link Force: Imagine nodes connected by rubber bands, which pull connected nodes to a suitable distance. The tightness of the rubber band is the force strength, and the ideal length is the distance we set.
  • Many-Body Force: Similar to magnets, it allows all nodes to attract or repel each other. When the force strength is negative, nodes repel each other (like like poles of magnets); when positive, they attract (like opposite poles). This force determines the density of the graph.
  • Center Force: Like all nodes are tied to the center of the canvas by an invisible string. This force prevents nodes from drifting too far and keeps the graph centered.
  • Collision Force: Treats nodes as solid balls. When nodes get too close, they automatically bounce apart. This force mainly prevents node overlap and improves readability.
  • Radial Force: Imagine an invisible ring that attracts nodes to the ring. By setting the radius and force strength, nodes can form a beautiful circular layout.

Iteration System

Layout calculation is an iterative process with two key concepts:

Alpha Value (Energy)

Like the “energy” of the layout, it determines how vigorously nodes move:

  • Initial state: Alpha = 1, nodes move vigorously
  • During calculation: Alpha gradually decreases, node movement slows
  • End state: When Alpha < alphaMin, nodes stop moving

Iterations

Controls the number of times forces are applied in each calculation:

  • Effect: The larger the value, the more precise the layout, but the slower the computation
  • Adjustment:
    • Simple graphs: use the default value
    • Complex graphs: increase the number of iterations as needed
    • Real-time interaction: use fewer iterations

Tip: Iterations and alpha value work together. Increasing iterations makes each step more precise, while alpha controls the overall progress.

Options

Property Description Type Default Required
type Layout type string ‘d3-force’ ✓
centerX Layout center x coordinate number width / 2
centerY Layout center y coordinate number height / 2
centerStrength Center force strength number -
nodeSize Node size (diameter), for collision detection number | ((node, index, nodes) => number) 10
nodeSpacing Extra spacing between nodes number | ((node, index, nodes) => number) 0
iterations Number of force iterations, higher is more precise number -
onTick Callback for each iteration, for real-time results (data: LayoutMapping) => void -
forceSimulation Custom force simulation, defaults to d3.js method Simulation<NodeDatum, EdgeDatum> -
randomSource Function to generate random numbers () => number -

Iteration Control

Property Description Type Default Required
alpha Current convergence threshold, controls activity number 1
alphaMin Minimum threshold to stop, when alpha < this, stop number 0.001
alphaDecay Decay rate of alpha, [0, 1], 0.028 ≈ 300 iterations number 0.028
alphaTarget Target alpha, system tries to converge to this value number 0
velocityDecay Velocity decay factor, higher means slower movement number 0.4

Force Model Options

Property Description Type Default Required
link.id Function to generate edge id (edge, index, edges) => string (e) => e.id
link.distance Ideal edge length number | ((edge, index, edges) => number) 30
link.strength Force strength, higher means closer to ideal number | ((edge, index, edges) => number) 1
link.iterations Number of link force iterations number 1

Many-Body Force (manyBody)

Property Description Type Default Required
manyBody.strength Force strength, negative for repulsion, positive for attraction number | ((node, index, nodes) => number) -30
manyBody.theta Barnes-Hut accuracy, smaller is more accurate number 0.9
manyBody.distanceMin Minimum distance, prevents excessive force number 1
manyBody.distanceMax Maximum distance, beyond which no force is applied number Infinity

Center Force (center)

Property Description Type Default Required
center.x Center x coordinate number -
center.y Center y coordinate number -
center.strength Force strength, higher means closer to center number -

Note: In 5.1, centerX / centerY are the recommended shortcut fields. If you configure the center force directly, it follows the native d3-force style.

Collision Force (collide)

Property Description Type Default Required
collide.radius Collision radius, nodes repel if closer number | ((node, index, nodes) => number) 10
collide.strength Force strength, higher means stronger repulsion number 1
collide.iterations Number of collision iterations number 1

Radial Force (radial)

Property Description Type Default Required
radial.strength Force strength, higher means closer to radius number | ((node, index, nodes) => number) 0.1
radial.radius Target radius, nodes are attracted to circle number | ((node, index, nodes) => number) 100
radial.x Center x coordinate number 0
radial.y Center y coordinate number 0

X Axis Force (x)

Property Description Type Default Required
x.strength Force strength in x direction number | ((node, index, nodes) => number) -
x.x Target x coordinate, nodes attracted here number | ((node, index, nodes) => number) -

Y Axis Force (y)

Property Description Type Default Required
y.strength Force strength in y direction number | ((node, index, nodes) => number) -
y.y Target y coordinate, nodes attracted here number | ((node, index, nodes) => number) -

Code Examples

Prevent Node Overlap

{
  layout: {
    type: 'd3-force',
    collide: {
      // Prevent nodes from overlapping by specifying a collision radius for each node.
      radius: (d) => d.size / 2,
    },
  },
}

See Example - Prevent Node Overlap in Force-Directed Layout

Team Clustering Layout

This example shows how to use force-directed layout for team clustering, where nodes of different teams automatically cluster together.


import { Graph } from '@antv/g6';

const graph = new Graph({
  container: 'container',
  width: 500,
  height: 250,
  autoFit: 'view',
  data: {
    nodes: [
      // Team A
      { id: 'A1', team: 'A', label: 'A1', size: 30 },
      { id: 'A2', team: 'A', label: 'A2', size: 20 },
      { id: 'A3', team: 'A', label: 'A3', size: 20 },
      { id: 'A4', team: 'A', label: 'A4', size: 20 },
      // Team B
      { id: 'B1', team: 'B', label: 'B1', size: 30 },
      { id: 'B2', team: 'B', label: 'B2', size: 20 },
      { id: 'B3', team: 'B', label: 'B3', size: 20 },
      { id: 'B4', team: 'B', label: 'B4', size: 20 },
      // Team C
      { id: 'C1', team: 'C', label: 'C1', size: 30 },
      { id: 'C2', team: 'C', label: 'C2', size: 20 },
      { id: 'C3', team: 'C', label: 'C3', size: 20 },
      { id: 'C4', team: 'C', label: 'C4', size: 20 },
    ],
    edges: [
      // Team A internal connections
      { source: 'A1', target: 'A2' },
      { source: 'A1', target: 'A3' },
      { source: 'A1', target: 'A4' },
      // Team B internal connections
      { source: 'B1', target: 'B2' },
      { source: 'B1', target: 'B3' },
      { source: 'B1', target: 'B4' },
      // Team C internal connections
      { source: 'C1', target: 'C2' },
      { source: 'C1', target: 'C3' },
      { source: 'C1', target: 'C4' },
      // Few connections between teams
      { source: 'A1', target: 'B1' },
      { source: 'B1', target: 'C1' },
    ],
  },
  node: {
    style: {
      size: (d) => d.size,
      fill: (d) => {
        // Different colors for different teams
        const colors = {
          A: '#FF6B6B',
          B: '#4ECDC4',
          C: '#45B7D1',
        };
        return colors[d.team];
      },
      labelText: (d) => d.label,
      labelPlacement: 'center',
      labelFill: '#fff',
    },
  },
  edge: {
    style: {
      stroke: '#aaa',
    },
  },
  layout: {
    type: 'd3-force',
    // Configure link force - nodes within the same team are closer
    link: {
      distance: (d) => {
        // Shorter distance within the same team
        if (d.source.team === d.target.team) return 50;
        // Longer distance between teams
        return 200;
      },
      strength: (d) => {
        // Stronger connection within the same team
        if (d.source.team === d.target.team) return 0.7;
        // Weaker connection between teams
        return 0.1;
      },
    },
    // Configure many-body force - control repulsion between nodes
    manyBody: {
      strength: (d) => {
        // Team leader nodes (ending with 1) have stronger repulsion
        if (d.label.endsWith('1')) return -100;
        return -30;
      },
    },
    // Configure collision force - prevent node overlap
    collide: {
      radius: 35,
      strength: 0.8,
    },
    // Configure center force - keep the graph centered
    center: {
      strength: 0.05,
    },
  },
  behaviors: ['drag-element-force'],
});

graph.render();
Show full code
import { Graph } from '@antv/g6';

// Create mock data with nodes from different teams
const data = {
  nodes: [
    // Team A
    { id: 'A1', team: 'A', label: 'A1', size: 30 },
    { id: 'A2', team: 'A', label: 'A2', size: 20 },
    { id: 'A3', team: 'A', label: 'A3', size: 20 },
    { id: 'A4', team: 'A', label: 'A4', size: 20 },
    // Team B
    { id: 'B1', team: 'B', label: 'B1', size: 30 },
    { id: 'B2', team: 'B', label: 'B2', size: 20 },
    { id: 'B3', team: 'B', label: 'B3', size: 20 },
    { id: 'B4', team: 'B', label: 'B4', size: 20 },
    // Team C
    { id: 'C1', team: 'C', label: 'C1', size: 30 },
    { id: 'C2', team: 'C', label: 'C2', size: 20 },
    { id: 'C3', team: 'C', label: 'C3', size: 20 },
    { id: 'C4', team: 'C', label: 'C4', size: 20 },
  ],
  edges: [
    // Team A internal connections
    { source: 'A1', target: 'A2' },
    { source: 'A1', target: 'A3' },
    { source: 'A1', target: 'A4' },
    // Team B internal connections
    { source: 'B1', target: 'B2' },
    { source: 'B1', target: 'B3' },
    { source: 'B1', target: 'B4' },
    // Team C internal connections
    { source: 'C1', target: 'C2' },
    { source: 'C1', target: 'C3' },
    { source: 'C1', target: 'C4' },
    // Few connections between teams
    { source: 'A1', target: 'B1' },
    { source: 'B1', target: 'C1' },
  ],
};

const graph = new Graph({
  container: 'container',
  data,
  node: {
    style: {
      size: (d) => d.size,
      fill: (d) => {
        // Different colors for different teams
        const colors = {
          A: '#FF6B6B',
          B: '#4ECDC4',
          C: '#45B7D1',
        };
        return colors[d.team];
      },
      labelText: (d) => d.label,
      labelPlacement: 'center',
      labelFill: '#fff',
    },
  },
  edge: {
    style: {
      stroke: '#aaa',
    },
  },
  layout: {
    type: 'd3-force',
    // Configure link force - nodes within the same team are closer
    link: {
      distance: (d) => {
        // Shorter distance within the same team
        if (d.source.team === d.target.team) return 50;
        // Longer distance between teams
        return 200;
      },
      strength: (d) => {
        // Stronger connection within the same team
        if (d.source.team === d.target.team) return 0.7;
        // Weaker connection between teams
        return 0.1;
      },
    },
    // Configure many-body force - control repulsion between nodes
    manyBody: {
      strength: (d) => {
        // Team leader nodes (ending with 1) have stronger repulsion
        if (d.label.endsWith('1')) return -100;
        return -30;
      },
    },
    // Configure collision force - prevent node overlap
    collide: {
      radius: 35,
      strength: 0.8,
    },
    // Configure center force - keep the graph centered
    center: {
      strength: 0.05,
    },
  },
  behaviors: ['drag-element-force'],
});

graph.render();

Main configuration notes:

  • link.distance: Shorter within teams, longer between teams
  • link.strength: Stronger within teams, weaker between teams
  • manyBody.strength: Controls repulsion between nodes
  • collide: Prevents node overlap
  • center: Keeps the layout centered

See also Customize parameters for different nodes.

© Copyright 2026 Ant Group Co., Ltd..备案号:京ICP备15032932号-38
Site search
Documentation and examplesType to start searching

Search documentation, APIs, and examples

↑↓Select↵OpenEscClose