Gain Reduction Meter

gain-reduction-meterViewers

The 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.

IN
GR
OUT
IN
GR
OUT
GR
IN
GR
OUT

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-meter

Pulls in

use-audio-sourcecontrol-theme

First time? Add the registry to your components.json once:

components.json
{
  "registries": {
    "@syntho-ui": "https://engine.syntho.app/r/{name}.json"
  }
}

Usage

Controlled or uncontrolled, whichever suits the surrounding state.

example.tsx
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

PropTypeDefaultDescription
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 / outputAnalyserAnalyserNodeRMS 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.
pausedbooleanfalseStop polling and hold the last frame.
showInput / showReduction / showOutputbooleantrueWhich 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.
lengthnumber120Long axis in pixels.
thicknessnumber10Width of each bar in pixels.
segmentsnumber0Draw as discrete LED segments; 0 is a continuous bar. Segment colours belong to their scale position, hardware-style.
showScalebooleanfalsedB tick labels: the level scale on one side, the GR scale on the other.
showLabelsbooleantrueShow the IN / GR / OUT captions.
labels{ input?: string; reduction?: string; output?: string }Caption overrides, e.g. { reduction: "COMP" }.
minDecibels / maxDecibelsnumber-60 / 0The dB range the level bars span.
minReductionnumber-24Deepest 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.
peakDecaynumber0.6GR 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 slider and switch roles with live aria-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.