Chord Diagram with Animated Bullets
A directed chord diagram with dots that keep running along the ribbons, so the direction of every flow is plain to see. Here, six nodes and nine flows.
About this chart
When motion shows direction
In a chord diagram, direction is easy to miss: a ribbon looks the same from both ends. Moving dots fix that without arrows or a legend, and they draw the eye to a chart on a landing page or a big screen.
Good for
- Flows with a clear direction, like migration or payments
- Dashboards on wall screens
- Landing pages that need a living chart
Think twice when
- Printed reports and screenshots: the motion is lost
- Many ribbons: the dots turn into noise
- Readers who find motion distracting: offer a still version
For developers
Get the code.
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.
For projects that use npm:
How it reads:
Using Claude? Install the amCharts plugin first, so it works from our docs and examples.
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.How it reads:
"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.Using Claude? Install the amCharts plugin first, so it works from our docs and examples.
// Create root element
// https://docs.amcharts.com/v5/getting-started/#root-element
var root = am5.Root.new("chartdiv");
// Set themes
// https://docs.amcharts.com/v5/concepts/themes/
root.setThemes([am5themes_Animated.new(root), am5themes_Responsive.new(root)]);
// Create series
// https://docs.amcharts.com/v5/charts/flow-charts/
var chart = root.container.children.push(am5.SerialChartContainer.new(root, {})); // holds the chord diagram
var series = chart.series.push(
am5flow.ChordDirected.new(root, {
sourceIdField: "from",
targetIdField: "to",
valueField: "value",
sort: "ascending" // the nodes go round from the smallest total to the largest
})
);
// each link takes the color of the node it comes from
series.links.template.set("fillStyle", "source");
series.nodes.get("colors").set("step", 2); // skip every other color, so neighboring nodes differ more
series.nodes.data.setAll([
{ id: "A" },
{ id: "B" },
{ id: "C" },
{ id: "D" },
{ id: "E" },
{ id: "F" }
]);
// a dot on each link travels from source to target, over and over, at its own speed
series.bullets.push(function (_root, _series, dataItem) {
var bullet = am5.Bullet.new(root, {
sprite: am5.Circle.new(root, {
radius: 5, // a 5px dot...
fill: dataItem.get("source").get("fill") // ...in the source node's color
})
});
bullet.animate({
key: "locationY",
to: 1, // from the source end...
from: 0, // ...to the target end
duration: Math.random() * 1000 + 2000, // 2 to 3 seconds per trip
loops: Infinity // over and over
});
return bullet;
});
series.nodes.labels.template.setAll({
textType: "regular", // plain horizontal labels
fill: root.interfaceColors.get("background"), // in the background color, over the node's circle
fontSize: "1.1em", // a little bigger than the chart's text
radius: -5 // 5px in from the ring
});
// a big circle on each node, behind its label
series.nodes.bullets.push(function (_root, _series, dataItem) {
return am5.Bullet.new(root, {
sprite: am5.Circle.new(root, {
radius: 20, // a 20px radius
fill: dataItem.get("fill") // in the node's color
})
});
});
// move the bullets to the bottom layer, under the links and the node labels
series.children.moveValue(series.bulletsContainer, 0);
// Set data
// https://docs.amcharts.com/v5/charts/flow-charts/#setting-data
series.data.setAll([
{ from: "A", to: "D", value: 10 },
{ from: "B", to: "C", value: 8 },
{ from: "B", to: "D", value: 4 },
{ from: "B", to: "E", value: 2 },
{ from: "C", to: "A", value: 14 },
{ from: "C", to: "E", value: 4 },
{ from: "E", to: "D", value: 8 },
{ from: "F", to: "A", value: 7 },
{ from: "D", to: "B", value: 2 }
]);
// 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": "C",
"value": 8
},
{
"from": "B",
"to": "D",
"value": 4
},
{
"from": "B",
"to": "E",
"value": 2
},
{
"from": "C",
"to": "A",
"value": 14
},
{
"from": "C",
"to": "E",
"value": 4
},
{
"from": "E",
"to": "D",
"value": 8
},
{
"from": "F",
"to": "A",
"value": 7
},
{
"from": "D",
"to": "B",
"value": 2
}
]
},
{
"series-0": {
"type": "ChordDirected",
"settings": {
"sourceIdField": "from",
"targetIdField": "to",
"valueField": "value",
"sort": "ascending"
},
"properties": {
"data": "#data-0",
"links": {
"properties": {
"template": {
"settings": {
"fillStyle": "source"
}
}
}
},
"nodes": {
"properties": {
"data": [
{
"id": "A"
},
{
"id": "B"
},
{
"id": "C"
},
{
"id": "D"
},
{
"id": "E"
},
{
"id": "F"
}
],
"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
}
}
}
},
"bullets": [
{
"type": "Bullet",
"settings": {
"sprite": {
"type": "Circle",
"settings": {
"radius": 5,
"fill": "@root.interfaceColors.get('primaryButton')"
}
},
"animations": [
{
"key": "locationY",
"from": 0,
"to": 1,
"duration": 2847.1734985414396,
"loops": 0
}
]
}
}
]
}
}
}
]
}



