Gain Reduction Meter
gain-reduction-meterViewersThe three bars on a compressor's front panel. Input and output level run on the usual dB scale, and between them the gain-reduction bar fills from the top downward — the hardware convention, because it shows what is being taken away rather than what is there — with a peak-hold tick marking the deepest recent bite. Wire it with dB values you already have, a per-frame callback (DynamicsCompressorNode.reduction slots straight in), or a pair of analysers for the level bars; every column is optional, so a lone GR bar works too.
A synthesised programme through a modelled 4:1 compressor — the GR bar hangs from the top and holds its deepest recent bite.
Install
Copies the source into your project. Anything it depends on comes with it.
$ npx shadcn add @syntho-ui/gain-reduction-meterPulls in
First time? Add the registry to your components.json once:
{
"registries": {
"@syntho-ui": "https://engine.syntho.app/r/{name}.json"
}
}Usage
Controlled or uncontrolled, whichever suits the surrounding state.
import { GainReductionMeter } from "@/components/ui/gain-reduction-meter"
// One compressor, tapped before and after:
const compressor = new DynamicsCompressorNode(context, { threshold: -24, ratio: 4 })
const inputAnalyser = new AnalyserNode(context)
const outputAnalyser = new AnalyserNode(context)
source.connect(inputAnalyser)
source.connect(compressor)
compressor.connect(outputAnalyser)
compressor.connect(context.destination)
<GainReductionMeter
getLevels={() => ({ reduction: compressor.reduction })}
inputAnalyser={inputAnalyser}
outputAnalyser={outputAnalyser}
segments={16}
showScale
/>Props
| Prop | Type | Default | Description |
|---|---|---|---|
| levels | { input?: number; output?: number; reduction: number } | — | Levels you already computed, in dB (reduction ≤ 0). Highest precedence: any field given here overrides the same field from getLevels or the analysers. |
| getLevels | () => { input?: number; output?: number; reduction: number } | — | Polled once per animation frame. The DynamicsCompressorNode hook: () => ({ reduction: compressor.reduction }). |
| inputAnalyser / outputAnalyser | AnalyserNode | — | RMS dB for the IN / OUT bars is computed from these each frame. Only ever read. Analysers cannot know reduction — the GR bar still wants levels or getLevels. |
| paused | boolean | false | Stop polling and hold the last frame. |
| showInput / showReduction / showOutput | boolean | true | Which columns render. Turn IN and OUT off for a lone GR bar. |
| orientation | "vertical" | "horizontal" | "vertical" | Which way the bars run. GR fills from the top downward when vertical, from the right edge inward when horizontal. |
| length | number | 120 | Long axis in pixels. |
| thickness | number | 10 | Width of each bar in pixels. |
| segments | number | 0 | Draw as discrete LED segments; 0 is a continuous bar. Segment colours belong to their scale position, hardware-style. |
| showScale | boolean | false | dB tick labels: the level scale on one side, the GR scale on the other. |
| showLabels | boolean | true | Show the IN / GR / OUT captions. |
| labels | { input?: string; reduction?: string; output?: string } | — | Caption overrides, e.g. { reduction: "COMP" }. |
| minDecibels / maxDecibels | number | -60 / 0 | The dB range the level bars span. |
| minReduction | number | -24 | Deepest reduction the GR scale shows, in dB (negative). |
| zones | { amber?: number; red?: number } | { amber: -18, red: -6 } | Where the level bars turn amber and red, in dB. |
| peakDecay | number | 0.6 | GR peak-hold decay in full-scale units per second. 0 hides the tick. |
| colors | { low?; mid?; high?; reduction?; peak? } | — | Per-part colour overrides. Defaults read --uikit-meter-low/-mid/-high with traffic-light fallbacks; the GR bar uses the theme accent. |
Interaction
- Drag with pointer capture, so the gesture keeps tracking past the edge of the control.
- Shift while dragging for a fine pass; release it mid-gesture and coarse tracking resumes.
- Double-click resets to
defaultValue. - Arrow keys step normally, Shift+arrow steps finely, PageUp/PageDown jump by ten, Home/End go to the extremes.
- Proper
sliderandswitchroles with livearia-valuetext, so a screen reader announces the formatted value rather than the raw number. - The scroll wheel adjusts the value while the pointer is over the control; stepped ranges move at least one declared step per notch by default.