A gauge split into seven colored bands, from Unsustainable to High Performing. The needle takes the color of the band it points at, and the score sits in its pin.
Bands turn a number into a verdict: 42 means little on its own, but Maturing tells people where they stand. They work when the scale has agreed cut-off points, like a maturity model, a credit score or a health check.
Built with amCharts 5, a JavaScript library. The JavaScript includes your theme and renderer (canvas or SVG); the JSON also follows the changes you make with the controls.
npm install @amcharts/amcharts5. See Getting started.
The same chart as a JSON config. Chart Maker reads and writes this format, the library's JSON parser turns it into a chart, and the <am5-chart> element puts it on a page with no code."type" is the class to create and "settings" what you would pass to its new() in code; "properties" fill in its parts, lists such as series and axes or templates such as columns.template; "refs" holds objects used in more than one place ("#series-0" stands for the one named series-0); a string starting with "@" takes a value from another object, such as "@series.get('fill')".
A complete page. Save it as an .html file and open it in a browser.
Paste this into an AI coding assistant to build the chart from scratch. More in amCharts & AI.
// Create root element
// https://www.amcharts.com/docs/v5/getting-started/#Root_element
var root = am5.Root.new("chartdiv");
// Set themes
// https://www.amcharts.com/docs/v5/concepts/themes/
root.setThemes([
am5themes_Animated.new(root),
am5themes_Responsive.new(root)
]);
// Create chart
// https://www.amcharts.com/docs/v5/charts/radar-chart/
var chart = root.container.children.push(am5radar.RadarChart.new(root, {
panX: false, // no dragging
panY: false,
// a 220-degree arc, open at the bottom
startAngle: 160,
endAngle: 380
}));
// Create axis and its renderer
// https://www.amcharts.com/docs/v5/charts/radar-chart/gauge-charts/#Axes
var axisRenderer = am5radar.AxisRendererCircular.new(root, {
// negative: 40px in from the outer edge, so the bands form a 40px-wide ring
innerRadius: -40
});
axisRenderer.grid.template.setAll({
stroke: root.interfaceColors.get("background"), // background-colored lines between the bands...
visible: true,
strokeOpacity: 0.8 // ...mostly opaque
});
var xAxis = chart.xAxes.push(am5xy.ValueAxis.new(root, {
maxDeviation: 0,
min: -40, // the scale runs from -40...
max: 100, // ...to 100...
strictMinMax: true, // ...exactly, not rounded out
renderer: axisRenderer
}));
// Add clock hand
// https://www.amcharts.com/docs/v5/charts/radar-chart/gauge-charts/#Clock_hands
var axisDataItem = xAxis.makeDataItem({});
var clockHand = am5radar.ClockHand.new(root, {
pinRadius: am5.percent(20), // a big pin, 20% of the gauge's radius: the number sits on it
radius: am5.percent(100), // the hand reaches the outer edge
bottomWidth: 40 // 40px wide at the base
})
var bullet = axisDataItem.set("bullet", am5xy.AxisBullet.new(root, {
sprite: clockHand
}));
xAxis.createAxisRange(axisDataItem);
var label = chart.radarContainer.children.push(am5.Label.new(root, {
fill: am5.color(0xffffff), // white to start; the code below picks white or black
centerX: am5.percent(50), // the label's middle...
textAlign: "center",
centerY: am5.percent(50), // ...on the gauge's center
fontSize: "3em" // three times the normal text size
}));
// White or black text, whichever reads better on a color
function textColor(color) {
return am5.Color.alternative(color, am5.color(0xffffff), am5.color(0x000000));
}
axisDataItem.set("value", 50); // the hand starts at 50
// whenever the hand turns, update the number and the colors
bullet.get("sprite").on("rotation", function () {
var value = axisDataItem.get("value");
var fill = am5.color(0x000000);
xAxis.axisRanges.each(function (axisRange) {
if (value >= axisRange.get("value") && value <= axisRange.get("endValue")) {
fill = axisRange.get("axisFill").get("fill");
}
})
label.set("text", Math.round(value).toString());
// the hand takes the color of the band it points at; the number stays readable on it
clockHand.pin.animate({ key: "fill", to: fill, duration: 500, easing: am5.ease.out(am5.ease.cubic) })
clockHand.hand.animate({ key: "fill", to: fill, duration: 500, easing: am5.ease.out(am5.ease.cubic) })
label.animate({ key: "fill", to: textColor(fill), duration: 500, easing: am5.ease.out(am5.ease.cubic) })
});
// every two seconds, the hand moves to a random value from -40 to 100
setInterval(function () {
axisDataItem.animate({
key: "value",
to: Math.round(Math.random() * 140 - 40),
duration: 500,
easing: am5.ease.out(am5.ease.cubic) // fast start, slow finish
});
}, 2000)
// Create axis ranges bands, colored on the theme's scale from its color for negative values (red) through its warning
// color (yellow) to its positive one (green), so they follow the theme and dark mode
// https://www.amcharts.com/docs/v5/charts/radar-chart/gauge-charts/#Bands
function scaleColor(position) {
return am5.Color.interpolateColors(position, [
root.interfaceColors.get("negative"),
root.interfaceColors.get("warning"),
root.interfaceColors.get("positive")
]);
}
var bandsData = [{
title: "Unsustainable",
lowScore: -40,
highScore: -20
}, {
title: "Volatile",
lowScore: -20,
highScore: 0
}, {
title: "Foundational",
lowScore: 0,
highScore: 20
}, {
title: "Developing",
lowScore: 20,
highScore: 40
}, {
title: "Maturing",
lowScore: 40,
highScore: 60
}, {
title: "Sustainable",
lowScore: 60,
highScore: 80
}, {
title: "High Performing",
lowScore: 80,
highScore: 100
}];
am5.array.each(bandsData, function (data, i) {
var color = scaleColor(i / (bandsData.length - 1)); // the first band red, the last green
var axisRange = xAxis.createAxisRange(xAxis.makeDataItem({}));
axisRange.setAll({
value: data.lowScore, // the band runs from the low score...
endValue: data.highScore // ...to the high score
});
axisRange.get("axisFill").setAll({
visible: true, // shown (axis fills are hidden by default)
fill: color, // the band's color...
fillOpacity: 0.8 // ...slightly see-through
});
axisRange.get("label").setAll({
text: data.title,
inside: true, // the label sits inside the ring
radius: 15,
fontSize: "0.9em", // a little smaller
fill: textColor(color) // white or black, whichever reads better on the band
});
});
// Make stuff animate on load
chart.appear(1000, 100);
{
"type": "RadarChart",
"settings": {
"panX": false,
"panY": false,
"startAngle": 160,
"endAngle": 380
},
"properties": {
"xAxes": [
"#axis-0"
],
"radarContainer": {
"children": [
{
"type": "Label",
"settings": {
"fill": {
"type": "Color",
"value": "#000000"
},
"centerX": {
"type": "Percent",
"value": 50
},
"textAlign": "center",
"centerY": {
"type": "Percent",
"value": 50
},
"fontSize": "3em",
"text": "87"
}
}
]
}
},
"refs": [
{
"axis-0": {
"type": "ValueAxis",
"settings": {
"maxDeviation": 0,
"min": -40,
"max": 100,
"strictMinMax": true,
"renderer": {
"type": "AxisRendererCircular",
"settings": {
"innerRadius": -40
},
"properties": {
"grid": {
"properties": {
"template": {
"settings": {
"stroke": "@root.interfaceColors.get('background')",
"visible": true,
"strokeOpacity": 0.8
}
}
}
}
}
}
}
}
},
{
"axisRange-0": {
"axis": "#axis-0",
"settings": {
"bullet": {
"type": "AxisBullet",
"settings": {
"sprite": {
"type": "ClockHand",
"settings": {
"pinRadius": {
"type": "Percent",
"value": 20
},
"radius": {
"type": "Percent",
"value": 100
},
"bottomWidth": 40
}
}
}
},
"value": 87
},
"__parseLogic": "axisRange"
}
},
{
"axisRange-1": {
"axis": "#axis-0",
"settings": {
"label": {
"type": "AxisLabelRadial",
"settings": {
"radius": 15,
"inside": true,
"fontSize": "0.9em",
"fill": {
"type": "Color",
"value": "#ffffff"
},
"text": "Unsustainable"
}
},
"axisFill": {
"type": "Slice",
"settings": {
"visible": true,
"fill": {
"type": "Color",
"value": "#b30000"
},
"fillOpacity": 0.8
}
},
"value": -40,
"endValue": -20
},
"__parseLogic": "axisRange"
}
},
{
"axisRange-2": {
"axis": "#axis-0",
"settings": {
"label": {
"type": "AxisLabelRadial",
"settings": {
"radius": 15,
"inside": true,
"fontSize": "0.9em",
"fill": {
"type": "Color",
"value": "#ffffff"
},
"text": "Volatile"
}
},
"axisFill": {
"type": "Slice",
"settings": {
"visible": true,
"fill": {
"type": "Color",
"value": "#c84010"
},
"fillOpacity": 0.8
}
},
"value": -20,
"endValue": 0
},
"__parseLogic": "axisRange"
}
},
{
"axisRange-3": {
"axis": "#axis-0",
"settings": {
"label": {
"type": "AxisLabelRadial",
"settings": {
"radius": 15,
"inside": true,
"fontSize": "0.9em",
"fill": {
"type": "Color",
"value": "#000000"
},
"text": "Foundational"
}
},
"axisFill": {
"type": "Slice",
"settings": {
"visible": true,
"fill": {
"type": "Color",
"value": "#dd8120"
},
"fillOpacity": 0.8
}
},
"value": 0,
"endValue": 20
},
"__parseLogic": "axisRange"
}
},
{
"axisRange-4": {
"axis": "#axis-0",
"settings": {
"label": {
"type": "AxisLabelRadial",
"settings": {
"radius": 15,
"inside": true,
"fontSize": "0.9em",
"fill": {
"type": "Color",
"value": "#000000"
},
"text": "Developing"
}
},
"axisFill": {
"type": "Slice",
"settings": {
"visible": true,
"fill": {
"type": "Color",
"value": "#f2c230"
},
"fillOpacity": 0.8
}
},
"value": 20,
"endValue": 40
},
"__parseLogic": "axisRange"
}
},
{
"axisRange-5": {
"axis": "#axis-0",
"settings": {
"label": {
"type": "AxisLabelRadial",
"settings": {
"radius": 15,
"inside": true,
"fontSize": "0.9em",
"fill": {
"type": "Color",
"value": "#000000"
},
"text": "Maturing"
}
},
"axisFill": {
"type": "Slice",
"settings": {
"visible": true,
"fill": {
"type": "Color",
"value": "#bcbd20"
},
"fillOpacity": 0.8
}
},
"value": 40,
"endValue": 60
},
"__parseLogic": "axisRange"
}
},
{
"axisRange-6": {
"axis": "#axis-0",
"settings": {
"label": {
"type": "AxisLabelRadial",
"settings": {
"radius": 15,
"inside": true,
"fontSize": "0.9em",
"fill": {
"type": "Color",
"value": "#000000"
},
"text": "Sustainable"
}
},
"axisFill": {
"type": "Slice",
"settings": {
"visible": true,
"fill": {
"type": "Color",
"value": "#86b810"
},
"fillOpacity": 0.8
}
},
"value": 60,
"endValue": 80
},
"__parseLogic": "axisRange"
}
},
{
"axisRange-7": {
"axis": "#axis-0",
"settings": {
"label": {
"type": "AxisLabelRadial",
"settings": {
"radius": 15,
"inside": true,
"fontSize": "0.9em",
"fill": {
"type": "Color",
"value": "#000000"
},
"text": "High Performing"
}
},
"axisFill": {
"type": "Slice",
"settings": {
"visible": true,
"fill": {
"type": "Color",
"value": "#50b300"
},
"fillOpacity": 0.8
}
},
"value": 80,
"endValue": 100
},
"__parseLogic": "axisRange"
}
}
]
}