Skip to content
NewYour data: charts, lists and meters that take your records
<GeoLatencyMatrix />ReactNetwork & latencyFree

Latency matrix

Round-trip times between regions as dot blocks that fill with latency; a map beside the grid slides the measured route from pair to pair.

View as Markdown
<GeoLatencyMatrix />

Tryhold an arrow key in the grid and watch the route slide across the map.

Your data

Pass your regions as regions and your round-trip times as measurements. Without either the component shows eight demo regions with demo latencies (the fibre minimum times a seeded stretch of 1.3 to 1.9); regions alone shows every pair as "no data". See Your data.

Playground

Change a prop and the component re-renders. Props marked remounts set an initial value, so the component starts over.

Loading <GeoLatencyMatrix />
Props
slowMslive
symmetriclive
labellive
React
<GeoLatencyMatrix slowMs={250} symmetric label="Latency between regions" />

Installation

pnpm dlx shadcn@latest add @lumesec/geo-latency-matrix
First timeRegister the @lumesec registry once, or install by URL+

Adds @lumesec to your components.json:

pnpm dlx shadcn@latest registry add @lumesec=https://elements.lumesec.ai/r/{name}.json

Or skip the setup and install by URL:

npx shadcn@latest add https://elements.lumesec.ai/r/geo-latency-matrix.json

Usage

geo-latency-matrix-example.tsx
"use client";

import * as React from "react";

import { GeoLatencyMatrix, type LatencyMeasurement, type LatencyPair, type LatencyRegion } from "@/components/lumesec/geo-latency-matrix";

const regions: LatencyRegion[] = [
  { id: "eu-central", city: "Frankfurt", lat: 50.11, lon: 8.68 },
  { id: "us-east", city: "Ashburn", lat: 39.04, lon: -77.49 },
  { id: "ap-southeast", city: "Singapore", lat: 1.35, lon: 103.82 },
];

export function Example({ measurements }: { measurements: LatencyMeasurement[] }) {
  const [pair, setPair] = React.useState<LatencyPair | null>({ from: "eu-central", to: "ap-southeast" });
  return <GeoLatencyMatrix regions={regions} measurements={measurements} value={pair} onValueChange={setPair} slowMs={200} />;
}

Behaviour

Round-trip times between regions as a grid of dot blocks: the slower the pair, the more dots are lit. Pairs above a threshold get a warning mark.

Point at a cell, or move through the grid with the arrow keys, and the mini map beside it shows that pair: the measured route against a faint arc for the fibre minimum, with the milliseconds and the path stretch rolling. The route slides from pair to pair instead of redrawing.

API reference

Props

Also accepts every prop of <div> (React.ComponentProps<"div">), spread onto the root element.

regionsTypereadonly LatencyRegion[]Default8 demo regions

Rows and columns, in order: { id, label?, city?, lat, lon }. label is the header text (default: the id), city the place name in the detail panel and the accessible names (default: the label). A GeoRegion from @/lib/lumesec/geo-data fits. Regions with a repeated id or a non-finite coordinate are skipped.

measurementsTypereadonly LatencyMeasurement[]Defaultdemo latencies

Round-trip times { from, to, ms } with region ids: from is the row, to the column. Entries with an unknown id or without a finite, non-negative ms are skipped; the last entry for a direction wins. Pairs without a measurement show "no data".

symmetricTypebooleanDefaulttrue

Uses a measurement for both directions when only one direction is given. A measurement given for a direction always wins over its mirror.

valueTypeLatencyPair | null

Selected pair { from, to } (controlled), or null for none. Use with onValueChange.

defaultValueTypeLatencyPair | nullDefaultfirst off-diagonal pair

Selected pair when uncontrolled.

onValueChangeType(value: LatencyPair | null) => void

Called with the new pair when the user selects a different cell: click, tap, Enter or Space.

slowMsTypenumberDefault250

Pairs at or above this round-trip time in ms get a warning mark in their cell, a Slow badge in the detail panel and "slow" in their accessible name.

labelTypestringDefault"Latency between regions"

Accessible name of the grid.

Ref

ref points at the root HTMLDivElement.

Keyboard

KeysAction
TabMoves into the grid, to the selected cell (one roving tab stop), and out again.
ArrowUp / ArrowDown / ArrowLeft / ArrowRightMoves one cell. The detail panel and the map follow the focused cell.
Home / EndMoves to the first or last cell of the row.
Ctrl+Home / Ctrl+EndMoves to the first or last cell of the grid.
Enter / SpaceSelects the focused pair.

Accessibility

  • The matrix is an ARIA grid (role="grid" with rowgroup, row, columnheader, rowheader and gridcell), named by label. The cells share one roving tab stop.
  • Each cell's aria-label reads like "Frankfurt to Singapore, 162 milliseconds, 61 % above fibre minimum", with "no data", "same region" or "slow" where they apply. The selected cell has aria-selected="true".
  • The header above the row headers is named "From"; rows are the regions a measurement starts from, columns the regions it goes to.
  • The detail panel is DOM text. The map has role="img" and an aria-label that names the route; its canvases and endpoint labels are aria-hidden.
  • The focused or hovered cell is marked by an accent frame on the matrix canvas, and its row and column headers are highlighted.
  • Reduced motion: the route jumps between pairs, the frame jumps between cells, numbers swap without rolling, the blocks show at once, and the cell twinkle, the looping packet, the ping and the sparks are off.

Theming

Styled with Tailwind classes on your shadcn theme tokens, so light and dark follow your theme. The accent comes from --lumesec. See Theming.

This component reads --lumesec, --lumesec-shine, --lumesec-warning, --foreground, --muted-foreground, --muted, --card and --border.

Notes

  • Each cell is a 5 × 5 block of 2 px dots. Its level is log(ms / fastest) / log(slowest / fastest) over the pairs off the diagonal, quantised to 25 steps, so the fastest pair lights one dot and the slowest all 25. Dots light in a fixed dispersed order and lean from the land grey toward --lumesec as the level rises.
  • The diagonal shows a single centre dot; pairs without a measurement show every other dot, faint, and read "no data". The legend under the grid gives the fastest and slowest time and shows the marks that are in use.
  • The fibre minimum is the great-circle distance at 204 km per ms each way. Stretch is the measured time over that minimum, minus 1. The measured route on the map bends away from the great circle, by 0.12 times the ratio and at most 0.3.
  • The route's two endpoints are springs in screen space, and the curves are rebuilt from them every frame, so moving across the grid slides and reshapes the route. Pairs that cross the 170° W edge take the shorter way and continue from the other edge.
  • Selecting a pair rings the land dots around its destination and sends a few sparks past the map's edge. Hover and focus preview a pair; leaving the grid returns to the selected one.
  • When measurements changes, every block springs to its new dot count, filling or thinning in rank order, and the numbers roll in the direction of change.
  • The map is equirectangular, 50° S to 70° N, with its central meridian at 10° E. At 560 px and wider the map sits beside the grid; below that it moves under it, and below 420 px the cells shrink from 24 to 20 px.
  • The animation runs only while the component is on screen. Colours follow the theme and are re-read when it changes.

Was this page helpful?