---
title: Audio effects
layout: ../../layouts/MainLayout.astro
---
import { MiniRepl } from '../../docs/MiniRepl';
import { JsDoc } from '../../docs/JsDoc';
# Audio Effects
Whether you're using a synth or a sample, you can apply any of the following built-in audio effects.
As you might suspect, the effects can be chained together, and they accept a pattern string as their argument.
# Signal chain
<img src="/img/strudel-signal-flow.png"></img>
The signal chain in Strudel is as follows:
- An sound-generating event is triggered by a pattern
- This has a start time and a duration, which is usually
controlled by the note length and ADSR parameters
- If we exceed the max polyphony, old sounds begin to die off
- Muted sounds (one whose `s` value is `-`, `~`, or `_`) are skipped
- A sound is produced (through, say, a sample or an oscillator)
- This is where detune-based effects (like `detune`, `penv`, etc. occur)
- The following will occur _in order_ and only if they've been called in the pattern. Note that all of these are
single use effects, meaning that multiple occurrences of them in a pattern will simply override the values
(e.g. you can't do `s("bd").lpf(100).distort(2).lpf(800)` to lowpass, distort, and then lowpass
again)
- Phase vocoder (`stretch`)
- Gain is applied (`gain`)
- This is where the main (volume) ADSR happens
- A lowpass filter (`lpf`)
- A highpass filter (`hpf`)
- A bandpass filter (`bandpass`)
- A vowel filter (`vowel`)
- Sample rate reduction (`coarse`)
- Bit crushing (`crush`)
- Waveshape distortion (`shape`)
- Normal distortion (`distort`)
- Tremolo (`tremolo`)
- Compressor (`compressor`)
- Panning (`pan`)
- Phaser (`phaser`)
- Postgain (`post`)
- The sound is then split into multiple destinations
- Dry output (amount controlled by `dry` parameter)
- The sends
- Analyzers
- These are used for tooling like `scope` and `spectrum` and their setup usually happens behind the scenes
- Delay (amount controlled by `delay` parameter)
- Reverb (amount controlled by `room` parameter)
- The dry output, delay, and reverb are joined into what is called the "orbit" of the pattern (see more in the section below)
- The `duck` effect affects the volume of all signals in the orbit
- The orbit is then sent to the mixer
## Orbits
Orbits are the way in which outputs are handled in Strudel. They also prescribe which delay and reverb to associate with the dry signal.
By default, all orbits are mixed down to channels `1` and `2` in stereo, however with the "Multi Channel Orbits" setting
(under Settings at the right) you can use them as individual 2 channel stereo outs (orbit `i` will be mapped to
to channels `2i` and `2i + 1`). You can then use routers like Blackhole 16 to retrieve and record all of the channels in a DAW for later processing.
The default orbit is `1` and it is set with `orbit`. You may send a sound to multiple orbits via mininotation
<MiniRepl client:visible tune={`s("white").orbit("2,3,4").gain(0.2)`} />
but please be careful as this will create three copies of the sound behind the scenes, meaning that if they are mixed
down to a single output, they will triple the volume. We've reduced the gain here to save your ears.
⚠️ There is only one delay and reverb per orbit, so please be aware that if you attempt to change the parameters on two
patterns pointing to the same orbit, it can lead to unpredictable results. Compare, for example, this pretty pluck
with a large reverb:
<MiniRepl
client:visible
tune={`
$: s("triangle*4").decay(0.5).n(irand(12)).scale('C minor')
.room(1).roomsize(10)`}
/>
versus the same pluck with a muted kick drum coming in and overwriting the `roomsize` value:
<MiniRepl
client:visible
tune={`
$: s("triangle*4").decay(0.5).n(irand(12)).scale('C minor')
.room(1).roomsize(10)
$: s("bd\*4").room(0.01).roomsize(0.01).postgain(0)`}
/>
This is due to them sharing the same orbit: the default of `1`. It can be corrected simply by updating the orbits to be
distinct:
<MiniRepl
client:visible
tune={`
$: s("triangle*4").decay(0.5).n(irand(12)).scale('C minor')
.room(1).roomsize(10).orbit(2)
$: s("bd\*4").room(0.01).roomsize(0.01).postgain(0)`}
/>
## Continuous changes
As all of the above is triggered by a _sound occurring_, it is often the case that parameters may not be
modified continuously in time. For example,
<MiniRepl
client:visible
tune={`
s("supersaw").lpf(tri.range(100, 5000).slow(2))`}
/>
Will not produce a continually LFO'd low-pass filter due to the `tri` only being sampled every time the note hits
(in this case the default of once per cycle). You can fake it by introducing more sound-generating events, e.g.:
<MiniRepl
client:visible
tune={`
s("supersaw").seg(16).lpf(tri.range(100, 5000).slow(2))`}
/>
Some parameters _do_ induce continuous variations in time, though:
- The ADSR curve (governed by `attack`, `sustain`, `decay`, `release`)
- The pitch envelope curve (governed by `penv` and its associated ADSR)
- The FM curve (`fmenv`)
- The filter envelopes (`lpenv`, `hpenv`, `bpenv`)
- Tremolo (`tremolo`)
- Phaser (`phaser`)
- Vibrato (`vib`)
- Ducking (`duckorbit`)
# Filters
Filters are an essential building block of [subtractive synthesis](https://en.wikipedia.org/wiki/Subtractive_synthesis).
Strudel comes with 3 types of filters:
- low-pass filter: low frequencies may _pass_, high frequencies are cut off
- high-pass filter: high frequencies may _pass_, low frequencies are cut off
- band-pass filters: only a frequency band may _pass_, low and high frequencies around are cut off
Each filter has 2 parameters:
- cutoff: the frequency at which the filter starts to work. e.g. a low-pass filter with a cutoff of 1000Hz allows frequencies below 1000Hz to pass.
- q-value: Controls the resonance of the filter. Higher values sound more aggressive. Also see [Q-Factor](https://en.wikipedia.org/wiki/Q_factor)
## lpf
<JsDoc client:idle name="lpf" h={0} />
## lpq
<JsDoc client:idle name="lpq" h={0} />
## hpf
<JsDoc client:idle name="hpf" h={0} />
## hpq
<JsDoc client:idle name="hpq" h={0} />
## bpf
<JsDoc client:idle name="bpf" h={0} />
## bpq
<JsDoc client:idle name="bpq" h={0} />
## ftype
<JsDoc client:idle name="ftype" h={0} />
## vowel
<JsDoc client:idle name="vowel" h={0} />
# Amplitude Modulation
Amplitude modulation changes the amplitude (gain) periodically over time.
## am
<JsDoc client:idle name="am" h={0} />
## tremolosync
<JsDoc client:idle name="tremolosync" h={0} />
## tremolodepth
<JsDoc client:idle name="tremolodepth" h={0} />
## tremoloskew
<JsDoc client:idle name="tremoloskew" h={0} />
## tremolophase
<JsDoc client:idle name="tremolophase" h={0} />
## tremoloshape
<JsDoc client:idle name="tremoloshape" h={0} />
# Amplitude Envelope
The amplitude [envelope](<https://en.wikipedia.org/wiki/Envelope_(music)>) controls the dynamic contour of a sound.
Strudel uses ADSR envelopes, which are probably the most common way to describe an envelope:

[image link](https://commons.wikimedia.org/wiki/File:ADSR_parameter.svg)
## attack
<JsDoc client:idle name="attack" h={0} />
## decay
<JsDoc client:idle name="decay" h={0} />
## sustain
<JsDoc client:idle name="sustain" h={0} />
## release
<JsDoc client:idle name="release" h={0} />
## adsr
<JsDoc client:idle name="adsr" h={0} />
# Filter Envelope
Each filter can receive an additional filter envelope controlling the cutoff value dynamically. It uses an ADSR envelope similar to the one used for amplitude. There is an additional parameter to control the depth of the filter modulation: `lpenv`|`hpenv`|`bpenv`. This allows you to play subtle or huge filter modulations just the same by only increasing or decreasing the depth.
<MiniRepl
client:idle
tune={`note("[c eb g <f bb>](3,8,<0 1>)".sub(12))
.s("<sawtooth>/64")
.lpf(sine.range(300,2000).slow(16))
.lpa(0.005)
.lpd(perlin.range(.02,.2))
.lps(perlin.range(0,.5).slow(3))
.lpq(sine.range(2,10).slow(32))
.release(.5)
.lpenv(perlin.range(1,8).slow(2))
.ftype('24db')
.room(1)
.juxBy(.5,rev)
.sometimes(add(note(12)))
.stack(s("bd*2").bank('RolandTR909'))
.gain(.5).fast(2)`}
/>
There is one filter envelope for each filter type and thus one set of envelope filter parameters preceded either by `lp`, `hp` or `bp`:
- `lpattack`, `lpdecay`, `lpsustain`, `lprelease`, `lpenv`: filter envelope for the lowpass filter.
- alternatively: `lpa`, `lpd`, `lps`, `lpr` and `lpe`.
- `hpattack`, `hpdecay`, `hpsustain`, `hprelease`, `hpenv`: filter envelope for the highpass filter.
- alternatively: `hpa`, `hpd`, `hps`, `hpr` and `hpe`.
- `bpattack`, `bpdecay`, `bpsustain`, `bprelease`, `bpenv`: filter envelope for the bandpass filter.
- alternatively: `bpa`, `bpd`, `bps`, `bpr` and `bpe`.
## lpattack
<JsDoc client:idle name="lpattack" h={0} />
## lpdecay
<JsDoc client:idle name="lpdecay" h={0} />
## lpsustain
<JsDoc client:idle name="lpsustain" h={0} />
## lprelease
<JsDoc client:idle name="lprelease" h={0} />
## lpenv
<JsDoc client:idle name="lpenv" h={0} />
# Pitch Envelope
You can also control the pitch with envelopes!
Pitch envelopes can breathe life into static sounds:
<MiniRepl
client:idle
tune={`n("<-4,0 5 2 1>*<2!3 4>")
.scale("<C F>/8:pentatonic")
.s("gm_electric_guitar_jazz")
.penv("<.5 0 7 -2>*2").vib("4:.1")
.phaser(2).delay(.25).room(.3)
.size(4).fast(1.5)`}
/>
You also create some lovely chiptune-style sounds:
<MiniRepl
client:idle
tune={`n(run("<4 8>/16")).jux(rev)
.chord("<C^7 <Db^7 Fm7>>")
.dict('ireal')
.voicing().add(note("<0 1>/8"))
.dec(.1).room(.2)
.segment("<4 [2 8]>")
.penv("<0 <2 -2>>").patt(.02).fast(2)`}
/>
Let's break down all pitch envelope controls:
## pattack
<JsDoc client:idle name="pattack" h={0} />
## pdecay
<JsDoc client:idle name="pdecay" h={0} />
## prelease
<JsDoc client:idle name="prelease" h={0} />
## penv
<JsDoc client:idle name="penv" h={0} />
## pcurve
<JsDoc client:idle name="pcurve" h={0} />
## panchor
<JsDoc client:idle name="panchor" h={0} />
# Dynamics
## gain
<JsDoc client:idle name="gain" h={0} />
## velocity
<JsDoc client:idle name="velocity" h={0} />
## compressor
<JsDoc client:idle name="compressor" h={0} />
## postgain
<JsDoc client:idle name="postgain" h={0} />
## xfade
<JsDoc client:idle name="xfade" h={0} />
# Panning
## jux
<JsDoc client:idle name="jux" h={0} />
## juxBy
<JsDoc client:idle name="juxBy" h={0} />
## pan
<JsDoc client:idle name="pan" h={0} />
# Waveshaping
## coarse
<JsDoc client:idle name="coarse" h={0} />
## crush
<JsDoc client:idle name="crush" h={0} />
## distort
<JsDoc client:idle name="distort" h={0} />
# Global Effects
## Local vs Global Effects
While the above listed "local" effects will always create a separate effects chain for each event,
global effects use the same chain for all events of the same orbit:
## orbit
<JsDoc client:idle name="orbit" h={0} />
## Delay
### delay
<JsDoc client:idle name="delay" h={0} />
### delaytime
<JsDoc client:idle name="delaytime" h={0} />
### delayfeedback
<JsDoc client:idle name="delayfeedback" h={0} />
## Reverb
### room
<JsDoc client:idle name="room" h={0} />
### roomsize
<JsDoc client:idle name="roomsize" h={0} />
### roomfade
<JsDoc client:idle name="roomfade" h={0} />
### roomlp
<JsDoc client:idle name="roomlp" h={0} />
### roomdim
<JsDoc client:idle name="roomdim" h={0} />
### iresponse
<JsDoc client:idle name="iresponse" h={0} />
## Phaser
### phaser
<JsDoc client:idle name="phaser" h={0} />
### phaserdepth
<JsDoc client:idle name="phaserdepth" h={0} />
### phasercenter
<JsDoc client:idle name="phasercenter" h={0} />
### phasersweep
<JsDoc client:idle name="phasersweep" h={0} />
## Duck
### duckorbit
<JsDoc client:idle name="duckorbit" h={0} />
### duckattack
<JsDoc client:idle name="duckattack" h={0} />
### duckdepth
<JsDoc client:idle name="duckdepth" h={0} />
Next, we'll look at input / output via [MIDI, OSC and other methods](/learn/input-output).