RareCharts provides three circular chart types that share a common API and theme system: Donut, Pie, and Gauge. Each answers a different question.
Donut
The default circular chart. A ring with a center area — the center gives you a place to put something useful: the total, a headline number, or a short label. That makes donut the better default for dashboards, where charts rarely live alone and context matters.
new RareCharts.Donut('#chart', {
title: 'Revenue by Product',
source: 'Source: Internal accounting',
legend: segments.map(d => ({ label: d.label, type: 'bar' })),
height: 300,
centerText: data => '$' + d3.format(',.0f')(d3.sum(data, d => d.value)) + 'K',
centerLabel: 'Revenue',
}).setData(segments);
Legend to the right
Pass legendPosition: 'right' to place the legend in a vertical column beside the chart instead of above it. Works well when you have many categories or want to keep the chart area compact.
new RareCharts.Donut('#chart', {
legend: segments.map(d => ({ label: d.label, type: 'bar' })),
legendPosition: 'right',
height: 340,
}).setData(segments);
Drill-down (hierarchy mode)
Hand Donut a tree — a single root object instead of a flat array — and it becomes an interactive drill-down. It opens on the root’s direct children; click a slice that has children to descend into it, and click the center (or a breadcrumb crumb) to come back up. A leaf slice just shows its value. It is the same tree contract as the hierarchical bar — one data shape, two views: the bar lays the whole hierarchy out at once, the donut walks it one ring at a time.
Two composition details carry over from the tree contract. A parent whose children don’t add up to its stated value has a remainder — the undisclosed gap — which draws as a muted slice once you surface it with showRemainder: true or a per-node remainderLabel. And a named-but-undisclosed item (value: null) has no magnitude, so it cannot be a slice; the ring notes it as “N not disclosed” beneath the chart instead. See the hierarchical bar page for the full node shape and the value tri-state.
new RareCharts.Donut('#chart', {
showRemainder: true,
}).setData({
label: 'Portfolio', value: 1400,
children: [
{ label: 'World Liberty Financial', value: 536.4, remainderLabel: 'Other', children: [
{ label: 'Ethereum Key', value: 106 },
{ label: 'USDC Key', value: 56 },
] },
{ label: 'Stablecoin proceeds', value: 196.9 },
{ label: 'NFT INT, LLC', value: null }, // undisclosed → noted, not drawn
],
});
Pie
A pie is a donut with innerRadius: 0. The class name is Donut — Pie is an alias. Same API, same options, same behavior.
A pie chart is a blunt instrument: it answers “how is the total split” when there are only a few categories and the differences are obvious. It breaks down quickly when slices are similar in size, when there are many categories, or when the reader needs precision.
new RareCharts.Pie('#chart', {
height: 300,
showLabels: true,
}).setData(segments);
Outer labels
Enable with showLabels: true. Each slice gets a leader line and two-line text (category name + percent). Slices below labelMinPct (default: 4%) are skipped automatically.
Control what appears with labelContent:
'both'— category name + percentage (default)'label'— category name only'percent'— percentage only
new RareCharts.Pie('#chart', {
showLabels: true,
labelContent: 'percent', // just percentages
labelMinPct: 0.05, // hide labels below 5%
}).setData(segments);
Gauge
An arc-based progress chart. Shows a value relative to a maximum along a partial arc. Use it for goal completion, budget usage, KPI progress — anywhere the question is "how far along are we?"
setData() accepts a plain number, or an object to override max and min per render:
// 73 out of 100 (default max)
new RareCharts.Gauge('#chart', {
centerLabel: 'Complete',
}).setData(73);
// 50 achieved out of 80 plan — fills to 62.5%
new RareCharts.Gauge('#chart', {
max: 80,
color: '#00c97a',
centerText: (value, max) => `${value}/${max}`,
centerLabel: 'Achieved',
}).setData(50);
// Override max at render time
gauge.setData({ value: 50, max: 80 });
The arc geometry is fully configurable:
new RareCharts.Gauge('#chart', {
startAngle: -Math.PI * 0.75, // -135° (default)
endAngle: Math.PI * 0.75, // +135° (default, 270° sweep)
thickness: 0.18, // ring thickness as fraction of radius
cornerRadius: 6,
trackColor: '#e8e8e8', // background arc
color: '#ff3b5c', // fill arc
});
Speedometer — half-circle and needle
Two ingredients make the automotive-indicator look:
- Sweep.
startAngle/endAngleset the arc in radians, clockwise from 12 o'clock. The default±Math.PI * 0.75is a 270° horseshoe;-Math.PI / 2 … Math.PI / 2is the flat 180° half-circle. The gauge re-centers vertically for any sweep and reserves room for the center text when the arc is shallow, so nothing clips. - Needle.
needle: truedraws a pointer pivoting on a hub at the dial center, animated to the value together with the fill. The center readout moves below the hub, dial-style. Color it withneedleColor(default: theme text).
new RareCharts.Gauge('#chart', {
startAngle: -Math.PI / 2, // 9 o'clock
endAngle: Math.PI / 2, // 3 o'clock — a 180° sweep
needle: true,
max: 240,
centerText: v => v,
centerLabel: 'km/h',
}).setData(87);
Data format
All three types use the same segment data structure:
[
{ label: 'Subscriptions', value: 42000, color: '#00c97a' },
{ label: 'Services', value: 18000 },
{ label: 'Other', value: 6000 }
]
Only positive finite values are rendered. Zero and negative values are filtered out.
Passing a single root object instead of a flat array switches Donut into drill-down mode, where the data is a tree — see the Drill-down section above.
Colors can be provided per item. If omitted, the chart uses the active theme palette in order.
Slice geometry
padAngle controls the gap between slices (a small default keeps separation without turning the chart into a flower). cornerRadius rounds slice corners. Both have sensible defaults and adjust slightly between Pie and Donut mode.
Hover interaction expands the hovered slice outward and shows a tooltip. Tooltip content is fully customizable:
tooltipFormat: ({ label, value, percent, color }) => `
<div style="color:${color}">${label}</div>
<div>${d3.format(',.0f')(value)}</div>
<div style="color:#888">${d3.format('.1%')(percent)}</div>
`
Circular charts options
| Option | Default | Description |
|---|---|---|
Donut / Pie |
||
height |
280 |
Chart height px |
innerRadius |
0.58 |
Hole size as fraction of outer radius; 0 = Pie |
padAngle |
0.018 |
Gap between slices (radians) |
cornerRadius |
3 |
Rounded slice corners px |
showLabels |
false |
Show outer leader-line labels |
labelContent |
'both' |
'both' / 'label' / 'percent' |
labelMinPct |
0.04 |
Hide label when slice < this fraction |
legendPosition |
— | 'right' to place legend in vertical aside |
showCenter |
true (donut) |
Show center text hole |
centerText |
formatted total | String or function(data) => string |
centerLabel |
'Total' |
Secondary line below center text |
valueFormat |
locale number | function(value) => string — center total and default tooltip value |
percentFormat |
.1% |
function(pct) => string — outer-label and tooltip percents |
animate |
true |
Animate on first render |
duration |
650 |
Animation duration ms |
ease |
'cubicOut' |
'cubicOut' / 'cubicInOut' / 'linear' |
tooltipFormat |
built-in | function({label, value, percent, color}) => html |
showRemainder |
false |
Drill-down mode: draw a muted slice for each node’s undisclosed remainder (value − Σ children) |
remainderLabel |
'Other' |
Drill-down mode: fallback label for a surfaced remainder |
strict |
false |
Drill-down mode: throw when a node’s children exceed its stated value (default: warn, leave the negative remainder undrawn) |
Gauge |
||
height |
220 |
Chart height px |
min |
0 |
Minimum value |
max |
100 |
Maximum value |
startAngle |
-¾π |
Arc start (radians, clockwise from top) |
endAngle |
+¾π |
Arc end (270° sweep by default) |
thickness |
0.18 |
Ring thickness as fraction of outer radius |
cornerRadius |
6 |
Arc end rounding px |
needle |
false |
Speedometer pointer pivoting at the dial center; the center readout moves below the hub |
needleColor |
theme text | Needle and hub color |
color |
theme.accent |
Fill arc color |
trackColor |
theme.grid |
Background arc color |
showCenter |
true |
Show center text |
centerText |
'63%' |
String or function(value, max, min) => string |
centerLabel |
— | Secondary line below center text |
animate |
true |
Animate fill on first render |
duration |
800 |
Animation duration ms |
ease |
'cubicOut' |
'cubicOut' / 'cubicInOut' / 'linear' |
tooltipFormat |
built-in | function({value, max, min, percent}) => html |