Chord Diagram Chart — Free HTML CSS JS Snippet

Chord Diagram Chart · Charts · Plain HTML, CSS & JS · Live preview

Share & Support

What's included

Features

Data model is a plain adjacency matrix — matrix[i][j] is the flow from group i to group j, independently of matrix[j][i]
Arc length per group is proportional to total involvement (outgoing plus incoming), not just a fixed equal division
Each arc sub-divides into per-connection segments sized by that specific pairwise flow amount
Ribbons are single closed SVG paths using arc plus quadratic Bézier segments bowing through the circle center
Ribbon fill color reflects whichever side of a pairwise relationship sent the larger amount
Hover-to-isolate interaction dims every non-connected ribbon via shared data-a/data-b attribute lookups
Legend entries and arcs are cross-linked — hovering either isolates the same category's ribbons
Native SVG title tooltips on every ribbon report the exact combined flow amount for that pair
Layout automatically adapts to however many categories exist in the NAMES and matrix arrays

About this UI Snippet

Chord Diagram Chart — Circular Flow Visualization Built from an Adjacency Matrix

Screenshot of the Chord Diagram Chart snippet rendered live

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 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:

text
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

  1. 1
    Read 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.
  2. 2
    Read 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.
  3. 3
    Hover 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.
  4. 4
    Check exact numbersHover any ribbon directly to see a native tooltip with the exact combined handoff count between that pair of categories.
  5. 5
    Edit 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].
  6. 6
    Add 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

Cross-team or cross-department flow analysis
Visualize handoffs, referrals, or dependencies between teams, departments, or services, revealing which pairs exchange the most work relative to a plain Sankey diagram view.
Trade, migration, or transaction flow visualization
Chord diagrams are a standard way to show flows between countries, regions, or accounts — imports and exports, migration counts, or fund transfers between entities.
Relationship and network analysis dashboards
Show pairwise interaction volume between any set of categories, such as customer segments cross-purchasing between product lines.
Teaching circular layout and adjacency-matrix visualization
A concrete, from-scratch example of converting a matrix of pairwise values into proportional arcs and ribbons using polar coordinates and Bézier curves.
Reference for SVG ribbon and arc path construction
The arcPath() and ribbon-path construction logic are small, dependency-free, and reusable anywhere a project needs circular flow or relationship visualization without a charting library.

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.