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