Subtractive, additive, modal and FM synthesis, each as a complete runnable patch — the four approaches that account for most of the sound design you will ever do.

1. Basic waveforms and selection

The four classic oscillator shapes differ in harmonic content, and that difference is the starting point of subtractive synthesis: you cannot filter out harmonics that were never there.

ClassWaveformCharacter
SineOscSinePure tone, no harmonics
TriOscTriangleOdd harmonics, weak; soft and hollow
SquareOscSquareOdd harmonics, strong; bright and reedy
SawOscSawtoothAll harmonics; rich and buzzy
PulseOscPulseSquare with a variable duty cycle
NoiseWhite noiseAll frequencies at equal power

Beyond these, Phausto carries 42 oscillators in total, including the thirteen Casio CZ phase-distortion shapes and a family of antialiased variants. See the UGen Library.

1.1 Switching waveforms with PhSelect4

Combine four oscillators in parallel with the comma operator and use a PhNumEntry to choose between them at runtime:

"Create the four oscillators"
sine := SineOsc new.
tri  := TriOsc new.
sqr  := SquareOsc new.
saw  := SawOsc new.

"A numerical UI parameter — the index of the active oscillator"
index := PhNumEntry new label: 'Wave' init: 0 min: 0 max: 3 step: 1.

"Combine the oscillators in parallel using the comma operator"
oscillators := sine , tri , sqr , saw.

"Connect index + oscillators to the 4-input selector"
selector := index , oscillators connectTo: PhSelect4 new.

dsp := selector stereo asDsp.
dsp init.
dsp start.

"Open the UI — use the 'Wave' entry to switch waveform (0–3)"
dsp displayUI.
dsp stop.
dsp destroy.
This is FAUST showing through index , oscillators connectTo: … mirrors FAUST's own routing syntax closely. It is available when you need it, but the Phausto API in §1.2 is easier to read and should be preferred.

1.2 More than four with PhSelectN

When you need more than four options — adding Noise as a fifth source, say — PhSelectN takes any number of inputs:

sine  := SineOsc new.
tri   := TriOsc new.
sqr   := SquareOsc new.
saw   := SawOsc new.
noise := Noise new.

"five sources, so the index runs 0 to 4"
index := PhNumEntry new label: 'Wave' init: 0 min: 0 max: 4 step: 1.
oscillators := sine , tri , sqr , saw , noise.

selector := (PhSelectN new: oscillators) index: index.

dsp := selector stereo asDsp.
dsp init.
dsp start.
dsp displayUI.
dsp stop.
dsp destroy.
Match the index range to the source count If max: is smaller than the number of sources, the extra ones are simply unreachable — a silent bug, since nothing errors. Five sources need min: 0 max: 4.

2. Subtractive synthesis

Start from a harmonically rich source, shape its amplitude with an envelope, then sculpt its timbre with a filter. This is the classic analogue synthesiser model, and in Phausto it is three objects and two operators:

"1. An oscillator — the sound source"
oscillator := PulseOsc new.

"2. An ADSR envelope — shapes amplitude over time"
envelope := ADSREnv new.

"3. A Moog VCF low-pass filter — removes high frequencies"
filter := MoogVcf new.

"Chain them with the chuck operator:
 oscillator => envelope  multiplies the osc by the envelope signal
 => filter               feeds the result into the filter input"
synth := oscillator => envelope => filter.

dsp := synth stereo asDsp.
dsp init.
dsp start.

"Open the UI — press the gate button to trigger the envelope"
dsp displayUI.

"Experiment with attack, decay, sustain, release, and filter cutoff"
dsp stop.
Reading the chain Pharo binary operators associate left to right, so a => b => c is (a => b) => c. Signal flows in reading order, which is exactly what you want. More on this in Connecting Unit Generators.

Swap MoogVcf for any of the sixty filters — DiodeLadder, Korg35LPF, OberheimBPF, PhSvfLp — and the character changes completely while the patch stays the same. See Filters.

3. Additive synthesis

Additive synthesis builds complex timbres by summing sine waves, each with its own frequency, amplitude and phase. It is the direct expression of Fourier's theorem: any periodic waveform is a sum of sinusoids. Hammond organs and the Telharmonium are additive instruments built in hardware.

3.1 Stacking with a loop

"Start with one oscillator and add nine more, each detuned by 90 Hz"
sine1 := SineOsc new freq: 200; uLevel: 0.5.
detuning := 90.
(1 to: 9) do: [ :i |
  sine1 := sine1 + (SineOsc new freq: 200 + (i * detuning); uLevel: 0.05)
].

"sine1 is now the sum of ten detuned SineOscs"
dsp := sine1 stereo asDsp.
dsp init.
dsp start.
dsp stop.

Change detuning and listen. At 90 Hz the partials are far apart and you hear a chord; at 2 Hz they beat against each other and you hear a single thick tone.

3.2 Using asSumOfUGen

The same patch, expressed as a collection rather than an accumulator:

