# Waveform (/docs/charts/waveform)

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.

```tsx
import { Waveform } from "@microcharts/react/waveform";

<Waveform
  data={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),
  )}
  title="Voice memo"
/>
```

## Install

```tsx
import { Waveform } from "@microcharts/react/waveform";

<Waveform data={samples} title="Voice memo" />
```

Setup (package + stylesheet): [Quickstart](/docs/quickstart#set-up-with-an-ai-agent) or paste [`/agent-setup.md`](/agent-setup.md) into your agent.


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


## Variants

```tsx
<Waveform
  data={Array.from({ length: 200 }, (_, i) => Math.sin(i / 3) * 0.4 * (1 - Math.abs(i - 100) / 220))}
  mode="envelope"
/>
```

```tsx
<Waveform data={[0.2, 0.5, 0.82, 0.4, 0.1]} locale="de-DE" />
```

With a `locale`, the announced peak follows that locale's own decimal mark — "0,82" in German, not "0.82".

## Edge cases

```tsx
<Waveform data={[0.6]} />
```

```tsx
<Waveform data={[0, 0, 0, 0, 0, 0, 0, 0]} />
```

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


## 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](/docs/accessibility#one-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 | Description |
| --- | --- | --- |
| `data` (required) | `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` | (interactive) Opt-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](/docs/quickstart#the-shared-grammar).
