Describe the Compressor tab's layout and auto-gain as a switch (#378)

- Sounds: knobs at the bottom, levels filling the height, the curve
  centred at their right, and the least size of its window.
- Sounds: auto-gain of the Compressor, Limiter and Saturation is a
  switch with the makeup or output on top. The Compressor's gives back
  what it takes at -18 dB. New offline measurement.
- Concepts: a window is at least 320 x 200, more where its tab needs.
- Development: 1949 editor checks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
adminandClaude Opus 5.5 committed 2026-10-10 00:57:28 +02:00
1 parent 5b9343f173
commit a51884fafb
3 files changed
+42 -25

No files matched your search

+2 -1
@@ -359,7 +359,8 @@ one kind can be open at once — two textures, three behaviors — each with its
- **A tab can float** (#371): right-click it and pick *Float*, and the document leaves the row for
a window of its own above the editor. Its title bar moves the window, the lower right corner
sizes it, *Dock* puts the document back into the row, showing, and the `×` closes it. Only
documents float, never Scene or Game.
documents float, never Scene or Game. A window is at least 320 × 200 px. A tab that needs more,
like a Compressor's with its knobs (#378), holds its window at that size.
- Every kind has one editor, so it has **one window**: with a texture in a window, opening
another texture shows it there and closes the one it had. Windows of different kinds can be
open side by side, a sound FX beside its Parametric EQ for instance.
+1 -1
@@ -19,7 +19,7 @@ php tests/php/accounts.test.php # the API's accoun
php tests/php/snapshots.test.php # the snapshot store (#231), the same way
php tests/php/releases.test.php # the release store (#266), the same way
php tests/php/files.test.php # uploaded files in a folder per account (#344), the same way
node tools/seed-e2e.mjs && node tools/e2e.mjs # 1947 editor checks, screenshots in /tmp/gm-e2e-*.png
node tools/seed-e2e.mjs && node tools/e2e.mjs # 1949 editor checks, screenshots in /tmp/gm-e2e-*.png
GM_STEPS=20 node tools/e2e.mjs # or 1-12, or 6,20 — a subset while working on one step
./e2e.sh start [GM_STEPS] # the same run detached, see below
./e2e.sh status # live progress, or the last run's result
+39 -23
@@ -435,9 +435,9 @@ response of a Reverb, and as what the Sound FX tab tries out. Its row means noth
| **Phaser** | Sweeps notches through the sound, the swirl of a phaser pedal. **Stages** (2, 4, 6, 8 or 12, 4 by default) is how many allpass stages it has, every two make one notch. They sit around `frequency` (800 Hz by default), and an LFO moves them `depth` octaves wide (2), `rate` times a second (0.5 Hz). `feedback` (0.4) sharpens the notches, `wet` (1) is how much is heard. See [below](#phaser-and-flanger). |
| **Flanger** | Blends the sound with a copy a few ms late: the jet sound of a flanger, and what sfxr calls its phaser. `offset` is how late the copy is (1 ms by default), `sweep` moves that on by so many ms a second from the start of the sound (0), an LFO adds up to `depth` ms (2) `rate` times a second (0.25 Hz). `feedback` (0.5) and `wet` (1) as on the Phaser. See [below](#phaser-and-flanger). |
| **Pan** | Moves the sound left or right: `pan` -1 is all left, 1 all right, 0 (the default) leaves it in the middle. A mono sound is spread on both sides and then moved (each side 0.707 at 0). A stereo sound keeps the side it goes to and the other side moves over into it, as Web Audio's `StereoPannerNode` does: at 1 both sides are heard on the right. In an [instrument](Instruments#envelopes) an Envelope can move it. Measured (e2e 34, Chrome offline): a mono sine at 0.5 came out 0.5 / 0 at -1 and 0.354 / 0.354 at 0, a stereo one at 0.5 came out 0.354 / 0.854. |
| **Compressor** | Squeezes the loudness together: what goes above `threshold` (-24 dB by default) comes out only 1/`ratio` (4) as much louder. `knee` (6 dB) eases into it, `attack` (0.003 s) and `release` (0.25 s) say how fast it turns down and lets go. `makeup` (0 dB) raises it afterwards, `auto-gain` (0) blends that with the gain that brings a sound at full scale back to full scale. A double-click on the node opens its tab: a knob for each number, its curve, and the level going in and out. See [below](#compressor-and-limiter). |
| **Limiter** | Keeps every peak below `threshold` (-6 dB): it looks 5 ms ahead, so even a sudden peak never gets through, and comes back up in `release` (0.1 s). `auto-gain` (1) raises the limited sound towards `ceiling` (-1 dB). Nothing ever goes above the ceiling. See [below](#compressor-and-limiter). |
| **Saturation** | Bends the sound, from warmth to a hard distortion. **Type**: *Soft (tanh)* (the default), *Cubic*, *Hard clip*, *Tube*, *Fuzz*, *Foldback*, *Sine fold* or *Rectify*. `drive` (12 dB) is how hard the sound is pushed into it, `tone` (1) a low-pass after it from 1 kHz (0) to wide open (1). `output` (0 dB) and `auto-gain` (1) set the level of the bent sound, `wet` (1) how much of it is heard instead of the sound itself. See [below](#saturation). |
| **Compressor** | Squeezes the loudness together: what goes above `threshold` (-24 dB by default) comes out only 1/`ratio` (4) as much louder. `knee` (6 dB) eases into it, `attack` (0.003 s) and `release` (0.25 s) say how fast it turns down and lets go. `makeup` (0 dB) raises it afterwards. **Auto-gain** (off) gives back what it takes from a sound at -18 dB, with the makeup on top. A double-click on the node opens its tab: a knob for each number, its curve, and the level going in and out. See [below](#compressor-and-limiter). |
| **Limiter** | Keeps every peak below `threshold` (-6 dB): it looks 5 ms ahead, so even a sudden peak never gets through, and comes back up in `release` (0.1 s). **Auto-gain** (on) raises the limited sound to `ceiling` (-1 dB). Nothing ever goes above the ceiling. See [below](#compressor-and-limiter). |
| **Saturation** | Bends the sound, from warmth to a hard distortion. **Type**: *Soft (tanh)* (the default), *Cubic*, *Hard clip*, *Tube*, *Fuzz*, *Foldback*, *Sine fold* or *Rectify*. `drive` (12 dB) is how hard the sound is pushed into it, `tone` (1) a low-pass after it from 1 kHz (0) to wide open (1). **Auto-gain** (on) keeps the bent sound about as loud as it came in, `output` (0 dB) is added on top. `wet` (1) how much of it is heard instead of the sound itself. See [below](#saturation). |
| **Tremolo** | Makes the volume swing `rate` times a second (5 Hz by default), from full down by `depth` percent (50) and back. **Shape**: *Sine* (the default), *Triangle*, *Square* or *Saw*. See [below](#tremolo-and-vibrato). |
| **Vibrato** | Makes the pitch swing `rate` times a second (5 Hz), `depth` semitones up and as far down (0.5). **Shape** as on the Tremolo; a *Square* jumps between two pitches. See [below](#tremolo-and-vibrato). |
| **Reverb** | The sound in a space. **Type**: *Room* (the default), *Hall*, *Cave*, *Plate*, *Custom* or *Sound*. Custom shows the rows `decay` (s, 1.5 by default), `damping` (0.4) and `pre-delay` (s, 0.01); Sound picks a sound asset as the impulse response of a real space, with `pre-delay`. `wet` (0.3) is how much of the reverb is heard instead of the sound itself. See [below](#reverb). |
@@ -451,12 +451,15 @@ Bitcrusher `bits` 1 to 16, `rate` 100 to 48000 Hz, Phaser `frequency` 20 to
125 ms/s, `rate` 0 to 10 Hz, `depth` 0 to 10 ms, both with `feedback` 0 to 0.95,
Pan -1 to 1, Compressor `threshold` -100 to 0 dB, `ratio` 1 to 20, `knee` 0 to 40 dB, `attack` and
`release` 0 to 1 s, `makeup` -24 to +24 dB, Limiter `threshold` -60 to 0 dB, `ceiling` -30 to 0 dB,
`release` 0.001 to 2 s, both with `auto-gain` 0 to 1, Saturation `drive` 0 to 48 dB, `tone` and
`auto-gain` 0 to 1, `output` -24 to +24 dB, Tremolo `rate` 0 to 20 Hz and `depth` 0 to 100 %,
`release` 0.001 to 2 s, Saturation `drive` 0 to 48 dB, `tone` 0 to 1, `output` -24 to +24 dB, Tremolo `rate` 0 to 20 Hz and `depth` 0 to 100 %,
Vibrato `rate` 0.5 to 20 Hz and `depth` 0 to 4 semitones, Reverb `decay` 0.1 to 10 s, `damping`
0 to 1, `pre-delay` 0 to 0.5 s, and every `wet` 0 to 1),
whichever way the value arrives.
**Auto-gain** of the Compressor, the Limiter and the Saturation is a switch on the node, not an
input (#378). Until then it was a number from 0 to 1 that blended between the node's own gain and
the auto-gain. A project from before reads such a number as on when it is above 0.
### Bypass and Wet
Every effect has a **Bypass** and a **`wet`** (#354), all but the Mix, the Input and the Output.
@@ -708,16 +711,18 @@ Both (#326) work on the whole sound passing them: on a voice that is one sound,
- **Compressor** is Web Audio's `DynamicsCompressorNode`. Its knee is a curve the browser draws;
the game takes Chromium's, so the numbers below are worked out the way Chrome compresses.
- The node **adds a gain of its own**: 0.6 of what it takes off a sound at full scale (the spec
says so). The game takes that back out, so with `makeup` 0 and `auto-gain` 0 a compressed sound
really is quieter. After the node the sound gets `(1 - auto-gain) * makeup + auto-gain * A` dB,
where `A` is what the curve takes off at 0 dBFS. At `auto-gain` 1 a sound at full scale comes
back near full scale.
says so). The game takes that back out, so with `makeup` 0 and auto-gain off a compressed sound
really is quieter. After the node the sound gets `makeup + A` dB with auto-gain on, `makeup`
with it off. `A` is what the curve takes off a sound at **-18 dB**, about where a mixed sound
sits on average, so that one comes out as loud as it went in. Below the threshold `A` is 0.
Until #378 `A` was taken at 0 dB. With a low threshold that lifted everything far too much
(+28.1 dB at -40 dB, 4:1, knee 6, against +14.6 dB now).
- **Limiter** runs on the FX worklet. It holds the sound back 5 ms, finds the gain every sample in
that window needs to stay below the lower of `threshold` and `ceiling`, and averages it over the
window, so the gain is down smoothly by the time a peak is heard. Then it comes back up in
`release`. Both sides of a stereo sound are turned down together, by the louder one. `auto-gain`
raises what comes out by its share of `ceiling - threshold` dB: at 1 the peaks sit on the
ceiling, at 0 on the threshold.
`release`. Both sides of a stereo sound are turned down together, by the louder one. Auto-gain
on raises what comes out by `ceiling - threshold` dB, so the peaks sit on the ceiling. Off, they
sit on the threshold.
- **Both are late**: the Compressor by the spec's look-ahead of 6 ms, the Limiter by 5 ms (240
samples at 48 kHz, 221 at 44.1 kHz). This is made up exactly, as for a
[Pitch on a bus](#a-pitch-on-a-bus): a voice starts as much earlier, the other way of a Mix and
@@ -727,28 +732,38 @@ Both (#326) work on the whole sound passing them: on a voice that is one sound,
their ratio.
- **The Compressor's tab** (#378): a double-click on the node opens it, one tab per node, named
and closed like a [Parametric EQ's](#parametric-eq). It has three parts:
- **Knobs**, one for each of the seven numbers. Drag up or down to turn one: 200 px is the
- **Knobs**, one for each of the six numbers. Drag up or down to turn one: 200 px is the
whole turn, with Shift a tenth of that. A notch of the wheel turns it by a fiftieth.
A double-click sets the default, and a click on the number below lets you type one. Attack,
release and ratio give most of the turn to the low end, where they are used. A knob whose
input is linked is greyed: the link decides its value.
- **Auto-gain** is a switch at the right of the knobs. Below it stands what it gives, for
example `+14.6 dB`, or `off`. A click is one undo step.
- **The curve**, level in across and level out up, both -60 to 0 dB, before the makeup and
auto-gain. Two points are dragged: the one where it starts to bend sets `threshold`, the one
at its end sets the `ratio` that ends there. A dot on the curve is the level coming in now.
With auto-gain on, a line at -18 dB in leads from the curve up to the diagonal: that is how
far auto-gain lifts the sound.
- **The levels** of the last 4 s, newest at the right, +6 to -60 dB: grey what comes in, in
the colour of a sound port what goes out. Where the compressor takes more than 0.5 dB away,
the stretch below the level coming in is tinted by as much as it takes. The makeup is left
out of that, so a raised sound still shows what was squeezed. A level is the loudest sample
the stretch below the level coming in is tinted by as much as it takes. The makeup and
auto-gain are left out of that, so a raised sound still shows what was squeezed. Every
reading keeps the gain of its time, so changing the makeup does not repaint the past. A level is the loudest sample
of the last 25 ms. The line across is the threshold.
Each drag, notch or typed number is one undo step. The levels come from the same taps as the
EQ's spectrum: a gain of 1 in front of the node and behind it, fed into an analyser only while
the tab is seen. An EQ's tab and a Compressor's can be listened to at once.
The knobs stay on the bottom line, centred. The levels fill the height above them. The curve
is a square at their right, centred up and down. It grows with the tab from 180 to 360 px, but
takes no more than 40 % of its width. A floated Compressor's window cannot be made smaller than
what shows every knob and the smallest pictures (560 × 414 px in the test browser).
- **Measured** (2026-10-07, e2e 34, Chrome offline): an impulse came out of the Compressor 288
samples late and out of the Limiter 240. A 1 kHz sine at full scale through a hard-knee
Compressor at -12 dB and 4:1 came out at +0.19 dB with full auto-gain and at -8.81 dB with none
(the curve says -9). A sine at 2 through a Limiter at -6 dB peaked at exactly -6.00 dB, with
auto-gain 1 at -1.00 dB. Chrome's own gain far below the threshold was measured at four
samples late and out of the Limiter 240. A 1 kHz sine at -18 dB through a hard-knee
Compressor at -24 dB and 4:1 came out at -17.92 dB with auto-gain on and at -22.42 dB with it
off (the curve says -22.5; measured 2026-10-09, #378). A sine at 2 through a Limiter at -6 dB
peaked at exactly -6.00 dB, with auto-gain on at -1.00 dB. Chrome's own gain far below the threshold was measured at four
settings: 1.524 at -24 dB, knee 30, 12:1; 1.862 at -12 dB, knee 0, 4:1; 1.675 at -20 dB,
knee 10, 2:1; 1.000 at 0 dB. The curve gives all four to three places
(`tests/core/dynamics.test.ts`).
@@ -766,8 +781,9 @@ aliasing.
types make.
- **`drive` is a gain in front of the curve**, not a curve of its own: the curve spans 256 times
full scale and the sound is turned down as far before it. So a drive glides like any gain.
- **`output` and `auto-gain`**: the bent sound gets `(1 - auto-gain) * output + auto-gain * A` dB.
`A` is the gain that brings a sine at -12 dBFS, driven and bent, back to the RMS it came in with.
- **`output` and auto-gain**: the bent sound gets `output + A` dB with auto-gain on, `output`
with it off. `A` is the gain that brings a sine at -12 dBFS, driven and bent, back to the RMS it
came in with.
- **`tone`** is a low-pass after the curve, from 1 kHz at 0 to 20 kHz at 1, evenly in octaves.
- **`wet`** blends the bent sound with the sound itself. The oversampling makes the bent sound
**192 samples late** (Chrome, at 48 and at 44.1 kHz alike), so the sound itself is held back as
@@ -776,8 +792,8 @@ aliasing.
leaves the node out.
- **Measured** (2026-10-07, e2e 34, Chrome offline): an impulse came out 192 samples late at 48
and at 44.1 kHz. Half bent and half dry at 1125 Hz, where 192 samples off would cancel, the sine
came out at 0.00 dB. A sine at -12 dBFS driven 24 dB into *Soft* came out at 0.00 dB with full
auto-gain.
came out at 0.00 dB. A sine at -12 dBFS driven 24 dB into *Soft* came out at 0.00 dB with
auto-gain on.
### Tremolo and Vibrato