# Plot

> The <audio-ui-plot> custom element of @audio-ui/elements: its attributes, its parts and a live preview.

`<audio-ui-plot>` defines two axes and lays out everything drawn on them in axis units, for response curves, filter displays and transfer functions. Each drawing part renders its own SVG covering the plot, in `currentColor`, with a constant stroke width. Curves take their function and traces their live source as properties:

## Usage

{/* usage:start: written by `bun scripts/element-usage.ts --write` */}

The styles read the theme's CSS variables, which [Styling](/docs/elements#styling) sets.

**JavaScript**

*With CSS*

```html
<style>
  audio-ui-plot {
    display: block;
    width: 100%;
    max-width: 32rem;
    height: calc(var(--spacing, 0.25rem) * 44);
    border-radius: calc(var(--radius, 0.625rem) - 2px);
    background: var(--muted, oklch(0.97 0 0));
  }

  audio-ui-plot-grid {
    color: var(--muted-foreground, oklch(0.556 0 0));
    font-size: 10px;
  }

  audio-ui-plot-grid [data-part="line"] {
    stroke: var(--border, oklch(0.922 0 0));
  }

  audio-ui-plot-grid [data-part="label"][data-axis="x"] {
    bottom: calc(var(--spacing, 0.25rem) * 1);
    translate: 0.25rem 0;
  }

  audio-ui-plot-grid [data-part="label"][data-axis="y"] {
    left: calc(var(--spacing, 0.25rem) * 1);
    translate: 0 -100%;
  }

  audio-ui-plot-curve {
    color: color-mix(in oklab, var(--primary, oklch(0.205 0 0)) 15%, transparent);
  }

  audio-ui-plot-handle {
    width: calc(var(--spacing, 0.25rem) * 4);
    height: calc(var(--spacing, 0.25rem) * 4);
    border-radius: var(--radius, 0.625rem);
    background: var(--primary, oklch(0.205 0 0));
    box-shadow: 0 0 0 4px color-mix(in oklab, var(--primary, oklch(0.205 0 0)) 20%, transparent);
    cursor: grab;
  }
</style>

<audio-ui-plot min-x="20" max-x="20000" min-y="-18" max-y="18" scale-x="log">
  <audio-ui-plot-grid></audio-ui-plot-grid>
  <audio-ui-plot-curve fill-to="0"></audio-ui-plot-curve>
  <audio-ui-plot-handle aria-label="Band" value="800 6"></audio-ui-plot-handle>
</audio-ui-plot>

<script type="module">
  import "@audio-ui/elements/plot";

  const plotHandle = document.querySelector("audio-ui-plot-handle");

  // The curve: its level in dB at each frequency, such as a filter's response.
  const response = (hz) => 0;
  document.querySelector("audio-ui-plot-curve").fn = response;
  plotHandle.addEventListener("input", (event) =>
    console.log(event.detail.value)
  );
</script>
```

*With Tailwind*

```html
<audio-ui-plot class="block h-44 w-full max-w-lg rounded-md bg-muted" min-x="20" max-x="20000" min-y="-18" max-y="18" scale-x="log">
  <audio-ui-plot-grid class="text-[10px] text-muted-foreground [&_[data-part=label][data-axis=x]]:bottom-1 [&_[data-part=label][data-axis=x]]:translate-x-1 [&_[data-part=label][data-axis=y]]:left-1 [&_[data-part=label][data-axis=y]]:-translate-y-full [&_[data-part=line]]:stroke-border"></audio-ui-plot-grid>
  <audio-ui-plot-curve class="text-primary/15" fill-to="0"></audio-ui-plot-curve>
  <audio-ui-plot-handle class="size-4 cursor-grab rounded-lg bg-primary ring-4 ring-primary/20" aria-label="Band" value="800 6"></audio-ui-plot-handle>
</audio-ui-plot>

<script type="module">
  import "@audio-ui/elements/plot";

  const plotHandle = document.querySelector("audio-ui-plot-handle");

  // The curve: its level in dB at each frequency, such as a filter's response.
  const response = (hz) => 0;
  document.querySelector("audio-ui-plot-curve").fn = response;
  plotHandle.addEventListener("input", (event) =>
    console.log(event.detail.value)
  );
</script>
```

