webdevpuneet.com

Chord Diagram Chart — Free HTML CSS JS Snippet

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

CategoryCharts

Copy & paste

Source Code

<div class="app">
  <div class="card">
    <div class="card-header">
      <h3>Cross-Team Handoffs</h3>
      <p class="sub">Ribbon thickness shows how many items moved between two teams. Hover an arc to isolate its ribbons.</p>
    </div>
    <div class="chart-wrap">
      <svg id="chord" viewBox="0 0 480 480" xmlns="http://www.w3.org/2000/svg"></svg>
    </div>
    <div class="legend" id="legend"></div>
  </div>
</div>

Extend with AI

Take This Snippet Further With AI

The AI prompt for this one is for Patreon supporters ($3 a month, 7 days free), along with a site with no ads. Supporters enter the passcode once and it is remembered in this browser.

· Become a supporter

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

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.