Chord Diagram Chart — Free HTML CSS JS Snippet
Chord Diagram Chart · Charts · Plain HTML, CSS & JS · Live preview
What's included
Features
About this UI Snippet
Chord Diagram Chart — Circular Flow Visualization Built from an Adjacency Matrix

A chord diagram arranges categories as arcs around a circle and draws a ribbon between every pair with a nonzero flow, where each ribbon's width represents the size of that flow. It is the standard way to visualize relationships or transfers between many entities at once — who sends what to whom — when a Sankey diagram's left-to-right layout doesn't fit because the flows are bidirectional or cyclic (any category can send to, and receive from, any other) rather than moving through discrete left-to-right stages.
Representing flows as an adjacency matrix
The data model is a square matrix where matrix[i][j] is the amount that flowed from group i to group j — here, work items handed off from one team to another. Unlike a Sankey diagram's stage-based node list, a matrix naturally allows matrix[i][j] and matrix[j][i] to both be nonzero and different (Frontend hands off 24 items to Backend, while Backend hands back only 10 in the other direction), which is exactly the kind of bidirectional relationship a chord diagram is built to show that a strictly directional layout cannot.
Sizing each group's arc proportionally to its total involvement
Each group's outer arc length is proportional to its groupTotals[i] — the sum of everything it sent out (rowTotals[i]) plus everything it received (colTotals[i]) — divided by the grandTotal across all groups, scaled to fill 2π radians minus small fixed gaps between arcs. A group involved in a lot of handoffs, in either direction, gets a visually larger arc than one with only a little total flow, giving an immediate at-a-glance sense of which categories are the busiest hubs.
Sub-dividing each arc into per-connection segments
Within a single group's arc span, the code further divides it into subSegments — one smaller segment per other group it exchanges with, sized proportionally to that specific flow amount relative to the group's own total. Outgoing flows are placed first around the arc, followed by incoming flows, so each group's arc reads, walking around it, as "here's how each of my outgoing flows breaks down, then here's how each of my incoming flows breaks down." These segment boundaries are exactly where each connecting ribbon attaches to the arc.
Drawing a ribbon between two arcs
For every unordered pair of groups with any nonzero flow in either direction, one ribbon is drawn as a single closed SVG path: two arcs along the inner radius (each following that pair's matching sub-segment boundaries computed above) connected by two quadratic Bézier curves (Q commands) that bow through the circle's center point. This is the classic chord-diagram ribbon shape — a band that appears to twist from one arc's edge to the other's, with its width at each end reflecting the actual flow size in that direction. The ribbon's fill color is taken from whichever of the two groups sent the larger amount, so at a glance the color hints at which direction dominates that pairwise relationship.
Hover-to-isolate interaction
Because a busy chord diagram can have many overlapping ribbons, hovering any arc or its matching legend entry calls setActive(idx), which checks every ribbon's stored data-a/data-b group indices and dims (opacity near zero) every ribbon not touching the hovered group while boosting the opacity of every ribbon that does — instantly isolating one category's full set of relationships out of an otherwise dense tangle of bands.
Build with AI
Build, Understand, Optimize, and Extend It With AI
Paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to explain exactly how groupTotals and subSegments work together to size and position each arc and its per-connection segments, and why each ribbon is built from two arc commands and two quadratic Bézier curves rather than a simpler shape. It's also a good candidate for extension — ask it to add click-to-pin isolation (so a category stays highlighted after the mouse leaves, useful on touch devices), animate ribbons growing in from zero width on load, or add a directional indicator like a small arrowhead or gradient along each ribbon to make the dominant flow direction readable without needing to hover.
Prompt to recreate it
Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:
Build a chord diagram chart in plain HTML, CSS, and JavaScript using inline SVG created with createElementNS — no charting library, no canvas.
Requirements:
- Represent the data as a square matrix where matrix[i][j] is the flow amount from category i to category j, and matrix[j][i] can be a different, independent value representing the reverse direction.
- Compute each category's total involvement as the sum of its outgoing flows (its row total) plus its incoming flows (its column total), and size that category's outer arc proportionally to its share of the total involvement across all categories, arranging all arcs around a full circle with small fixed gaps between them.
- Within each category's arc, further divide it into smaller segments — one per other category it has any nonzero flow with — sized proportionally to that specific pairwise flow amount, with outgoing-flow segments placed before incoming-flow segments around the arc.
- For every pair of categories with a nonzero flow in either direction, draw one ribbon as a single closed SVG path connecting the two categories' matching segment boundaries along the inner radius, using two quadratic Bézier curves that bow through the circle's exact center point to create the classic twisting chord-ribbon shape.
- Color each ribbon based on whichever of the two connected categories sent the larger amount in that relationship, and give it a semi-transparent fill so overlapping ribbons remain visually distinguishable.
- Add a hover interaction on both the arcs and a text legend that dims every ribbon not connected to the hovered category while boosting the opacity of every ribbon that is connected to it, and a native tooltip on each ribbon reporting the exact combined flow amount between that pair.Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.
Step by step
How to Use
- 1Read the outer arcsEach colored arc is one category; its length is proportional to that category's total flow, both sent and received, relative to the whole dataset.
- 2Read the ribbonsEach band connecting two arcs represents the flow between that pair; its width at each end shows how much moved in each direction, and its color reflects whichever side sent more.
- 3Hover an arc or legend entry to isolate itsetActive(idx) dims every ribbon that does not touch the hovered category, letting you trace one category's full set of relationships instantly.
- 4Check exact numbersHover any ribbon directly to see a native tooltip with the exact combined handoff count between that pair of categories.
- 5Edit the dataUpdate the NAMES array and the matrix of flow values — matrix[i][j] is the amount flowing from group i to group j, and can differ from matrix[j][i].
- 6Add more categoriesAdd an entry to NAMES and COLORS, and a corresponding new row plus a new column entry in every existing row of matrix. The layout code automatically adapts to however many groups exist.
Real-world uses
Common Use Cases
Got questions?
Frequently Asked Questions
A Sankey diagram arranges nodes in ordered left-to-right stages and flows move strictly forward between them. A chord diagram arranges all categories around a single circle instead, which naturally supports bidirectional or cyclic relationships — any category can both send to and receive from any other category, shown as one ribbon with potentially different widths on each end.
Each group's arc length is proportional to groupTotals[i], the sum of everything that group sent out (its row total) plus everything it received (its column total), divided by the grand total across all groups and all flows, then scaled to fill the full circle minus small fixed gaps between arcs.
Each ribbon is one closed SVG path: an arc along the inner radius at one group's sub-segment boundary, a quadratic Bézier curve (Q command) bowing through the circle's exact center to the other group's matching sub-segment boundary, another inner-radius arc there, and a second Bézier curve back through the center to close the shape.
The ribbon fill uses whichever group sent the larger amount in that pairwise relationship (matrix[i][j] versus matrix[j][i]), giving a quick color-based hint about which direction of the relationship dominates without needing a separate directional indicator like an arrowhead.
Yes — that is the normal case. matrix[i][j] and matrix[j][i] are independent values, so a ribbon can be visibly wider at one end than the other, directly showing an imbalanced relationship (for example, one team sending far more handoffs than it receives back from the same team).
Yes. Use the JSX, Vue, Angular, or Tailwind export buttons on this page. In React, recompute the group angles, sub-segments, and ribbon paths from your matrix data during render (memoizing with useMemo since the layout math runs over every pair of groups) and manage the hovered index in state instead of toggling classList directly.