3.7 KiB
<MeterPeak> — peak meter with hold + decay
(no submodules)
Purpose
<MeterPeak> is a digital-style peak meter that shows the instantaneous
input level alongside a held-then-decaying peak marker. After every new
sample exceeds the held maximum, the marker freezes for holdMs and
then decays linearly at decayPerSec level-units per second. The
default 1000 ms hold + 1.4-per-second decay matches the legibility
profile common in commercial DAWs.
This component intentionally does not include ballistics — for analogue
VU response use <MeterVU>. <MeterPeak> is the right choice for
clip detection, true-peak indication, and any time the user must see
the actual recent maximum rather than a perceptual average.
Features
- Independent
value(filled bar) andpeak(hold marker) channels - Configurable hold window (
holdMs) and decay rate (decayPerSec) - Optional
peakprop — when omitted, derived fromvalue role="meter"witharia-valuetextdescribing both level and peak- CSS-variable themed fill and hold marker
data-level/data-peakdeterministic E2E hooks
Usage: Basic
import { MeterPeak } from "@suaveplan/react-control-surface";
<MeterPeak value={0.4} peak={0.7} label="L" />;
Usage: Advanced
import { MeterPeak } from "@suaveplan/react-control-surface";
function ClipDetector({ stream }: { stream: AsyncIterable<number> }) {
const [v, setV] = useState(0);
useEffect(() => {
let cancelled = false;
(async () => {
for await (const s of stream) {
if (cancelled) return;
setV(s);
}
})();
return () => {
cancelled = true;
};
}, [stream]);
return (
<MeterPeak
value={v}
holdMs={2000}
decayPerSec={0.6}
label="Clip"
/>
);
}
API Reference
| Prop | Type | Default | Description |
|---|---|---|---|
value |
number |
required | Instantaneous level in [0, 1]. |
peak |
number |
derived | External peak source. Falls back to value. |
holdMs |
number |
1000 |
Hold time after each new maximum. |
decayPerSec |
number |
1.4 |
Linear decay rate once the hold expires. |
width |
number |
160 |
Root width in CSS pixels. |
height |
number |
24 |
Root height in CSS pixels. |
label |
string |
— | Visible + accessible label. |
ariaLabel |
string |
derived | Override the accessible label. |
className |
string |
— | Appended to rcs-meter-peak. |
The exported stepPeakHold(held, heldUntil, incoming, now, holdMs, decayPerSec, dtMs) helper is the pure-function hold envelope used
internally. It is exported for re-use and is unit-tested independently.
Implementation Notes
State is held in a useRef ({ held, heldUntil, last }) so smoothing
does not require re-renders on every RAF tick — only the display value
updates via setState. Hold deadlines are stored as absolute timestamps
(Date.now() + holdMs) which makes the math identical between live
runs and the synchronous unit tests of stepPeakHold.
The hold marker is rendered as a 2-pixel-wide absolutely-positioned div that tracks the held value's percentage position; the fill bar tracks the instantaneous value. Both are themable via CSS variables.