daw-ui

Meter

A peak level meter with peak hold and a clip indicator, that never renders through React.

Anatomy

import { Meter } from '@addstack/daw-ui/react';

<Meter.Root read={() => level()}>
  <Meter.Label />
  <Meter.Clip />
  <Meter.Track>
    <Meter.Bar />
    <Meter.Peak />
  </Meter.Track>
</Meter.Root>

Reading the level

read is called once per animation frame and returns the level in dBFS. One shared requestAnimationFrame loop drives every meter on the page and writes the result straight to the DOM, as CSS variables and attributes: a mixer of 64 running meters causes no React renders at all.

With the Web Audio API, read the peak of an AnalyserNode:

import { gainToDecibels } from '@addstack/daw-ui';

const buffer = new Float32Array(analyser.fftSize);

function peak() {
  analyser.getFloatTimeDomainData(buffer);
  let max = 0;
  for (const sample of buffer) max = Math.max(max, Math.abs(sample));
  return gainToDecibels(max);
}

<Meter.Root read={peak} />;

When the level lives in React state instead, pass it as level.

Styling

Bar covers the track and is clipped to the level with clip-path, so a gradient on it stays in place: green at the bottom, red at the top, whatever the level. The root exposes the level and the peak as CSS variables and marks signal and clips with data attributes:

.bar { background: linear-gradient(to top, #22c55e 64%, #eab308 82%, #ef4444 92%); }
.meter:not([data-active]) .peak { display: none; }
.clip[data-clipped] { background: #ef4444; }

To colour the whole meter by its level instead, declare zones={{ warm: -18, hot: -6, clip: 0 }}: the root gets data-zone with the zone the level is in, written from the frame loop only when it changes.

.meter[data-zone="clip"] .bar { background: #ef4444; }

API reference

Root

Reads the level and runs the ballistics. Renders a <div>.

Prop

Type

Data attribute
data-orientation"vertical" or "horizontal".
data-activeWhile the peak is above min.
data-clippedAfter the level went above clipAbove, until the clip indicator is pressed.
data-zoneThe zone of zones the level is in.
CSS variable
--meter-levelTravel of the level, from 0 to 1.
--meter-peakTravel of the held peak, from 0 to 1.

Track

The scale the level fills. Renders a <div> with role="meter"; its aria-valuenow and aria-valuetext are updated four times a second, not on every frame. Name it with Meter.Label or an aria-label.

Prop

Type

Bar

The lit part. Renders a <div> that covers the track, clipped to the level.

Prop

Type

Peak

The peak-hold marker; its edge sits at the recent peak. Renders a <div>.

Prop

Type

Clip

The clip indicator: a <button> with data-clipped that resets it when pressed. Place it outside Meter.Track (a meter's content is hidden from assistive technology) and give it an aria-label in your application's language.

Prop

Type

Label

Names the meter. Renders a <span>.

Prop

Type

On this page