A chord diagram with thin lines instead of wide ribbons: it shows which nodes are connected rather than how much flows between them. Dots travel back and forth along each line.
Ribbons are good at showing size, but they cover each other when there are many. Thin lines keep every connection in view, so the shape of the network comes through: who links to many, and who to few.
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 series
// https://www.amcharts.com/docs/v5/charts/flow-charts/
// a chart whose contents can be zoomed and panned
var chart = root.container.children.push(am5.SerialChartContainer.new(root, {}));
// a chord diagram with thin links instead of ribbons
var series = chart.series.push(am5flow.ChordNonRibbon.new(root, {
sourceIdField: "from",
targetIdField: "to",
valueField: "value"
}));
series.nodes.get("colors").set("step", 2); // every second theme color for the nodes
// a dot on each link runs back and forth between its two nodes, each at its own speed
series.bullets.push(function (_root, _series, dataItem) {
var bullet = am5.Bullet.new(root, {
locationY: Math.random(), // each dot starts somewhere along its link
sprite: am5.Circle.new(root, {
radius: 5, // 5px radius
fill: dataItem.get("source").get("fill") // in the color of the link's source node
})
});
bullet.animate({
key: "locationY", // moves along the link...
to: 1, // ...to the target end...
from: 0, // ...from the source end...
duration: Math.random() * 1000 + 2000, // ...in 2 to 3 seconds...
loops: Infinity, // ...forever...
easing: am5.ease.yoyo(am5.ease.cubic) // ...there and back, slowing at each end
});
return bullet;
});
// straight (not curved) names in the background color, set on the node circles
series.nodes.labels.template.setAll({
textType: "regular",
fill: root.interfaceColors.get("background"),
fontSize: "1.1em", // a little larger than normal text
radius: -5 // pulled 5px in, onto the node circles
});
// a big circle on each node, in the node's color
series.nodes.bullets.push(function (_root, _series, dataItem) {
return am5.Bullet.new(root, {
sprite: am5.Circle.new(root, {
radius: 20, // 20px radius
fill: dataItem.get("fill") // the node's color
})
});
});
// move the bullets container to the back, behind the links and nodes
series.children.moveValue(series.bulletsContainer, 0);
// Set data
// https://www.amcharts.com/docs/v5/charts/flow-charts/#Setting_data
series.data.setAll([
{ from: "A", to: "D", value: 10 },
{ from: "B", to: "D", value: 8 },
{ from: "B", to: "E", value: 4 },
{ from: "B", to: "C", value: 2 },
{ from: "C", to: "E", value: 14 },
{ from: "E", to: "D", value: 8 },
{ from: "C", to: "A", value: 4 },
{ from: "G", to: "A", value: 7 }
]);
// Make stuff animate on load
series.appear(1000, 100);
{
"type": "SerialChartContainer",
"properties": {
"series": [
"#series-0"
]
},
"refs": [
{
"data-0": [
{
"from": "A",
"to": "D",
"value": 10
},
{
"from": "B",
"to": "D",
"value": 8
},
{
"from": "B",
"to": "E",
"value": 4
},
{
"from": "B",
"to": "C",
"value": 2
},
{
"from": "C",
"to": "E",
"value": 14
},
{
"from": "E",
"to": "D",
"value": 8
},
{
"from": "C",
"to": "A",
"value": 4
},
{
"from": "G",
"to": "A",
"value": 7
}
]
},
{
"series-0": {
"type": "ChordNonRibbon",
"settings": {
"sourceIdField": "from",
"targetIdField": "to",
"valueField": "value"
},
"properties": {
"data": "#data-0",
"nodes": {
"properties": {
"labels": {
"properties": {
"template": {
"settings": {
"textType": "regular",
"fill": "@root.interfaceColors.get('background')",
"fontSize": "1.1em",
"radius": -5
}
}
}
},
"bullets": [
{
"type": "Bullet",
"settings": {
"sprite": {
"type": "Circle",
"settings": {
"radius": 20,
"fill": "@root.interfaceColors.get('primaryButton')"
}
}
}
}
]
},
"settings": {
"colors": {
"type": "ColorSet",
"settings": {
"step": 2,
"colors": [
"@root.interfaceColors.get('primaryButton')",
{
"type": "Color",
"value": "#6f51a6"
},
{
"type": "Color",
"value": "#97539f"
},
{
"type": "Color",
"value": "#b9588c"
},
{
"type": "Color",
"value": "#d46370"
},
{
"type": "Color",
"value": "#e3764f"
},
"@root.interfaceColors.get('primaryButton')",
{
"type": "Color",
"value": "#6f51a6"
},
{
"type": "Color",
"value": "#97539f"
},
{
"type": "Color",
"value": "#b9588c"
},
{
"type": "Color",
"value": "#d46370"
},
{
"type": "Color",
"value": "#e3764f"
}
],
"reuse": true,
"passOptions": {
"hue": 0.05,
"saturation": 0,
"lightness": 0
},
"startIndex": 0,
"stateAnimationDuration": 600
}
}
}
},
"bullets": [
{
"type": "Bullet",
"settings": {
"sprite": {
"type": "Circle",
"settings": {
"radius": 5,
"fill": "@root.interfaceColors.get('primaryButton')"
}
},
"animations": [
{
"key": "locationY",
"from": 0,
"to": 1,
"duration": 1101.043094655251,
"loops": 0,
"easing": "cubic",
"yoyo": true
}
]
}
}
]
}
}
}
]
}