Radar chart

레이더 차트

Spreads several axes out radially and connects the values into a polygon — good for showing one item’s multi-dimensional "profile."

Also known as: Spider chart방사형 차트
···
html
<div class="viz">
  <svg id="svg"></svg>
  <div class="legend" id="lg"></div>
</div>
css
:root{--viz-4:#e8a23a;--viz-5:#4098d7}
body{display:block}
.viz{display:flex;flex-direction:column;width:100%;height:100%}
svg{flex:1;min-height:0;display:block;width:100%;overflow:visible}
.legend{flex:none;display:flex;flex-wrap:wrap;gap:4px 10px;justify-content:center;padding:2px 4px 4px;font:600 9px/1 system-ui,sans-serif;color:var(--muted)}
.legend b{display:inline-flex;align-items:center;gap:3px;font-weight:600}
.legend i{width:7px;height:7px;border-radius:2px;display:inline-block}
.grid{fill:none;stroke:var(--line);stroke-width:1}
.spoke{stroke:var(--line);stroke-width:1}
js
var svg = document.getElementById('svg');
var lg = document.getElementById('lg');
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 AXES = ['속도','안정성','디자인','가격','지원','확장성'];
var A = AXES.length;
var SER = [
  { name: '현재', color: 'var(--accent)' },
  { name: '경쟁작', color: 'var(--viz-4)' }
];
lg.innerHTML = SER.map(function(s){ return '<b><i style="background:'+s.color+'"></i>'+s.name+'</b>'; }).join('');

var cur = SER.map(function(){ return AXES.map(function(){ return 55; }); });
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 pt(cx,cy,rad,ang){ return { x: cx+Math.cos(ang)*rad, y: cy+Math.sin(ang)*rad }; }

function draw(){
  svg.innerHTML = '';
  var cx=W/2, cy=H/2;
  var rad = Math.max(16, Math.min(W,H)/2 - 22);
  var fs = Math.max(8, Math.min(11, Math.min(W,H)/20));

  [0.33,0.66,1].forEach(function(f){
    var pts = [];
    for (var i=0;i<A;i++){ var a = -Math.PI/2 + i*(2*Math.PI/A); var p = pt(cx,cy,rad*f,a); pts.push(p.x+','+p.y); }
    var poly = se('polygon');
    sa(poly,{ points: pts.join(' '), class:'grid' });
    svg.appendChild(poly);
  });

  for (var i=0;i<A;i++){
    var a = -Math.PI/2 + i*(2*Math.PI/A);
    var edge = pt(cx,cy,rad,a);
    var sp = se('line');
    sa(sp,{ x1:cx, y1:cy, x2:edge.x, y2:edge.y, class:'spoke' });
    svg.appendChild(sp);
    var lp = pt(cx,cy,rad+11,a);
    var t = se('text');
    sa(t,{ x:lp.x, y:lp.y+fs*0.3, 'text-anchor':'middle', fill:'var(--muted)', 'font-size':fs, 'font-weight':600 });
    t.textContent = AXES[i];
    svg.appendChild(t);
  }

  cur.forEach(function(vals, si){
    var pts = vals.map(function(v,i){ var a=-Math.PI/2+i*(2*Math.PI/A); return pt(cx,cy,rad*(v/100),a); });
    var d = pts.map(function(p,i){ return (i===0?'M':'L')+p.x.toFixed(1)+','+p.y.toFixed(1); }).join(' ')+' Z';
    var fill = se('path');
    sa(fill,{ d:d, fill:SER[si].color, 'fill-opacity':0.16, stroke:SER[si].color, 'stroke-width':2, 'stroke-linejoin':'round' });
    svg.appendChild(fill);
  });
}

function animate(target){
  var from = cur.map(function(a){ return a.slice(); });
  var t0 = performance.now();
  function step(t){
    var p = Math.min(1,(t-t0)/750);
    var e = 1-Math.pow(1-p,3);
    cur = from.map(function(row,si){ return row.map(function(v,i){ return v+(target[si][i]-v)*e; }); });
    draw();
    if (p<1) requestAnimationFrame(step);
  }
  requestAnimationFrame(step);
}

new ResizeObserver(function(){ measure(); draw(); }).observe(svg);
measure();
draw();
animate(SER.map(function(){ return AXES.map(function(){ return R(35,92); }); }));
setInterval(function(){
  animate(SER.map(function(){ return AXES.map(function(){ return R(25,95); }); }));
}, 3600);

With roughly 3-8 axes, it summarizes "how strong is this item on each criterion" as a single shape. A polygon's silhouette — spiky here, flat there — reads strengths and weaknesses intuitively, which is why it shows up so often for product comparisons and skill profiles.

Two real traps. First, reordering the axes changes how "spiky" the same data looks — anything meant to be compared must share the exact same axis order. Second, it's not built for precise reading — judging the exact distance between a near axis and a far one by eye is hard, so give exact values in a table alongside it.

Overlapping series cap out around 2-3. Past that, it stops being clear which line belongs to which polygon.

When to use

Use it to summarize one item across several criteria (3-8) as a profile. Pair it with a table when precision matters.