**Vue**

*With CSS*

```vue
<script setup lang="ts">
import "@audio-ui/elements/plot";
import { ref } from "vue";

const band = ref({ x: 800, y: 6 });

// The curve: its level in dB at each frequency, such as a filter's response.
const response: (hz: number) => number = (hz) => 0;
</script>

<template>
  <audio-ui-plot min-x="20" max-x="20000" min-y="-18" max-y="18" scale-x="log">
    <audio-ui-plot-grid />
    <audio-ui-plot-curve fill-to="0" :fn="response" />
    <audio-ui-plot-handle aria-label="Band" v-model="band" />
  </audio-ui-plot>
</template>

<style>
audio-ui-plot {
  display: block;
  width: 100%;
  max-width: 32rem;
  height: calc(var(--spacing, 0.25rem) * 44);
  border-radius: calc(var(--radius, 0.625rem) - 2px);
  background: var(--muted, oklch(0.97 0 0));
}

audio-ui-plot-grid {
  color: var(--muted-foreground, oklch(0.556 0 0));
  font-size: 10px;
}

audio-ui-plot-grid [data-part="line"] {
  stroke: var(--border, oklch(0.922 0 0));
}

audio-ui-plot-grid [data-part="label"][data-axis="x"] {
  bottom: calc(var(--spacing, 0.25rem) * 1);
  translate: 0.25rem 0;
}

audio-ui-plot-grid [data-part="label"][data-axis="y"] {
  left: calc(var(--spacing, 0.25rem) * 1);
  translate: 0 -100%;
}

audio-ui-plot-curve {
  color: color-mix(in oklab, var(--primary, oklch(0.205 0 0)) 15%, transparent);
}

audio-ui-plot-handle {
  width: calc(var(--spacing, 0.25rem) * 4);
  height: calc(var(--spacing, 0.25rem) * 4);
  border-radius: var(--radius, 0.625rem);
  background: var(--primary, oklch(0.205 0 0));
  box-shadow: 0 0 0 4px color-mix(in oklab, var(--primary, oklch(0.205 0 0)) 20%, transparent);
  cursor: grab;
}
</style>
```

*With Tailwind*

```vue
<script setup lang="ts">
import "@audio-ui/elements/plot";
import { ref } from "vue";

const band = ref({ x: 800, y: 6 });

// The curve: its level in dB at each frequency, such as a filter's response.
const response: (hz: number) => number = (hz) => 0;
</script>

<template>
  <audio-ui-plot class="block h-44 w-full max-w-lg rounded-md bg-muted" min-x="20" max-x="20000" min-y="-18" max-y="18" scale-x="log">
    <audio-ui-plot-grid class="text-[10px] text-muted-foreground [&_[data-part=label][data-axis=x]]:bottom-1 [&_[data-part=label][data-axis=x]]:translate-x-1 [&_[data-part=label][data-axis=y]]:left-1 [&_[data-part=label][data-axis=y]]:-translate-y-full [&_[data-part=line]]:stroke-border" />
    <audio-ui-plot-curve class="text-primary/15" fill-to="0" :fn="response" />
    <audio-ui-plot-handle class="size-4 cursor-grab rounded-lg bg-primary ring-4 ring-primary/20" aria-label="Band" v-model="band" />
  </audio-ui-plot>
</template>
```

**Svelte**

*With CSS*

