HAHS.
Back to Catalog

Radar Chart

chart

Also known as: spider chart, web chart, star chart, polar chart, Kiviat diagram

CompareShow deviationShow distribution NumericalCategorical Arc/Radial

Description

A radar chart (also called a spider chart or web chart) displays multivariate data on a set of equi-angular axes radiating from a center point. Each axis represents a different variable, and data values are plotted along these axes at a distance from the center proportional to their magnitude. The plotted points are connected to form a closed polygon, whose shape reveals the overall “profile” of the data entity. Multiple entities can be overlaid as semi-transparent polygons to enable comparison.

Radar charts are most effective when the goal is to compare the overall profile or balance of an entity across several dimensions. They answer questions like “Is this athlete well-rounded or specialized?” or “How does Product A’s feature set compare to Product B’s?” The enclosed polygon area gives an intuitive sense of overall magnitude, while the shape reveals strengths and weaknesses.

However, radar charts carry significant perceptual limitations. The area of the polygon depends on the ordering of axes, which is arbitrary — rearranging axes produces a different shape and area for the same data. Human perception of angles and radial distances is less accurate than perception of aligned positions, making precise comparison difficult. For these reasons, radar charts are best used for quick qualitative comparison of profiles rather than precise quantitative analysis, and they work well with a small number of entities (2-4) and a moderate number of axes (5-10).

Radar Chart — interactive example

When to Use

  • Comparing the multi-dimensional profile of 2-4 entities (e.g., product features, player stats, skill assessments)
  • Showing whether an entity is balanced across dimensions or skewed toward certain attributes
  • Displaying performance scorecards or rating summaries where the overall shape matters
  • Creating compact summaries for dashboards when the audience is familiar with the format

When NOT to Use

  • When precise value comparison across entities is needed — use a grouped bar chart or parallel coordinates instead
  • When you have more than 4-5 overlapping entities — the chart becomes unreadable; use parallel coordinates or small multiples
  • When axes have very different scales or units — normalization is required first, and even then, comparison can be misleading
  • When the number of variables is fewer than 3 — the polygon degenerates; use a simpler chart
  • When the ordering of axes significantly affects the visual story — consider parallel coordinates where order is more explicit

Anatomy

  • Axes (spokes): Lines radiating from the center, each representing one variable
  • Gridlines (web): Concentric polygons or circles marking scale intervals along each axis
  • Data polygon: The closed shape connecting plotted values across all axes for one entity
  • Fill area: Semi-transparent fill inside the polygon to make the shape more visible
  • Center point: The origin (typically zero or the minimum value)
  • Axis labels: Variable names at the outer end of each spoke
  • Legend: Identifies which polygon belongs to which entity when multiple are overlaid

Variations

  • Filled radar: Polygons are filled with semi-transparent color, emphasizing area and making overlap visible
  • Radar with data points: Dots are placed at each axis value, making individual readings easier to identify
  • Small-multiple radar: Instead of overlaying polygons, each entity gets its own small radar chart in a grid, avoiding overlap clutter
  • Polar area chart (Nightingale rose): Similar radial layout but uses wedge area rather than point distance, related but distinct
  • Radar with min-max bands: A shaded band shows the range (min to max) across a group, with a line for the average

Code Reference

// D3 radar chart (simplified)
import * as d3 from "d3";

const axes = ["Speed", "Power", "Defense", "Stamina", "Skill", "Agility"];
const angleSlice = (2 * Math.PI) / axes.length;
const rScale = d3.scaleLinear().domain([0, 100]).range([0, radius]);

const line = d3.lineRadial()
  .radius(d => rScale(d.value))
  .angle((d, i) => i * angleSlice)
  .curve(d3.curveLinearClosed);

const svg = d3.select("#chart").append("svg")
  .attr("viewBox", [-width/2, -height/2, width, height]);

// Draw gridlines
[20, 40, 60, 80, 100].forEach(level => {
  svg.append("polygon")
    .attr("points", axes.map((_, i) => {
      const r = rScale(level);
      return [r * Math.cos(i * angleSlice - Math.PI/2),
              r * Math.sin(i * angleSlice - Math.PI/2)].join(",");
    }).join(" "))
    .attr("fill", "none").attr("stroke", "#ccc");
});

// Draw data polygon
svg.append("path").datum(playerStats)
  .attr("d", line).attr("fill", "steelblue").attr("fill-opacity", 0.3)
  .attr("stroke", "steelblue").attr("stroke-width", 2);