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