WinProbWorm
WinProbWorm plots win probability on a fixed 0–100 axis and marks every lead change.
- encodes
- y = win probability on a fixed 0–100 axis, split at the 50% line
- precision
- high
- nodes
- ≤ 5 + 1 per lead change
A model has the home side at 98% after three lead changes. WinProbWorm plots that series on a fixed 0–100 axis that is never truncated, so a 2-point edge always looks like a 2-point edge. It splits the worm at the 50% line: while your side leads the line reads accent, when it trails it reads neutral, and a dot marks each lead change. A win-probability curve is a modelled read, so the summary always says so, "per the supplied model".
Install
import { WinProbWorm } from "@microcharts/react/win-prob-worm";<WinProbWorm data={[50, 48, 45, 52, 60, 58, 42, 38, 55, 68, 82, 90, 88, 94, 98]} sides={["home", "away"]} title="Win probability"/>Try it
When to use it
Use it for live win or election probability where the lead flips, for a modelled forecast whose crossings are the point, and for any 0–100 probability you must not truncate. For a raw score or margin use Sparkline, and for a value that isn't a bounded 0–100 probability use another chart.
Sizing
Variants
The endpoint carries the current number. At a taller size the biggest single momentum swing also gets a hair connector
and a signed delta; drop that with markSwing={false}, and drop the endpoint probability with label="none".
Edge cases
With a format and locale, the endpoint label and the summary format their numbers in that locale's own decimal mark:
[50, 55.5, 61.2, 58.4, 63.5] in de-DE ends at "63,5%". Values outside 0–100 are clamped to the axis and
dev-warn, since a probability cannot exceed 100. A constant series has no crossing and no swing: an all-50 series reads
"even… throughout", any other constant reads "…holds X% throughout". A null breaks the worm into disconnected runs the
way a gap should. A single point has no line to draw, so it renders just the endpoint dot and its label, and data={[]}
renders the frame with the "no data" summary.
Four homes
Accessibility
The accessible name states the current leader, their probability, the number of lead changes, and the biggest momentum swing: "Per the supplied model, home leads at 98%; 3 lead changes, biggest swing +17 at point 8." A constant series reads "Per the supplied model, even at 50% throughout." The interactive entry roves the points with ←/→ (Home/End jump to the ends), each announcing the leader and probability at that point, with a live readout chip.
The interactive entry follows the shared interaction contract:
arrow keys rove between units on both axes, Home and End jump to the ends, and a click, tap, Enter or
Space selects a unit — pinning its readout so it survives blur, until you select it again or press Escape.
On touch, a tap pins and a drag scrubs.
Props
| Prop | Type | Notes |
|---|---|---|
| data* | number[] | A single win-probability series, clamped to 0–100. |
| sides | [string, string] | Names for the two sides — [>50, <50]. Default ["A", "B"]. |
| label | "last" | "none" | Print the current leader's probability at the endpoint (default "last"). |
| markSwing | boolean | Mark the biggest momentum swing (default true; seat-gated). |
| animate | boolean | interactiveOpt-in entrance motion when the chart mounts client-side — add import "@microcharts/react/motion" once. Inert on the server, on hydrated server HTML, and under prefers-reduced-motion. |
Plus the shared grammar — data, domain, color, title, summary, format — and the layout props (width, height, className, style) that every chart accepts. Interactive entries also share animate and live, and — wherever a chart has more than one navigable unit — onActive, onSelect, selectedIndex and defaultSelectedIndex; and — wherever the chart shows a hover value — readout. See the shared grammar.