detuning := 14.
groupOfSine := (1 to: 10) collect: [ :i |
  SineOsc new freq: 200 + (i * detuning); uLevel: 0.05
].

"asSumOfUGen reduces the collection by summing every element"
dsp := groupOfSine asSumOfUGen stereo asDsp.
dsp init.
dsp start.
dsp stop.
method asSumOfUGen

Sent to any SequenceableCollection of UGens; returns a single UGen that is the mix of every element. Its sibling asChainOfUGen connects them in series instead — see Connecting Unit Generators.

Watch the headroom Ten oscillators at uLevel: 0.5 would sum to 5.0 and clip hard. Divide your budget: for n equal sources, start around 1/n.

5. FM synthesis

Frequency modulation uses one oscillator, the modulator, to vary the frequency or phase of another, the carrier. Two operators are enough for an enormous timbral range — this is the principle the Yamaha DX7 made famous, and Phausto includes DX7-style operators directly.

"A slider for the base frequency of both operators"
frequencyKnob := PhHSlider new label: 'Freq' values: #(100 20 4000 0.1).

"The ratio between carrier and modulator frequency"
ratioKnob := PhHSlider new label: 'Ratio' values: #(1 0.1 32 0.1).

"The modulator: a sine at frequency × ratio"
modulator := SineOsc new freq: frequencyKnob * ratioKnob.

"The carrier: a Dx7 operator whose phase the modulator drives"
carrier := Dx7Op new phaseMod: modulator; freq: frequencyKnob.

dsp := carrier stereo asDsp.
dsp init.
dsp start.
dsp displayUI.   "sweep Freq and Ratio to explore the space"
dsp stop.
dsp destroy.

5.1 The PhHSlider values: array

values: takes a four-element array in a fixed order. Getting the order wrong is a common source of sliders that appear to do nothing.

PositionMeaning
1stInitial value
2ndMinimum
3rdMaximum
4thStep size

So #(100 20 4000 0.1) reads: start at 100, range 20 to 4000, in steps of 0.1. The equivalent long form is label:init:min:max:step:, which PhNumEntry uses above. See UI Primitives.

Choosing ratios Integer ratios (1, 2, 3…) give harmonic spectra that sound pitched. Non-integer ratios (1.41, 2.7…) give inharmonic, bell-like or metallic timbres. Above about 16 the result tends towards noise.

Dx7Op exposes the full DX7 operator model — envelope rates and levels, breakpoints, keyboard scaling, LFO sensitivity — around ninety setters in all. For four- operator arrangements see Fm4Op, and for the two-operator shorthand, Fm2Op in TurboPhausto.

6. Pitch envelopes

Envelopes are not only for amplitude. Routed to frequency, an ADSREnv produces the pitch drop that defines a kick drum, or the swell of a tom:

"Scale the ADSR output to a 200 Hz sweep range"
pitchEnv := 200 asBox * ADSREnv new.

"Offset it from a 100 Hz base frequency"
osc := TriOsc new freq: (100 asBox + pitchEnv).

dsp := osc stereo asDsp.
dsp init.
dsp start.

"Open the UI — press the trigger to hear the sweep"
dsp displayUI.
dsp stop.
method asBox

asBox wraps a Pharo Number as a constant signal. FAUST arithmetic needs signals on both sides of an operator, so a bare number added to a UGen has to be promoted first. 100 asBox + pitchEnv is a signal; 100 + pitchEnv asks a SmallInteger to add a UGen to itself, which fails.

When you do not need asBox Setters accept plain numbers directly — SineOsc new freq: 440 is fine. asBox is only needed when the number is one operand of an arithmetic expression whose other operand is a signal.

The envelope family, and modulation more generally, is covered in Envelopes & Modulation.

7. Where to go next

DocumentWhat it covers
Envelopes & ModulationThe full envelope and LFO families, and where else to route them.
FiltersSixty filters, and which to reach for when.
Physical ModellingReady-made instruments and the waveguide primitives behind them.
Sequencing & SamplingDriving these patches from patterns instead of by hand.

8. Troubleshooting

The subtractive patch is silent no matter what I do

An ADSREnv outputs zero until it is gated. Open dsp displayUI and press the trigger button, or gate it from code — see Parameters & Control.

My additive stack is distorted and crackly

Summed amplitudes exceed 1.0 and are clipping. See the headroom note in §3.2.

The waveform selector only reaches some of my oscillators

The index range is too small. Five sources need max: 4 — see §1.2.

ModeFilter raises doesNotUnderstand

The class is PhModeFilter, and the impulsifier is PhImpulsify. See §4.

Moving the FM sliders changes nothing

Check the order of the values: array — a minimum above the maximum, or a step larger than the range, produces a slider that cannot move. See §5.1.

The modal patch clicks but never rings

The t60: values are too short, or the excitation is a continuous signal rather than an impulse. Pulse => PhImpulsify gives a single-sample click; feeding a raw Pulse keeps the modes driven and smears the decay.