HAHS.
Back to Catalog

Icicle Diagram

chart

Also known as: icicle chart, icicle plot, partition diagram, flame chart, flame graph

Show hierarchyShow compositionEnable exploration HierarchicalCategoricalNumerical Bar/Column

Description

An icicle diagram (also called a partition diagram) displays hierarchical data as nested rectangles arranged in horizontal rows, one row per level of the hierarchy. The root occupies the full width at the top, its children divide that width proportionally in the row below, and so on recursively. Each rectangle’s width encodes its value (or the sum of its descendants’ values), and its vertical position encodes its depth in the tree. The result looks like icicles hanging from a ceiling — hence the name.

Icicle diagrams are the rectangular cousin of the sunburst chart: where a sunburst uses concentric rings and arc angles, an icicle uses stacked rows and horizontal widths. The rectangular layout offers a significant perceptual advantage: comparing widths of aligned rectangles is far more accurate than comparing arc lengths in a radial layout. This makes icicle diagrams better for analytical tasks where precise size comparison matters.

The most widely known variant is the “flame graph” used in software performance profiling, where the hierarchy represents a call stack and the width of each rectangle represents time spent in that function. Flame graphs are inverted icicles (growing upward), and they have become an indispensable tool in software engineering for identifying performance bottlenecks.

Icicle Diagram — interactive example

When to Use

  • Visualizing hierarchical data where both structure and size matter (e.g., disk usage, organizational budgets)
  • Comparing the relative size of nodes at the same level of the hierarchy (width comparison is perceptually accurate)
  • Software performance profiling (flame graphs) to identify time-consuming call stacks
  • Exploring multi-level part-to-whole decompositions where the audience needs precise comparison

When NOT to Use

  • When the hierarchy is very deep (>6 levels) — rows become too thin to read; use a collapsible dendrogram or treemap
  • When there is no meaningful size variable — use a dendrogram that focuses on structure alone
  • When the audience expects a treemap — icicle diagrams are less familiar and may need explanation
  • When space is limited — the vertical stacking requires significant height for deep hierarchies

Anatomy

  • Root rectangle: The topmost rectangle spanning the full width, representing the total
  • Level rows: Horizontal bands of rectangles, one per hierarchy level, with child rectangles nested within their parent’s horizontal span
  • Rectangle width: Proportional to the node’s value (or sum of descendant values)
  • Rectangle height: Uniform within each level, representing hierarchy depth
  • Color encoding: Applied to rectangles by top-level category or by a separate metric
  • Labels: Text inside each rectangle when space permits, showing the node name and optionally the value
  • Gaps: Small padding between adjacent rectangles to distinguish siblings

Variations

  • Flame graph (inverted icicle): Grows upward instead of downward, widely used in CPU profiling to visualize call stacks
  • Zoomable icicle: Clicking a rectangle zooms in, making it the new root for detailed exploration
  • Horizontal icicle: Levels stack left-to-right instead of top-to-bottom, useful when labels are long
  • Icicle + dendrogram: A dendrogram on one side shows the tree structure while the icicle encodes values
  • Animated icicle: Smooth transitions when drilling in and out of hierarchy levels

Code Reference

// D3 icicle diagram using d3.partition
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([width, height]).padding(1);
partition(root);

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

svg.selectAll("rect")
  .data(root.descendants()).join("rect")
  .attr("x", d => d.x0).attr("y", d => d.y0)
  .attr("width", d => d.x1 - d.x0)
  .attr("height", d => d.y1 - d.y0)
  .attr("fill", d => d3.schemeTableau10[d.depth % 10]);

svg.selectAll("text")
  .data(root.descendants().filter(d => d.x1 - d.x0 > 40))
  .join("text")
  .attr("x", d => d.x0 + 4).attr("y", d => d.y0 + 14)
  .text(d => d.data.name).attr("font-size", 10);