DoughDSP: hear the primitives
The DoughDsp port gives Field a handful of low-level
DSP building blocks. A picture of a filter curve only gets you so far; the way to know what a
plate reverb, an LFO-swept phaser, a resonant lowpass, a click-free envelope, a soft clipper or
a pair of aliasing oscillators does is to run a real signal through it and listen.
Each section below builds one primitive, plays a signal through it, plots the result with
showSignals, and lets you hear it. The clips are
rendered offline by the same code the audio engine runs.
In each example the code that builds the primitive is shown, and the rendering and plotting are
folded behind a + … pill you can click to open. The dough* helpers it calls (doughRenderMono,
doughPluckEnv, doughAbsEnvelope and the rest) are available in every box and
listed in the reference.
PlateReverb — a pluck and its tail
A single 120 ms pluck at 330 Hz is fed into the Dattorro plate (decay = 0.85, mix = 0.5)
and the stereo output is folded to mono. The dry note is gone almost immediately; what you hear
after it is entirely the plate ringing down. The envelope plot shows the same decay: a sharp
onset, then a long smooth tail.
:::static
val pluck = SineOsc(Param(330f)) * doughPluckEnv(tau = 0.045f, holdSec = 0.12f)
val verb = PlateReverb(source = pluck, decay = Param(0.85f), mix = Param(0.5f))
:::fold render 3 s offline and plot the |amplitude| envelope
val mono = doughRenderStereoMono(verb, seconds = 3f)
showSignals(
"|amplitude| envelope" to doughAbsEnvelope(mono, buckets = 400),
channel = "main",
title = "PlateReverb — a pluck and its decaying tail",
xTitle = "time",
yTitle = "|amp|",
)
:::endfold

Phaser — notches sweeping across a sawtooth
A sustained 220 Hz sawtooth through the LFO-swept cascaded notches (rate = 0.5 Hz,
depth = 0.7). As the notches slide across the sawtooth's harmonics you get the classic hollow,
watery whoosh. The plot traces the short-time RMS: it breathes in time with the LFO.
:::static
val saw = ZawOsc(Param(220f)) * 0.4f
val out = saw.through(
Phaser(rate = Param(0.5f), depth = Param(0.7f), center = Param(700f), sweep = Param(1400f), mix = Param(1f)),
)
:::fold render 3 s offline and plot the short-time RMS (the LFO whoosh)
val audio = doughRenderMono(out, seconds = 3f)
showSignals(
"short-time RMS" to doughShortTimeRms(audio, windowMs = 25.0, points = 300),
channel = "main",
title = "Phaser — notches sweeping across a sawtooth",
xTitle = "time",
yTitle = "rms",
)
:::endfold

ChamberlinLowpass — a resonant cutoff sweep
A raw 110 Hz sawtooth through the superdough 2-pole lowpass with resonance = 0.6, while the
cutoff sweeps 150 Hz → 6 kHz. You hear the filter open up, the resonance whistling along the top
of the sweep. The plot tracks a cheap brightness proxy (high-frequency energy) climbing as the
cutoff rises.
:::static
val saw = ZawOsc(Param(110f)) * 0.5f
val cutoff = Param(150f).also { it.supplier = { 150f + 5850f * (time / 3f).coerceIn(0f, 1f) } }
val out = saw.through(ChamberlinLowpass(cutoff, Param(0.6f)))
:::fold render 3 s offline and plot the rising brightness
val audio = doughRenderMono(out, seconds = 3f)
showSignals(
"brightness (HF energy)" to doughShortTimeBrightness(audio, windowMs = 40.0, points = 200),
channel = "main",
title = "ChamberlinLowpass — a resonant cutoff sweep",
xTitle = "time",
yTitle = "hf ratio",
)
:::endfold

CurvedADSR — a gated three-note rhythm
A 220 Hz sine multiplied by the click-free curved envelope, gated on and off three times. Each note swells in and fades out with real silence between, and the curved envelope keeps the retrigger free of clicks. The envelope plot shows three clean humps.
:::static
val gate = Gate()
val voice = SineOsc(Param(220f)) * CurvedADSR(gate, Param(0.02f), Param(0.08f), Param(0.6f), Param(0.15f))
// (open?, seconds): three notes with silent gaps
val rhythm = [true to 0.25f, false to 0.20f, true to 0.25f, false to 0.20f, true to 0.25f, false to 0.35f]
:::fold render the gate schedule offline and plot the amplitude envelope
val audio = doughRenderGated(voice, gate, rhythm)
showSignals(
"|amplitude| envelope" to doughAbsEnvelope(audio, buckets = 400),
channel = "main",
title = "CurvedADSR — a gated three-note rhythm",
xTitle = "time",
yTitle = "|amp|",
)
:::endfold

SoftDistort — from clean to crunchy
A 220 Hz sine through dough's expm1 soft clipper while the drive sweeps 0.2 → 7. It starts as
a pure tone and grows a buzzy edge as the drive climbs and the clipper starts folding the peaks.
The plot overlays two two-cycle windows: an early clean sine and a late, flat-topped, harmonically
rich one.
:::static
val drive = Param(0.2f).also { it.supplier = { 0.2f + 6.8f * (time / 3f).coerceIn(0f, 1f) } }
val out = SineOsc(Param(220f)).through(SoftDistort(drive, Param(0.8f)))
:::fold render 3 s offline and overlay two 2-cycle windows (clean vs driven)
val audio = doughRenderMono(out, seconds = 3f)
showSignals(
"clean (t≈0.15 s)" to doughWaveWindow(audio, atSeconds = 0.15f, cycles = 2, freqHz = 220f),
"driven (t≈2.8 s)" to doughWaveWindow(audio, atSeconds = 2.8f, cycles = 2, freqHz = 220f),
channel = "main",
title = "SoftDistort — a sine driven from clean to crunchy",
xTitle = "sample",
yTitle = "amp",
)
:::endfold

ZawOsc / PulzeOsc — raw, aliasing tones
Both of these oscillators are naive and un-bandlimited. The clip is one second of a 200 Hz
sawtooth (ZawOsc) then one second of a 200 Hz narrow pulse (PulzeOsc, duty 0.3). The raw
ramps buzz with aliasing, which is what they are for. The plot shows two periods of each: a
clean rising ramp and a narrow rectangular pulse.
:::static
val saw = ZawOsc(Param(200f)) * 0.7f
val pulse = PulzeOsc(Param(200f), Param(0.3f)) * 0.7f
:::fold render one second of each (saw then pulse) and plot two periods of each
val audio = doughRenderMono(saw, seconds = 1f) + doughRenderMono(pulse, seconds = 1f)
showSignals(
"zaw (saw)" to doughWaveWindow(audio, atSeconds = 0.005f, cycles = 2, freqHz = 200f),
"pulze (duty 0.3)" to doughWaveWindow(audio, atSeconds = 1.005f, cycles = 2, freqHz = 200f),
channel = "main",
title = "ZawOsc / PulzeOsc — raw, aliasing tones",
xTitle = "sample",
yTitle = "amp",
)
:::endfold

See also: Subharmonic Synthesis, which runs pitch trackers through these filters and the saturator to put a tone an octave under a bassline, a kick drum and a film voice; A pattern in space, which plays patterns through the full dough engine; and Making a sound in Field for every sound path side by side.