Level Meter
A peak and RMS level meter with zones, peak hold, a scale, a readout and a clip light.
Installation
$ pnpm dlx shadcn-svelte@latest add https://juddisjudd.github.io/audiocn-svelte/r/level-meter.json$ npx shadcn-svelte@latest add https://juddisjudd.github.io/audiocn-svelte/r/level-meter.json$ yarn dlx shadcn-svelte@latest add https://juddisjudd.github.io/audiocn-svelte/r/level-meter.json$ bunx --bun shadcn-svelte@latest add https://juddisjudd.github.io/audiocn-svelte/r/level-meter.jsonUsage
<script lang="ts">
import { LevelMeter } from "#lib/components/ui/level-meter/index.js";
</script>
<LevelMeter aria-label="Microphone level" peakDb={-12} />With no children, LevelMeter renders one track per channel with a peak bar and a peak-hold tick. Add children to control the anatomy.
Anatomy
<LevelMeter source={analyser.meter}>
<LevelMeterChannels>
<LevelMeterChannel index={0}>
<LevelMeterTrack>
<LevelMeterBar measure="rms" />
<LevelMeterBar measure="peak" />
<LevelMeterHold />
</LevelMeterTrack>
</LevelMeterChannel>
<LevelMeterScale />
</LevelMeterChannels>
<LevelMeterValue />
<LevelMeterClip />
</LevelMeter>Examples
Default
Values
Pass peakDb (and optionally rmsDb) for data that arrives slowly. The meter still moves smoothly between updates.
Vertical
Variants
Peak and RMS
Layer an rms bar over a translucent peak bar for a dual meter.
Ballistics
peak rises in 15 ms and falls steadily; vu is slow and averaged; instant follows the input exactly.
Colors and zones
Override the meter tokens with a class, or pass your own zones.
Build your own visual
Each channel writes its live level to --meter-level (0..1). Anything inside a LevelMeterChannel can use it in CSS.
Microphone
Feeding data
A meter accepts levels three ways. See Feeding data.
<script lang="ts">
import { LevelMeter, type LevelMeterActions } from "#lib/components/ui/level-meter/index.js";
let meter = $state<LevelMeterActions>();
// From your own frame loop:
const onFrame = (peakDb: number) => meter?.paint({ channels: [{ peakDb }] });
</script>
<!-- A source: subscribes and paints itself, without going through Svelte state. -->
<LevelMeter source={analyser.meter} />
<!-- Values: for slow data. Ballistics smooth the movement. -->
<LevelMeter peakDb={-9.1} rmsDb={-18} />
<!-- A handle: for callers that own their own frame loop. -->
<LevelMeter bind:this={meter} />Theming
| Token or variable | Where | Default |
|---|---|---|
--meter-ok, --meter-warn, --meter-clip | theme | green, amber, --destructive |
--meter-thickness | root | 0.5rem (sm 0.25rem, lg 0.75rem) |
--meter-gap | root | 0.25rem, between channels |
--meter-level, --meter-rms, --meter-hold | each channel, live | 0..1 |
| Data attribute | On | Meaning |
|---|---|---|
data-orientation, data-variant, data-size | root | Current settings |
data-zone | root, channels | ok, warn or clip |
data-clipping | root | The signal clipped in the last 1.5 s |
data-active | channels | The level is above the bottom of the range |
data-dimmed | root | Inside a muted or silenced channel strip |
data-measure | bars | peak or rms |
Accessibility
- The root has
role="meter"witharia-valuemin,aria-valuemaxandaria-valuenowin dB, andaria-valuetextsuch as "−12.0 dB". These update at most four times a second. - Give every meter a name with
aria-labeloraria-labelledby. - With reduced motion, the meter shows the current level four times a second instead of animating.
API reference
LevelMeter
| Prop | Type | Default |
|---|---|---|
source Subscribes and paints without going through Svelte state. | FrameSource<MeterFrame> | null | — |
peakDb Mono peak level in dBFS, for declarative use. | number | — |
rmsDb Mono RMS level in dBFS. | number | — |
channels Levels per channel, for declarative multi-channel use. | ChannelLevel[] | — |
channelCount Tracks to render before data arrives. | number | 1 |
minDb Bottom of the displayed range. | number | -60 |
maxDb Top of the displayed range. | number | 0 |
zones Colour zones: ok, warn from −20, clip from −9. | MeterZone[] | DEFAULT_ZONES |
ballistics How the meter moves. | "peak" | "vu" | "instant" | Partial<BallisticsOptions> | "peak" |
taper Scale law. | "linear" | "audio" | Taper | "linear" |
orientation Inherited from a channel strip. | "horizontal" | "vertical" | "horizontal" |
variant segmented is an LED ladder. | "solid" | "segmented" | "gradient" | "solid" |
segments Segments for the segmented variant. | number | 24 |
size Track thickness. | "sm" | "default" | "lg" | "default" |
Exported functions
Bind the component with bind:this to call them. Their type is LevelMeterActions.
| Prop | Type | Default |
|---|---|---|
paint Paint a frame directly, for callers that own their own frame loop. | (frame: MeterFrame) => void | — |
reset Drop the level and the peak hold to silence. | () => void | — |
LevelMeterChannel
| Prop | Type | Default |
|---|---|---|
index Which channel of the frames this track shows. | number | 0 |
LevelMeterBar
| Prop | Type | Default |
|---|---|---|
measure Which measurement the bar shows. | "peak" | "rms" | "peak" |
LevelMeterScale, LevelMeterValue, LevelMeterClip
These wrap DbScale, DbReadout and ClipIndicator, and take their props. The range, orientation and data come from the meter. LevelMeterClip exports the same report and reset functions as ClipIndicator.