A risk matrix on two category axes: each of the 25 cells is colored from dark green to red by its rating, with a circle sized by its value and the value written inside.
A risk matrix sorts items into a grid by two ratings, like likelihood and impact, and colors each cell by how urgent that mix is. Readers find the red corner first; the circles add a second measure, like the number of risks in each cell.
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/xy-chart/
var chart = root.container.children.push(
am5xy.XYChart.new(root, {
panX: false, // no dragging...
panY: false,
wheelX: "none", // ...and no wheel zoom: the page scrolls past the chart
wheelY: "none",
paddingLeft: 0, // the row labels sit at the chart's left edge
// a little room between the last column and the edge
paddingRight: 15,
layout: root.verticalLayout
})
);
// Create axes and their renderers
var yRenderer = am5xy.AxisRendererY.new(root, {
visible: false, // no axis line; the labels still show
minGridDistance: 20, // small, so every row keeps its label
// Critical, the first category, at the top
inversed: true,
minorGridEnabled: true
});
yRenderer.grid.template.set("visible", false); // no grid lines: the gaps between the cells divide them
var yAxis = chart.yAxes.push(
am5xy.CategoryAxis.new(root, {
renderer: yRenderer,
categoryField: "category"
})
);
var xRenderer = am5xy.AxisRendererX.new(root, {
visible: false, // no axis line; the labels still show
minGridDistance: 30, // at least 30px between the column labels
// Critical on the right, so the riskiest cells meet in the top right corner
inversed: true,
minorGridEnabled: true
});
xRenderer.grid.template.set("visible", false); // no grid lines
var xAxis = chart.xAxes.push(
am5xy.CategoryAxis.new(root, {
renderer: xRenderer,
categoryField: "category"
})
);
// Create series
// https://www.amcharts.com/docs/v5/charts/xy-chart/#Adding_series
var series = chart.series.push(
am5xy.ColumnSeries.new(root, {
calculateAggregates: true, // works out the lowest and highest values, which the heat rule needs
// gaps between the cells in the chart's background color
stroke: root.interfaceColors.get("background"),
clustered: false,
xAxis: xAxis,
yAxis: yAxis,
categoryXField: "x",
categoryYField: "y",
valueField: "value"
})
);
series.columns.template.setAll({
tooltipText: "{categoryY}, {categoryX}: {value}", // the row, the column and the value
strokeOpacity: 1, // a solid outline...
strokeWidth: 2, // ...2px wide, in the background color set above
cornerRadiusTL: 5, // rounded corners, 5px
cornerRadiusTR: 5,
cornerRadiusBL: 5,
cornerRadiusBR: 5,
width: am5.percent(100), // each cell fills its column...
height: am5.percent(100), // ...and its row
// each cell's fill comes from columnSettings in its data
templateField: "columnSettings"
});
// one shared template for the bubbles, which the heat rule sizes by value
var circleTemplate = am5.Template.new({});
// Add heat rule
// https://www.amcharts.com/docs/v5/concepts/settings/heat-rules/
series.set("heatRules", [{
target: circleTemplate,
min: 10, // the smallest value a 10px bubble...
max: 35, // ...the largest 35px
dataField: "value",
key: "radius"
}]);
// a dark see-through bubble in each cell, sized by the heat rule
series.bullets.push(function () {
return am5.Bullet.new(root, {
sprite: am5.Circle.new(
root,
{
fill: am5.color(0x000000),
fillOpacity: 0.5, // half see-through, so the cell color shows
strokeOpacity: 0 // no outline
},
circleTemplate
)
});
});
// the value, on top of the bubble
series.bullets.push(function () {
return am5.Bullet.new(root, {
sprite: am5.Label.new(root, {
fill: am5.color(0xffffff), // white text on the dark bubble
populateText: true, // fills in {value} from the cell's data
centerX: am5.p50, // centered on the cell
centerY: am5.p50,
fontSize: 10, // small text, in pixels
text: "{value}"
})
});
});
var colors = { // the cell colors, from critical red to very good green
critical: am5.color(0xca0101),
bad: am5.color(0xe17a2d),
medium: am5.color(0xe1d92d),
good: am5.color(0x5dbe24),
verygood: am5.color(0x0b7d03)
};
// Set data
// https://www.amcharts.com/docs/v5/charts/xy-chart/#Setting_data
var data = [
{
y: "Critical",
x: "Very good",
columnSettings: {
fill: colors.medium
},
value: 20
},
{
y: "Bad",
x: "Very good",
columnSettings: {
fill: colors.good
},
value: 15
},
{
y: "Medium",
x: "Very good",
columnSettings: {
fill: colors.verygood
},
value: 25
},
{
y: "Good",
x: "Very good",
columnSettings: {
fill: colors.verygood
},
value: 15
},
{
y: "Very good",
x: "Very good",
columnSettings: {
fill: colors.verygood
},
value: 12
},
{
y: "Critical",
x: "Good",
columnSettings: {
fill: colors.bad
},
value: 30
},
{
y: "Bad",
x: "Good",
columnSettings: {
fill: colors.medium
},
value: 24
},
{
y: "Medium",
x: "Good",
columnSettings: {
fill: colors.good
},
value: 25
},
{
y: "Good",
x: "Good",
columnSettings: {
fill: colors.verygood
},
value: 15
},
{
y: "Very good",
x: "Good",
columnSettings: {
fill: colors.verygood
},
value: 25
},
{
y: "Critical",
x: "Medium",
columnSettings: {
fill: colors.bad
},
value: 33
},
{
y: "Bad",
x: "Medium",
columnSettings: {
fill: colors.bad
},
value: 14
},
{
y: "Medium",
x: "Medium",
columnSettings: {
fill: colors.medium
},
value: 20
},
{
y: "Good",
x: "Medium",
columnSettings: {
fill: colors.good
},
value: 19
},
{
y: "Very good",
x: "Medium",
columnSettings: {
fill: colors.good
},
value: 25
},
{
y: "Critical",
x: "Bad",
columnSettings: {
fill: colors.critical
},
value: 31
},
{
y: "Bad",
x: "Bad",
columnSettings: {
fill: colors.critical
},
value: 24
},
{
y: "Medium",
x: "Bad",
columnSettings: {
fill: colors.bad
},
value: 25
},
{
y: "Good",
x: "Bad",
columnSettings: {
fill: colors.medium
},
value: 15
},
{
y: "Very good",
x: "Bad",
columnSettings: {
fill: colors.good
},
value: 15
},
{
y: "Critical",
x: "Critical",
columnSettings: {
fill: colors.critical
},
value: 12
},
{
y: "Bad",
x: "Critical",
columnSettings: {
fill: colors.critical
},
value: 14
},
{
y: "Medium",
x: "Critical",
columnSettings: {
fill: colors.critical
},
value: 15
},
{
y: "Good",
x: "Critical",
columnSettings: {
fill: colors.bad
},
value: 25
},
{
y: "Very good",
x: "Critical",
columnSettings: {
fill: colors.medium
},
value: 19
}
];
series.data.setAll(data);
yAxis.data.setAll([
{ category: "Critical" },
{ category: "Bad" },
{ category: "Medium" },
{ category: "Good" },
{ category: "Very good" }
]);
xAxis.data.setAll([
{ category: "Critical" },
{ category: "Bad" },
{ category: "Medium" },
{ category: "Good" },
{ category: "Very good" }
]);
// Make stuff animate on load
// https://www.amcharts.com/docs/v5/concepts/animations/#Initial_animation
chart.appear(1000, 100);
{
"type": "XYChart",
"settings": {
"panX": false,
"panY": false,
"wheelX": "none",
"wheelY": "none",
"paddingLeft": 0,
"paddingRight": 15,
"layout": {
"type": "VerticalLayout"
}
},
"properties": {
"series": [
"#series-0"
],
"xAxes": [
"#axis-0"
],
"yAxes": [
"#axis-1"
]
},
"refs": [
{
"data-0": [
{
"y": "Critical",
"x": "Very good",
"columnSettings": {
"fill": {
"type": "Color",
"value": "#e1d92d"
},
"__parse": true
},
"value": 20,
"__parse": true
},
{
"y": "Bad",
"x": "Very good",
"columnSettings": {
"fill": {
"type": "Color",
"value": "#5dbe24"
},
"__parse": true
},
"value": 15,
"__parse": true
},
{
"y": "Medium",
"x": "Very good",
"columnSettings": {
"fill": {
"type": "Color",
"value": "#0b7d03"
},
"__parse": true
},
"value": 25,
"__parse": true
},
{
"y": "Good",
"x": "Very good",
"columnSettings": {
"fill": {
"type": "Color",
"value": "#0b7d03"
},
"__parse": true
},
"value": 15,
"__parse": true
},
{
"y": "Very good",
"x": "Very good",
"columnSettings": {
"fill": {
"type": "Color",
"value": "#0b7d03"
},
"__parse": true
},
"value": 12,
"__parse": true
},
{
"y": "Critical",
"x": "Good",
"columnSettings": {
"fill": {
"type": "Color",
"value": "#e17a2d"
},
"__parse": true
},
"value": 30,
"__parse": true
},
{
"y": "Bad",
"x": "Good",
"columnSettings": {
"fill": {
"type": "Color",
"value": "#e1d92d"
},
"__parse": true
},
"value": 24,
"__parse": true
},
{
"y": "Medium",
"x": "Good",
"columnSettings": {
"fill": {
"type": "Color",
"value": "#5dbe24"
},
"__parse": true
},
"value": 25,
"__parse": true
},
{
"y": "Good",
"x": "Good",
"columnSettings": {
"fill": {
"type": "Color",
"value": "#0b7d03"
},
"__parse": true
},
"value": 15,
"__parse": true
},
{
"y": "Very good",
"x": "Good",
"columnSettings": {
"fill": {
"type": "Color",
"value": "#0b7d03"
},
"__parse": true
},
"value": 25,
"__parse": true
},
{
"y": "Critical",
"x": "Medium",
"columnSettings": {
"fill": {
"type": "Color",
"value": "#e17a2d"
},
"__parse": true
},
"value": 33,
"__parse": true
},
{
"y": "Bad",
"x": "Medium",
"columnSettings": {
"fill": {
"type": "Color",
"value": "#e17a2d"
},
"__parse": true
},
"value": 14,
"__parse": true
},
{
"y": "Medium",
"x": "Medium",
"columnSettings": {
"fill": {
"type": "Color",
"value": "#e1d92d"
},
"__parse": true
},
"value": 20,
"__parse": true
},
{
"y": "Good",
"x": "Medium",
"columnSettings": {
"fill": {
"type": "Color",
"value": "#5dbe24"
},
"__parse": true
},
"value": 19,
"__parse": true
},
{
"y": "Very good",
"x": "Medium",
"columnSettings": {
"fill": {
"type": "Color",
"value": "#5dbe24"
},
"__parse": true
},
"value": 25,
"__parse": true
},
{
"y": "Critical",
"x": "Bad",
"columnSettings": {
"fill": {
"type": "Color",
"value": "#ca0101"
},
"__parse": true
},
"value": 31,
"__parse": true
},
{
"y": "Bad",
"x": "Bad",
"columnSettings": {
"fill": {
"type": "Color",
"value": "#ca0101"
},
"__parse": true
},
"value": 24,
"__parse": true
},
{
"y": "Medium",
"x": "Bad",
"columnSettings": {
"fill": {
"type": "Color",
"value": "#e17a2d"
},
"__parse": true
},
"value": 25,
"__parse": true
},
{
"y": "Good",
"x": "Bad",
"columnSettings": {
"fill": {
"type": "Color",
"value": "#e1d92d"
},
"__parse": true
},
"value": 15,
"__parse": true
},
{
"y": "Very good",
"x": "Bad",
"columnSettings": {
"fill": {
"type": "Color",
"value": "#5dbe24"
},
"__parse": true
},
"value": 15,
"__parse": true
},
{
"y": "Critical",
"x": "Critical",
"columnSettings": {
"fill": {
"type": "Color",
"value": "#ca0101"
},
"__parse": true
},
"value": 12,
"__parse": true
},
{
"y": "Bad",
"x": "Critical",
"columnSettings": {
"fill": {
"type": "Color",
"value": "#ca0101"
},
"__parse": true
},
"value": 14,
"__parse": true
},
{
"y": "Medium",
"x": "Critical",
"columnSettings": {
"fill": {
"type": "Color",
"value": "#ca0101"
},
"__parse": true
},
"value": 15,
"__parse": true
},
{
"y": "Good",
"x": "Critical",
"columnSettings": {
"fill": {
"type": "Color",
"value": "#e17a2d"
},
"__parse": true
},
"value": 25,
"__parse": true
},
{
"y": "Very good",
"x": "Critical",
"columnSettings": {
"fill": {
"type": "Color",
"value": "#e1d92d"
},
"__parse": true
},
"value": 19,
"__parse": true
}
]
},
{
"data-1": [
{
"category": "Critical"
},
{
"category": "Bad"
},
{
"category": "Medium"
},
{
"category": "Good"
},
{
"category": "Very good"
}
]
},
{
"template-0": {
"type": "Template"
}
},
{
"axis-0": {
"type": "CategoryAxis",
"settings": {
"renderer": {
"type": "AxisRendererX",
"settings": {
"visible": false,
"minGridDistance": 30,
"inversed": true,
"minorGridEnabled": true
},
"properties": {
"grid": {
"properties": {
"template": {
"settings": {
"visible": false
}
}
}
}
}
},
"categoryField": "category"
},
"properties": {
"data": "#data-1"
}
}
},
{
"axis-1": {
"type": "CategoryAxis",
"settings": {
"renderer": {
"type": "AxisRendererY",
"settings": {
"visible": false,
"minGridDistance": 20,
"inversed": true,
"minorGridEnabled": true
},
"properties": {
"grid": {
"properties": {
"template": {
"settings": {
"visible": false
}
}
}
}
}
},
"categoryField": "category"
},
"properties": {
"data": "#data-1"
}
}
},
{
"series-0": {
"type": "ColumnSeries",
"settings": {
"calculateAggregates": true,
"stroke": "@root.interfaceColors.get('background')",
"clustered": false,
"xAxis": "#axis-0",
"yAxis": "#axis-1",
"categoryXField": "x",
"categoryYField": "y",
"valueField": "value",
"heatRules": [
{
"target": "#template-0",
"min": 10,
"max": 35,
"dataField": "value",
"key": "radius",
"__parse": true
}
]
},
"properties": {
"data": "#data-0",
"columns": {
"properties": {
"template": {
"settings": {
"tooltipText": "{categoryY}, {categoryX}: {value}",
"strokeOpacity": 1,
"strokeWidth": 2,
"cornerRadiusTL": 5,
"cornerRadiusTR": 5,
"cornerRadiusBL": 5,
"cornerRadiusBR": 5,
"width": {
"type": "Percent",
"value": 100
},
"height": {
"type": "Percent",
"value": 100
},
"templateField": "columnSettings"
}
}
}
},
"bullets": [
{
"type": "Bullet",
"settings": {
"sprite": {
"type": "Circle",
"settings": {
"fill": {
"type": "Color",
"value": "#000000"
},
"fillOpacity": 0.5,
"strokeOpacity": 0
},
"properties": {
"template": "#template-0"
}
}
}
},
{
"type": "Bullet",
"settings": {
"sprite": {
"type": "Label",
"settings": {
"fill": {
"type": "Color",
"value": "#ffffff"
},
"populateText": true,
"centerX": {
"type": "Percent",
"value": 50
},
"centerY": {
"type": "Percent",
"value": 50
},
"fontSize": 10,
"text": "{value}"
}
}
}
}
]
}
}
}
]
}