Network graph

네트워크 그래프

Shows relationships as nodes (dots) and links (lines) between them. Positions come from a force simulation, not from any value.

Also known as: Node-link diagram노드-링크 다이어그램
···
html
<div class="viz">
  <svg id="svg"></svg>
</div>
css
:root{--viz-4:#e8a23a;--viz-5:#4098d7}
body{display:block}
.viz{position:relative;width:100%;height:100%}
svg{display:block;width:100%;height:100%;overflow:visible}
.edge{stroke:var(--line);stroke-width:1.2}
.ring{fill:var(--surface)}
js
var svg = document.getElementById('svg');
var NS = 'http://www.w3.org/2000/svg';
function se(t){ return document.createElementNS(NS,t); }
function sa(e,o){ for (var k in o) e.setAttribute(k,o[k]); }
function R(a,b){ return a + Math.random()*(b-a); }

var GROUPS = ['var(--accent)','var(--viz-4)','var(--viz-5)'];
var N = 11;
var nodes = [];
for (var i=0;i<N;i++) nodes.push({ x:R(0.25,0.75), y:R(0.25,0.75), vx:0, vy:0, g:i%3 });
var EDGES = [[0,1],[0,2],[1,2],[1,3],[3,4],[4,5],[3,5],[5,6],[6,7],[6,8],[7,8],[8,9],[9,10],[2,4],[0,5]];
var restLen = 0.22;
var W=300, H=200;

function measure(){
  var r = svg.getBoundingClientRect();
  W = Math.max(r.width,10); H = Math.max(r.height,10);
  sa(svg,{ viewBox: '0 0 ' + W + ' ' + H });
}

function physics(){
  for (var i=0;i<N;i++){
    for (var j=i+1;j<N;j++){
      var dx=nodes[j].x-nodes[i].x, dy=nodes[j].y-nodes[i].y;
      var d2=dx*dx+dy*dy+0.0006;
      var d=Math.sqrt(d2);
      var f=0.00028/d2;
      var fx=dx/d*f, fy=dy/d*f;
      nodes[i].vx-=fx; nodes[i].vy-=fy;
      nodes[j].vx+=fx; nodes[j].vy+=fy;
    }
  }
  EDGES.forEach(function(e){
    var a=nodes[e[0]], b=nodes[e[1]];
    var dx=b.x-a.x, dy=b.y-a.y;
    var d=Math.sqrt(dx*dx+dy*dy)+0.0006;
    var diff=(d-restLen)*0.02;
    var fx=dx/d*diff, fy=dy/d*diff;
    a.vx+=fx; a.vy+=fy;
    b.vx-=fx; b.vy-=fy;
  });
  nodes.forEach(function(n){
    n.vx += (0.5-n.x)*0.004;
    n.vy += (0.5-n.y)*0.004;
    n.vx*=0.82; n.vy*=0.82;
    n.x += n.vx; n.y += n.vy;
    n.x = Math.max(0.05,Math.min(0.95,n.x));
    n.y = Math.max(0.05,Math.min(0.95,n.y));
  });
}

function draw(){
  svg.innerHTML = '';
  var padL=12, padR=12, padT=12, padB=12;
  var pw=W-padL-padR, ph=H-padT-padB;
  function X(n){ return padL+n.x*pw; }
  function Y(n){ return padT+n.y*ph; }
  EDGES.forEach(function(e){
    var a=nodes[e[0]], b=nodes[e[1]];
    var line = se('line');
    sa(line,{x1:X(a),y1:Y(a),x2:X(b),y2:Y(b),class:'edge'});
    svg.appendChild(line);
  });
  nodes.forEach(function(n){
    var ring = se('circle');
    sa(ring,{cx:X(n),cy:Y(n),r:6.4,class:'ring'});
    svg.appendChild(ring);
    var dot = se('circle');
    sa(dot,{cx:X(n),cy:Y(n),r:4.6,fill:GROUPS[n.g]});
    svg.appendChild(dot);
  });
}

function loop(){
  physics();
  draw();
  requestAnimationFrame(loop);
}

new ResizeObserver(function(){ measure(); }).observe(svg);
measure();
loop();
setInterval(function(){ restLen = R(0.15,0.3); }, 4200);

This demo runs a tiny physics simulation every frame — every node pushes every other node away, while linked nodes pull toward a target link length, and the layout settles wherever those forces balance. Frequently connected nodes cluster together naturally; barely connected ones drift to the edges.

The most common misreading: treating node position or distance as if it encodes a value. It doesn't. A different layout algorithm — or even the same one with different starting positions — arranges identical data into a completely different shape. What this chart honestly conveys is "what's connected to what" and rough cluster structure, not precise numeric comparison.

Once there are dozens or hundreds of nodes, the lines tangle into a "hairball." At that point, filter down to the core clusters, weight links by thickness, or switch to an adjacency matrix instead.

When to use

Use it to show relationship structure and clustering. For precise value comparison, reach for a different chart.