Sunburst Diagram
chartAlso known as: sunburst chart, radial treemap, ring chart, multi-level pie chart, nested ring chart
Description
A sunburst diagram displays hierarchical data as a series of concentric rings. The innermost ring represents the root of the hierarchy, and each successive ring outward represents one deeper level. Each arc within a ring represents a node, and its angular width is proportional to its value (or the sum of its children’s values). The parent-child relationship is shown spatially: a child arc is positioned within the angular span of its parent arc, creating a clear visual nesting.
The sunburst format excels at showing part-to-whole relationships within a hierarchy. At any level, the arcs sum to the full circle (or to their parent’s arc), making it easy to see how sub-categories compose a larger category. The radial layout is compact and aesthetically distinctive, often making it a good choice for presentations and dashboards where visual impact matters.
Sunbursts work best with hierarchies of 2-4 levels deep and moderate branching. Very deep hierarchies produce thin outer rings that are hard to read, and very wide hierarchies (many siblings) create narrow arcs that are difficult to distinguish. Interactive features like click-to-zoom (drilling into a sub-tree by making it the new root) dramatically expand the usable range, allowing exploration of large hierarchies that would be unreadable in a static view.
When to Use
- Displaying hierarchical data with part-to-whole relationships (e.g., file system usage, organizational budgets, taxonomy breakdowns)
- Exploring multi-level categorical compositions interactively (click to drill into a sub-category)
- Showing how sub-categories nest within parent categories across 2-4 levels
- Creating visually engaging representations of tree structures for presentations
When NOT to Use
- When precise size comparison between arcs is needed — arc length and area are hard to compare accurately; use a treemap or bar chart instead
- When the hierarchy is very deep (>5 levels) — outer rings become unreadably thin; use a tree diagram or indented list
- When there is no meaningful hierarchy — don’t force flat data into rings; use a pie chart or bar chart
- When exact values matter more than structure — use a table or stacked bar chart
- When the hierarchy is very wide (many siblings per node) — narrow arcs become indistinguishable; use a treemap which handles wide hierarchies better
Anatomy
- Center circle: Represents the root node (sometimes left empty or used as a label)
- Rings: Concentric bands, each representing one level of the hierarchy
- Arcs (segments): Individual pieces within each ring; angular width is proportional to the node’s value
- Color encoding: Applied to arcs, typically by top-level category (so all descendants share the same hue family)
- Radial depth: Distance from center encodes depth in the hierarchy
- Labels: Text placed inside arcs when space permits, or shown in tooltips
Variations
- Zoomable sunburst: Clicking an arc zooms in, making that node the new center and its children fill the rings; essential for large hierarchies
- Icicle diagram: The rectangular (non-radial) equivalent; each level is a horizontal row of rectangles instead of a ring of arcs; easier for precise comparison
- Sunburst with breadcrumb trail: A breadcrumb bar shows the path from root to the hovered node, aiding navigation
- Partial sunburst: Only a portion of the circle is used (e.g., 180 degrees), leaving space for labels or other elements
- Animated sunburst: Transitions between different hierarchical datasets with smooth arc morphing
Code Reference
// D3 sunburst diagram
import * as d3 from "d3";
const root = d3.hierarchy(data).sum(d => d.value).sort((a, b) => b.value - a.value);
const partition = d3.partition().size([2 * Math.PI, radius]);
partition(root);
const arc = d3.arc()
.startAngle(d => d.x0).endAngle(d => d.x1)
.innerRadius(d => d.y0).outerRadius(d => d.y1);
const svg = d3.select("#chart").append("svg")
.attr("viewBox", [-width/2, -height/2, width, height]);
svg.selectAll("path")
.data(root.descendants().filter(d => d.depth))
.join("path")
.attr("d", arc)
.attr("fill", d => { while (d.depth > 1) d = d.parent; return d3.schemeTableau10[d.data.name.length % 10]; })
.attr("fill-opacity", d => 1 - d.depth * 0.15)
.attr("stroke", "white").attr("stroke-width", 0.5)
.append("title").text(d => `${d.ancestors().map(d => d.data.name).reverse().join("/")}\n${d.value}`);