Describe the new sound FX nodes (#318 #319 #326 #323 #320 #328)

The sound FX got a reworked Filter and six new nodes in one series.
The wiki now explains each of them where the other nodes are.

- Sounds: a row in the node table for each new node, the Filter
  with all types, Q, Gain and Slope, and sections for the Filter,
  Compressor and Limiter, Saturation, Tremolo and Vibrato and the
  Reverb, each with the values measured in e2e 34.
- Instruments: the node table lists the new nodes, and the numbers an
  Envelope can move name the Filter's frequency, Q and gain and the
  pan of a Pan instead of cutoff and resonance.
- Development: 1878 editor checks instead of 1872.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
adminandClaude Opus 5.5 committed 2026-10-07 21:30:16 +02:00
1 parent b065d62d20
commit 892ae19f34
3 files changed
+168 -8

No files matched your search

+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 # 1872 editor checks, screenshots in /tmp/gm-e2e-*.png
node tools/seed-e2e.mjs && node tools/e2e.mjs # 1878 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
+4 -4
@@ -26,7 +26,7 @@ A at 440 Hz, 127 the highest. A note is named with its octave after it and no da
| **Sound FX** | Sends the sound through a sound FX asset, as if it were the sound at the FX's Input. Every parameter of that FX is an input here; one left open is the FX's default. The node is titled after the FX, and a double click opens it. |
| **Envelope** | Shapes a note as it is played: **Attack**, **Decay** and **Release** in seconds, **Sustain** from 0 to 1. On the way of the sound it is its volume; its output `value` moves a number with the same curve, from **From** to **To**. See [Envelopes](#envelopes). |
| **Output** | What is heard. It takes **one** sound, like every other sound input; a second edge replaces the first. To play several together, put a **Mix** in front of it (several Mixes in a row for more than two). One per instrument. |
| Gain, Pitch, 5-Band-EQ, Filter, Mix, Delay, Bitcrusher, Phaser, Flanger | The effects of a [sound FX](Sounds#sound-fx), the same nodes. |
| Gain, Pitch, 5-Band-EQ, Filter, Mix, Delay, Bitcrusher, Phaser, Flanger, Pan, Compressor, Limiter, Saturation, Tremolo, Vibrato, Reverb | The effects of a [sound FX](Sounds#sound-fx), the same nodes. |
**Play** of a Sample:
@@ -122,14 +122,14 @@ sound as it is.
- **`value`** moves a number with the same curve: at **From** while the note rests, at **To** at
the top of the attack, and where the sustain puts it between them while held. Its pin is of a kind of its
own, an **envelope**, drawn in coral. It fits only a number the sound itself holds while it
plays: `cutoff` and `resonance` of a Filter, `semitones` of a Pitch, `gain` of a Gain, a band of
plays: `frequency`, `Q` and `gain` of a Filter, `pan` of a Pan, `semitones` of a Pitch, `gain` of a Gain, a band of
the 5-Band-EQ, `mix` of a Mix, `bits`, `rate` and `mix` of a Bitcrusher, `frequency` and `mix`
of a Phaser, `offset` and `mix` of a Flanger, and `frequency` and `duty` of a Chip. Not the time
of a Delay, not the times of an Envelope, not a parameter of a Sound FX node, not the slide or the
other numbers of a Chip, not the LFO of a Phaser or Flanger. A cutoff, a Gain, the rate of a
Bitcrusher and the frequency of a Phaser or a Chip move in equal ratios (octaves, decibels), everything else evenly. Both ends are held to the range of the number it moves.
- **A filter sweep:** Sample → Filter → Envelope → Output, and a second Envelope with its `in` left
open, its `value` into the Filter's `cutoff`, From 200, To 5000. Each Envelope is a curve of its
open, its `value` into the Filter's `frequency`, From 200, To 5000. Each Envelope is a curve of its
own; one Envelope can feed several numbers.
- **A number an Envelope moves is the Envelope's.** It is no longer reached by a parameter or by
`Set sound FX parameters`. A Pitch it moves comes on top of the shift the note already has.
@@ -239,7 +239,7 @@ plays the note last played at once (C4 before any), for up to 2 s.
Flangers in the graph anew, as sfxr's randomizer does: most of them wild. A Flanger's offset and
sweep come from sfxr's phaser, its LFO and feedback and all of a Phaser, which sfxr has not,
within bounds of their own (a rate of 0.05 to 5 Hz, a Phaser at 200 Hz to 4 kHz). It leaves the
wave, *Follow note*, the type of a Filter, the *Stages* of a Phaser, every *Mix*, an Envelope's
wave, *Follow note*, the type and Slope of a Filter, the *Stages* of a Phaser, every *Mix*, an Envelope's
*From* and *To* and every other node as they are. An Envelope gets *Sustain* 0. An input with an edge into it keeps what it is fed.
- **Mutate** moves the numbers of those nodes a little, each one in two: a frequency, cutoff or
rate by up to a quarter of an octave, the rest by a step and a tenth of its value. A 0 stays 0,
+163 -3
@@ -248,20 +248,33 @@ Input and the Output once each; their pins carry sound and fit nothing but each
| **Gain** | Louder or quieter, in dB (`Gain (dB)`): 0 leaves it alone, -6 is about half as loud, +6 twice. Above 0 the sound can clip. |
| **Pitch** | Higher or lower, in semitones (12 is an octave, 0.5 a quarter tone). The sound **keeps its length**, also when the pitch changes while it plays. The pitch is made where the sound is read, so an effect before the Pitch acts as if it came after it. On a [Chip](Instruments#chips) of an instrument it moves the frequency of the Chip itself. |
| **5-Band-EQ** | Five bands in dB: 80 Hz and 12 kHz as shelves, 250 Hz, 1 kHz and 4 kHz as peaks. A band at 0 does nothing. |
| **Filter** | Takes away what is above `cutoff` (Hz, 1000 by default): **Type** *Low-pass*. Or what is below it (*High-pass*), or both, leaving a band around it (*Band-pass*). `resonance` from 0 to 1 raises what is right at the cutoff: a low- or high-pass has no bump at 0 (Q -3 dB) and rises by 20 dB at 1, a band-pass gets narrower (Q 0.7 to 20). In an [instrument](Instruments#envelopes) an Envelope can move both while a note plays. |
| **Filter** | **Type** *Low-pass* (the default) takes away what is above `frequency` (Hz, 1000 by default), *High-pass* what is below it, *Band-pass* all but a band around it and *Notch* just that band. *Low shelf* and *High shelf* raise or lower everything below or above it by `gain` (dB), *Peaking* a band around it; only these three have the `gain` row. *All-pass* leaves every frequency as loud as it was and only turns its phase. `Q` (0.707 by default) says how sharp it is: at 0.707 a low- or high-pass has no bump, higher it rings at the frequency, and a band, a notch or a peak gets narrower. **Slope** makes a low-pass, high-pass, band-pass or notch steeper: *12 dB/oct* is one filter, *24* two and *48* four in a row, and only the last of them has the Q. In an [instrument](Instruments#envelopes) an Envelope can move all three numbers while a note plays. See [below](#filter). |
| **Mix** | Blends the sounds at `a` and `b`: at 0 only `a` is heard, at 1 only `b` (0.5 is the default). **Curve** says what lies between: *Crossfade* (the default) gives half of each at 0.5, *Both at center* keeps each full up to the middle. See [below](#the-curve-of-a-mix). |
| **Delay** | An echo: the sound comes back after `time`, again and again, each time `feedback` times as loud. **Sync** says what `time` counts: *Seconds*, or *Beats* in quarter notes at the tempo of the voice. `damping` makes every echo duller than the last, `mix` is how loud the echoes are next to the sound, which stays as it is. **Ping-pong** sends the echoes left and right in turn. See [below](#delay-and-the-tail-of-a-voice). |
| **Bitcrusher** | Makes the sound rough, like an old console. `bits` rounds it to fewer steps (8 by default), `rate` takes a sample only so many times a second and holds it until the next (8000 Hz by default), `mix` is how much of the crushed sound is heard instead of the sound itself (1 by default). See [below](#bitcrusher). |
| **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, `mix` (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 `mix` (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. 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, `mix` (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`. `mix` (0.3) is how much of the reverb is heard instead of the sound itself. See [below](#reverb). |
Every number of these nodes is an **input**: typed in while nothing is connected, or fed from a
parameter of the Input. Each is held to its range (Gain -120 to +24 dB, Pitch -64 to +64, the EQ
bands -60 to +24 dB, Filter `cutoff` 20 to 20000 Hz and `resonance` 0 to 1, Mix 0 to 1, Delay
bands -60 to +24 dB, Filter `frequency` 20 to 20000 Hz, `Q` 0.1 to 30 and `gain` -40 to +40 dB, Mix 0 to 1, Delay
`time` 0.01 to 8 and never more than 2 s, `feedback` 0 to 0.95, `damping` and `mix` 0 to 1,
Bitcrusher `bits` 1 to 16, `rate` 100 to 48000 Hz and `mix` 0 to 1, Phaser `frequency` 20 to
20000 Hz, `rate` 0 to 10 Hz, `depth` 0 to 6 octaves, Flanger `offset` 0 to 10 ms, `sweep` -125 to
125 ms/s, `rate` 0 to 10 Hz, `depth` 0 to 10 ms, both with `feedback` 0 to 0.95 and `mix` 0 to 1),
125 ms/s, `rate` 0 to 10 Hz, `depth` 0 to 10 ms, both with `feedback` 0 to 0.95 and `mix` 0 to 1,
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`,
`auto-gain` and `mix` 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` and
`mix` 0 to 1, `pre-delay` 0 to 0.5 s),
whichever way the value arrives.
**How Pitch keeps the length** (#317). A way with a Pitch is played by an `AudioWorklet` on the
@@ -376,6 +389,153 @@ change ends. The end is timed on the clock of the sound, so a paused run holds a
about 0.3 s, so about **400 of them play at once** (2026-10-06). There is no limit on how many
voices play, also now that they live longer.
### Filter
A **Filter** (#335, #318) is Web Audio's `BiquadFilterNode`, every type it has.
- **`Q` is a factor** for every type, 0.1 to 30. Web Audio takes the Q of a low- or high-pass in
dB, the game hands it on as `20 * log10(Q)`, so 0.707 is a flat -3 dB there. A shelf has no Q in
Web Audio, the row does nothing for it.
- **Slope** puts 2 or 4 filters of the same type in a row. They share one `frequency`, carried by
one source into all of them, so a sweep moves them together. Only the last has `Q`, the others
sit at 0.707, so a resonance is as high at 48 dB/oct as at 12. Shelves, peaks and the all-pass
have no Slope row: in a row they would only add up their gain or their phase.
- `frequency` moves in octaves, `Q` in its ratio (in dB for a low- or high-pass), `gain` in dB.
All three move with *Set sound FX parameters*, and an Envelope can move them in an
[instrument](Instruments#envelopes).
- A Filter before #318 had *Resonance* from 0 to 1 instead of `Q`. No project used it. The Chip
Presets keep their sound: their resonance is now the Q it gave before.
- **Measured** (2026-10-07, e2e 34, Chrome offline): a 2 kHz sine through a low-pass at 1 kHz with
Q 0.707 came out 12.37 dB quieter at 12 dB/oct and 49.5 dB quieter at 48 dB/oct. A peak of
+6 dB at 1 kHz raised a 1 kHz sine by 6.00 dB, a notch there took it down by 124 dB.
### Compressor and Limiter
Both (#326) work on the whole sound passing them: on a voice that is one sound, on a
[bus](#buses-and-the-master) everything playing through it.
- **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.
- **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.
- **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
the other layers of a note are held back as far, and a bus counts it into how late it is. A voice
with a Limiter is let go only once what the Limiter still holds is out.
- Every number moves with *Set sound FX parameters*: dB values evenly, times and the ratio in
their ratio.
- **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
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`).
### Saturation
A **Saturation** (#323) is a `WaveShaperNode` with a fixed curve per type, oversampled 4x against
aliasing.
- **The types**: *Soft* is `tanh`, *Cubic* `1.5 * (x - x³/3)` up to full scale, both round the peaks
off. *Hard clip* cuts them at ±1. *Tube* is `tanh` shifted, so one half bends harder than the
other (even harmonics). *Fuzz* is harder still and lopsided. *Foldback* mirrors what goes past
full scale back in, again and again, *Sine fold* does that smoothly. *Rectify* turns the lower half
up, which sounds an octave higher. A 10 Hz high-pass after the curve takes out the DC the lopsided
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.
- **`tone`** is a low-pass after the curve, from 1 kHz at 0 to 20 kHz at 1, evenly in octaves.
- **`mix`** 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
far and the two meet without a comb. The node as a whole is late by that much, which a voice, a
Mix and a bus make up for as for a [Compressor](#compressor-and-limiter). A `mix` typed in at 0
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.
### Tremolo and Vibrato
Both (#320) run on the FX worklet with an LFO that is **at phase 0 on the first sample of its
voice**, so every sound starts its swing the same way. On a bus the LFO starts when the bus gets its
FX. A `depth` typed in at 0 leaves the node out.
- **The shapes**, over one period: *Sine* rises from 0, *Triangle* too, *Square* is up for the first
half and down for the second, *Saw* rises from 0 to the top, drops to the bottom and rises again.
- **Tremolo**: the volume is `1 - depth * (1 - LFO) / 2`, so full at the top of the LFO and
`1 - depth` at its bottom. A sine starts halfway down.
- **Vibrato** moves the pitch through a short delay whose time swings: a delay getting shorter is
read faster, so higher. That works on a bus too, where there is no source to speed up, and the
sound keeps its length. The delay is the LFO summed up, so its slope is the LFO and the pitch
follows the shape: a *Square* is a trill between two pitches. At the top the sound is read
`2^(depth/12)` times as fast, at the bottom as much slower as that is faster.
- The delay swings around a **center**, as far as it swings out at the `rate` and `depth` the voice
starts with: about 1 ms at 5 Hz and half a semitone, 83 ms for a sine and 130 ms for a square at
0.5 Hz and 4 semitones. That center is how late the Vibrato is, and a voice, a Mix and a bus make
up for it as for a [Compressor](#compressor-and-limiter). A sound started *now* in a game is heard
that much later.
- `rate` (in its ratio) and `depth` move with *Set sound FX parameters*. **Turning the depth up or
the rate down after the start** asks for a delay below 0 at the low end of the swing: there it
stays at 0, and the lower half of the swing flattens.
- **Measured** (2026-10-07, e2e 34, Chrome offline): a Tremolo square at 10 Hz, depth 100 %, on a
steady 0.5 for a voice starting at 0.1 s was silent before it, then 0.5 for 50 ms and 0 for 50
ms. A Vibrato square at 5 Hz and one semitone on a 1 kHz sine read 1059.5 Hz in the first half of
a period and 940.5 Hz in the second.
### Reverb
A **Reverb** (#328) is a `ConvolverNode`: the sound folded with an impulse response, the way a
space answers a click. It works on a voice and on a bus; a shared one on a bus is far cheaper than
one on every voice (see [Buses and the master](#buses-and-the-master), and #351 for sending only a
share of a sound there).
- **Type** picks the space:
| Type | `decay` | `damping` | `pre-delay` |
| --- | --- | --- | --- |
| Room | 0.6 s | 0.5 | 5 ms |
| Hall | 2.5 s | 0.35 | 20 ms |
| Cave | 5 s | 0.15 | 40 ms |
| Plate | 1.6 s | 0.1 | 0 |
*Custom* shows the three rows to set them yourself, *Sound* the pick of a sound asset and
`pre-delay`. The rows are only there for those types (they are ports of their own, so an edge
into one that goes away is a ghost port).
- **The made impulse response** is noise, its own on either side, that has fallen by 60 dB after
`decay` seconds (up to 10). A low-pass closes over that time, from 20 kHz towards 500 Hz as far as
`damping` says, so the highs of the tail die first. It is made once for its numbers and kept.
- **A sound as the impulse response** is loaded with the voice, or before a bus gets its FX. Web
Audio normalizes it, so a loud recording is not louder than a quiet one. A sound that cannot be
loaded leaves the reverb silent and the sound dry.
- **`mix`**: `(1 - mix)` of the sound itself, `mix` of the reverb. A bus that only carries reverb
wants 1. A `mix` typed in at 0 leaves the node out.
- **Only `mix` moves while the sound plays.** `decay`, `damping` and `pre-delay` are set when a
voice starts, on a bus when it gets its FX: a new impulse response would click.
- **The voice lasts until the reverb has faded**: its pre-delay and its decay, or as long as its
sound.
- **Cost**: every voice with a Reverb folds its sound with the whole impulse response. Many short
sounds at once, each with a long decay, are better sent through one bus.
- **Measured** (2026-10-07, e2e 34, Chrome offline): an impulse through a *Custom* reverb of 1 s with
50 ms pre-delay was silent up to 50 ms after it, and fell by 35.9 dB from 0.2-0.3 s to 0.8-0.9 s
after it (36 dB to be expected). At `mix` 0.5 the impulse itself came through at half. With a sound
as the impulse response that has one sample at 0.2 s, the impulse came back exactly 0.25 s later and
nowhere else.
### Bitcrusher
A **Bitcrusher** (#322) runs on the same worklet as Pitch, sample by sample: