Waveform
React waveform chart — signal shape compressed by max-per-bucket at word width. Tiny accessible SVG microchart; RSC-safe static or /interactive.
Waveform answers "what is the shape of a high-frequency signal — where are its spikes and silences — at word width?". It compresses with max-per-bucket, never a mean, so a single spike survives the squeeze to a sparkline width. The peak is disclosed in the accessible summary.
Install
import { Waveform } from "@microcharts/react/waveform";// samples — real values under “Sample data” below<Waveform data={samples} title="Voice memo" />Sample data
const samples = Array.from( { length: 200 }, (_, i) => (i === 126 ? 0.82 : Math.sin(i / 3) * 0.15 + Math.sin(i / 11) * 0.35) * (1 - Math.abs(i - 100) / 260),);Try it
When to use it
- Good for — voice-memo / audio scrubbers, high-frequency log or request volume.
- Avoid for — exact values (Sparkline) or categorical state (Hypnogram).
Sizing
Variants
With a locale, the announced peak follows that locale's own decimal mark — "0,82" in German, not "0.82".
Edge cases
A single sample renders as one bucket, top-to-bottom. All-zero data renders every bucket at the shared 0.4-unit "silent"
tick height, and the accessible summary says "Silent." rather than reporting a zero peak. A null in the data
behaves the same as a true zero for any bucket where it's the only sample — maxPerBucket skips non-finite values when
picking the bucket's peak, and a bucket with nothing finite in it falls back to that same silence tick, so a genuine gap
and real silence render identically here (unlike Sparkline's line break).
Four homes
Why this default
Bars with max-per-bucket are the only compression that can't hide an incident — averaging a bucket would erase the very
spike someone is looking for. The auto domain is symmetric ±max|data|, which reads shape honestly and discloses the peak
in the summary; for absolute loudness comparison across rows, pass an explicit shared domain so quiet data is never
silently rescaled to look loud.
Accessibility
The accessible name discloses the peak — "Peak 0.398 at 50% through 200 samples." Pure silence reads "Silent." rather than blank. The interactive entry roves the buckets, announcing each bucket's position and peak amplitude.
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[] | Amplitude samples; negatives allowed. |
| progress | number | 0–1 played fraction; left buckets tint accent. |
| mode | "bars" | "envelope" | Envelope draws the min/max area. |
| mirror | boolean | Mirror around center; false for magnitude-only. |
| 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.
Related charts
EtaBar
React ETA bar chart — remaining time sized by the observed rate, not linear interpolation. Tiny accessible SVG microchart; RSC-safe static or /interactive.
EventRaster
React event raster chart — multi-lane event ticks for sync at a glance. Tiny accessible SVG microchart; RSC-safe static or /interactive.