Stereo Distortion Module
Nine distortion algorithms: asymmetric hard clipping with wavefolding (Dist 1), gated version (Dist 2), multiband compression with bit crushing (Comp), half/full wave rectification (Rectify), resonant filter feedback meltdown (Squelch), iterated chaos mapping (Fractal), stochastic sample decimation (Sparser), ring modulation with a frequency-shifted oscillator (Noise Mod), and subharmonic generation via rectified low-pass filtering (Subharm). Stereo width parameter applies different drive/fold amounts to each channel.
Available as a send effect in the sequencer (Stereo Distortion card in the master collapsible section, Snd tab per-sound send fader).
Signal Flow
input (mono send sum)
→ ChannelSplitter(2)
├→ L: driveGain → mode-specific chain → levelGain
└→ R: driveGain(×width) → mode-specific chain(×width) → levelGain
→ ChannelMerger(2) → wetGain → output
dry path ──────────────→ dryGain → output
The mode-specific chain is rewired per algorithm:
| Mode | Chain |
|---|---|
| Dist 1 | driveGain → hardClip → foldGain → fold → gate(flat) → crush → levelGain |
| Dist 2 | driveGain → hardClip → foldGain → fold → gate(active) → crush → levelGain |
| Comp | same as Dist 1, with crush always active and extra makeup gain |
| Rectify | driveGain → rectifyWS → levelGain |
| Squelch | driveGain → bandpassFilter(with feedback) → hardClip → levelGain |
| Fractal | driveGain → fractalWS(iterated sin+tanh) → levelGain |
| Sparser | driveGain → sparseWS(stochastic hold) → levelGain |
| Noise Mod | driveGain → hardClip(modulated by sine osc at Shift Hz) → levelGain |
| Subharm | driveGain → hardClip(dry) + subRectWS → subLPF → subGain → merger |
Parameters
| Param | Range | Default | Modes | Description |
|---|---|---|---|---|
ruinaMode |
9 pills | dist1 | All | Distortion algorithm |
ruinaDrive |
0–1 | 0.5 | All except Rectify | Input gain before processing |
ruinaFold |
0–1 | 0 | dist1, dist2, comp | Wavefolding depth |
ruinaCrush |
0–1 | 0 | comp | Bit reduction (4–16 bits) |
ruinaMix |
0–1 | 0.5 | All | Wet/dry blend |
ruinaWidth |
0–1 | 0.5 | All | Stereo separation (L/R channel difference) |
rectifyBlend |
0–1 | 0.5 | rectify | Half-wave(0) to full-wave(1) rectification |
squelchReso |
0–1 | 0.5 | squelch | Bandpass filter resonance / feedback amount |
squelchSweep |
0–1 | 0 | squelch | Filter frequency sweep (200–3200 Hz) |
fractalIter |
1–8 | 3 | fractal | Iterations of sin+tanh chaotification |
sparseProb |
0–1 | 0.3 | sparse | Probability of holding previous sample |
noiseShift |
0–500 Hz | 100 | noisemod | Ring-mod oscillator frequency offset |
subDrive |
0–1 | 0.5 | subharm | Subharmonic rectifier drive |
subMix |
0–1 | 0.5 | subharm | Subharmonic wet level |
Modes
Dist 1 (Hard Clip + Fold)
Asymmetric hard clipping using tanh with different positive/negative scaling
(×0.7 vs ×1.3). The fold parameter adds sine-based wavefolding for aggressive,
metallic textures. drive controls input gain before the clip stage.
Dist 2 (Gated)
Same clip + fold as Dist 1, but with a noise gate that drops the signal to zero
when the input amplitude is below drive × 0.3. Creates a gated, chopped sound.
Comp (Multiband Crush)
OTT-style compression (split into 3 bands, each band compressed) combined with
bit crushing. fold acts as compression ratio makeup gain. crush sets bit
depth via quantised waveshaper curve.
Rectify
Blends between half-wave rectification (max(0, x) — clamps negative) and
full-wave rectification (|x| — folds negative to positive, producing octave-up
frequency doubling). Both are scaled and offset to maintain level.
Squelch
Drives the signal through a resonant bandpass filter with configurable feedback
gain and filter Q. Self-oscillates at extreme settings. The filter frequency
sweeps from 200 Hz to ~3.2 kHz via the sweep param (quadratic mapping for
natural feel). The reso param controls both Q (5–30) and feedback (0–85%).
Fractal
Applies an iterated function to each sample: v = sin(v × π × 1.5) followed by
tanh(v × 1.3). Each iteration adds progressively more high-frequency chaos.
At 1–2 iterations: warm saturation. At 4–5: aggressive aliasing. At 7–8: the
waveform disintegrates into near-noise.
Sparser
A stochastic sample-and-hold waveshaper. Each sample in the curve has a
sparseProb chance of being replaced with the previous sample's value. At
low sparsity: subtle glitching. At high sparsity (>60%): frozen, stuttering
artifacts. The curve is regenerated every time the parameter changes (random
seed changes per generation).
Noise Mod
Ring-modulates the signal with a sine oscillator at Shift Hz. The oscillator
is summed into the signal path before the hard clip stage, creating carrier+sideband
distortion. At low shift values (<50 Hz): wobble and tremolo effects. At high
shift values (200–500 Hz): metallic bell tones and dissonant clangour.
Subharm
Parallel subharmonic synthesis. The signal passes through two paths:
1. Dry path: standard hard clipping for the main sound
2. Sub path: full-wave rectification (|x| creates frequency doubling),
then low-pass filtered at 120 Hz to extract only sub frequencies, then
sent through a tanh drive stage. The sub path feeds back into the stereo
merger post-clipping, blending with the dry path via Sub Mix.
WaveShaper Curves
| Curve | Function | Used by |
|---|---|---|
buildHardClipCurve(drive) |
Asymmetric tanh | dist1, dist2, comp, squelch, noisemod, subharm |
buildFoldCurve(amount) |
sin(x × π × (1 + amount × 5)) scaled |
dist1, dist2, comp |
buildCrushCurve(bits) |
Quantised levels 2^(4 + bits × 12) |
comp |
buildRectifyCurve(blend) |
Half/full wave rectification blend | rectify |
buildFractalCurve(drive, iters) |
Iterated sin+tanh chaos | fractal |
buildSparseCurve(prob) |
Stochastic sample hold | sparse |
buildSubCurve(drive) |
Full-wave rectification + tanh | subharm |
buildGateCurve(threshold) |
Zero below threshold, pass above | dist2 |
Exports
| Export | Signature | Purpose |
|---|---|---|
paramDefs |
— | UI param definitions (15 params, ruinaMode as pills) |
build |
(ctx, engine) |
Create stereo processor chain with dynamic wiring, returns { input, output, nodes } |
update |
(nodes, engine) |
Update curves, gains, and rewire topology on mode change |
Integration
As sequencer send FX
Wired in ensureMasterBus alongside reverb. Per-sound send level is controlled
via sound.sends.ruina (set in Snd tab). The module output feeds into
_limSumL/_limSumR post-glue, pre-limiter.
Mode switching
When update() detects a mode change, _wireMode() disconnects all
mode-specific nodes and re-connects the appropriate topology for the new mode.
Shared nodes (splitter, merger, wet/dry gains) persist across mode switches.
Files
| File | Purpose |
|---|---|
modules/ruina.js |
Module implementation |
sequencer-audio.js |
Master bus wiring (ensureMasterBus) |
sequencer-ui.js |
Master section Stereo Distortion card + Snd tab slider |
sequencer.js |
_playNote stereo distortion send routing |
See Also
docs/synth/sequencer.md— master section, send FXdocs/synth/sequencer-audio.md— stereo master busdocs/synth/modules/reverb.md— convolution reverb module (sibling send FX)