```svelte
<script lang="ts">
  import "@audio-ui/elements/plot";

  let band = $state({ x: 800, y: 6 });

  // The curve: its level in dB at each frequency, such as a filter's response.
  const response: (hz: number) => number = (hz) => 0;
</script>

<audio-ui-plot min-x="20" max-x="20000" min-y="-18" max-y="18" scale-x="log">
  <audio-ui-plot-grid></audio-ui-plot-grid>
  <audio-ui-plot-curve fill-to="0" fn={response}></audio-ui-plot-curve>
  <audio-ui-plot-handle aria-label="Band" value={band} oninput={(event) => (band = event.detail.value)}></audio-ui-plot-handle>
</audio-ui-plot>

<style>
:global {
  audio-ui-plot {
    display: block;
    width: 100%;
    max-width: 32rem;
    height: calc(var(--spacing, 0.25rem) * 44);
    border-radius: calc(var(--radius, 0.625rem) - 2px);
    background: var(--muted, oklch(0.97 0 0));
  }

  audio-ui-plot-grid {
    color: var(--muted-foreground, oklch(0.556 0 0));
    font-size: 10px;
  }

  audio-ui-plot-grid [data-part="line"] {
    stroke: var(--border, oklch(0.922 0 0));
  }

  audio-ui-plot-grid [data-part="label"][data-axis="x"] {
    bottom: calc(var(--spacing, 0.25rem) * 1);
    translate: 0.25rem 0;
  }

  audio-ui-plot-grid [data-part="label"][data-axis="y"] {
    left: calc(var(--spacing, 0.25rem) * 1);
    translate: 0 -100%;
  }

  audio-ui-plot-curve {
    color: color-mix(in oklab, var(--primary, oklch(0.205 0 0)) 15%, transparent);
  }

  audio-ui-plot-handle {
    width: calc(var(--spacing, 0.25rem) * 4);
    height: calc(var(--spacing, 0.25rem) * 4);
    border-radius: var(--radius, 0.625rem);
    background: var(--primary, oklch(0.205 0 0));
    box-shadow: 0 0 0 4px color-mix(in oklab, var(--primary, oklch(0.205 0 0)) 20%, transparent);
    cursor: grab;
  }
}
</style>
```

*With Tailwind*

```svelte
<script lang="ts">
  import "@audio-ui/elements/plot";

  let band = $state({ x: 800, y: 6 });

  // The curve: its level in dB at each frequency, such as a filter's response.
  const response: (hz: number) => number = (hz) => 0;
</script>

<audio-ui-plot class="block h-44 w-full max-w-lg rounded-md bg-muted" min-x="20" max-x="20000" min-y="-18" max-y="18" scale-x="log">
  <audio-ui-plot-grid class="text-[10px] text-muted-foreground [&_[data-part=label][data-axis=x]]:bottom-1 [&_[data-part=label][data-axis=x]]:translate-x-1 [&_[data-part=label][data-axis=y]]:left-1 [&_[data-part=label][data-axis=y]]:-translate-y-full [&_[data-part=line]]:stroke-border"></audio-ui-plot-grid>
  <audio-ui-plot-curve class="text-primary/15" fill-to="0" fn={response}></audio-ui-plot-curve>
  <audio-ui-plot-handle class="size-4 cursor-grab rounded-lg bg-primary ring-4 ring-primary/20" aria-label="Band" value={band} oninput={(event) => (band = event.detail.value)}></audio-ui-plot-handle>
</audio-ui-plot>
```

**React**

In React, the same control is the [Plot](/docs/react/plot) primitive of `@audio-ui/react`, with the same parts as React components and its value as props.

{/* usage:end */}

## API reference

```bash
npm install @audio-ui/elements
```

```ts
import "@audio-ui/elements/plot";
```

| Export | Kind | What it is |
| --- | --- | --- |
| `PLOT_TAGS` | const | The names the plot's elements are registered under, frozen: a page cannot rename them by accident. |
| `PlotBandElement` | class | The range of one `axis` between `from` and `to`, spanning the other axis, such as a sweep range or a level above a threshold. |
| `PlotCursorElement` | class | Tracks a mouse or pen over the plot, for crosshairs and readouts. |
| `PlotCursorReadoutElement` | class | Renders the value under the pointer as text, inside an `<audio-ui-plot-cursor>`. |
| `PlotCurveElement` | class | Plots `y = fn(x)` across the plot in axis units, from the `fn` property, such as a frequency response in dB. |
| `PlotDotElement` | class | A non-interactive point, such as the input level on a compressor curve. |
| `PlotDotSource` | type | A live point, in the axes' units. |
| `PlotElement` | class | The root of a plot. |
| `PlotGridElement` | class | Grid lines and labels at round values along the axes. |
| `PlotHandleChangeEvent` | type | Fired once a gesture that moved a handle ends, and after each key or input change. |
| `PlotHandleElement` | class | A draggable point in axis units, such as an equalizer band or a filter's cutoff and resonance. |
| `PlotHandleElementEventMap` | interface | The events a handle fires, as `addEventListener` types them. |
| `PlotHandleEventDetail` | interface | What a handle's `input` and `change` events carry, besides the handle itself as their target. |
| `PlotHandleInputEvent` | type | Fired on every move of a handle. |
| `PlotLineElement` | class | A straight line at `value`: vertical on the x axis (`axis="x"`, the default), such as a cutoff, or horizontal on the y axis, such as 0 dB. |
| `PlotMarkElement` | class | A label at `value` on one `axis`, such as "1k" or "+12", positioned with `left` (x, the default) or `top` (y). |
| `PlotSnapshot` | interface | What every part draws from: the plot's two axes, and whether it is disabled. |
| `PlotTraceElement` | class | A live line from the y values of the `source` property, such as a spectrum, spread evenly across the x axis and redrawn on each change. |
| `PlotTraceSource` | type | A live list of y values, spread evenly across the x axis. |

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `min-x` | `number` | `0` |  |
| `max-x` | `number` | `1` |  |
| `min-y` | `number` | `0` |  |
| `max-y` | `number` | `1` |  |
| `scale-x` | `"linear" \| "log"` | `"linear"` | The `scaleX` property also accepts a custom `{ toPosition, toValue }`. |
| `scale-y` | `"linear" \| "log"` | `"linear"` |  |
| `locale` | `string` |  | Locale of the handles' values. Defaults to the browser locale. |
| `disabled` | `boolean` |  |  |

The `valueAt(clientX, clientY)` method converts a pointer position into axis units.

| Part | Description |
| --- | --- |
| `<audio-ui-plot-curve>` | `y = fn(x)` across the plot, from the `fn` property. `fill-to` closes it to a y value as an area; `samples` sets the initial sampling (256), refined adaptively at sharp bends |
| `<audio-ui-plot-line>` | A line at `value`: vertical on the x `axis` (default), horizontal on the y axis |
| `<audio-ui-plot-band>` | The range of one `axis` between `from` and `to`, spanning the other axis |
| `<audio-ui-plot-trace>` | A live line from the `source` property, `{ getSnapshot, subscribe }`, redrawn on each change. Supports `fill-to`; `min` and `max` set an independent value scale |
| `<audio-ui-plot-grid>` | Grid lines and labels at round values. `axis` (`x`, `y` or `both`), `x-ticks` and `y-ticks`, `x-count` and `y-count`, `labels` (`none` or an axis), and a `format(value, axis)` property. Lines carry `data-part="line"`, labels `data-part="label"`, both `data-axis`, and the zero line `data-zero` |
| `<audio-ui-plot-mark>` | A label at `value` on one `axis`, hidden from assistive technology |
| `<audio-ui-plot-dot>` | A non-interactive point: a static `value` (`"x y"`) or the live point of its `source` property |
| `<audio-ui-plot-handle>` | A draggable point, with `value` and `default-value` as `"x y"`, optionally constrained to one `axis`. Relative drag (Shift for fine), reset on double click, Alt + click or Delete. Contains a native range input per axis, labelled from its `aria-label` ("Low X") or `label-x` and `label-y`. Fires `input` and `change` with `{ value, reason }` |
| `<audio-ui-plot-cursor>` | Tracks a mouse or pen over the plot, setting `--plot-cursor-x`, `--plot-cursor-y` and `data-active` |
| `<audio-ui-plot-cursor-readout>` | Inside the cursor, the value under the pointer as text, formatted by its `format` property |

**In React**

The same control in React, and its parts' props: [Plot](/docs/react/plot).

