A patch is UGens joined by operators. Phausto gives you a handful, each corresponding to one of FAUST's composition primitives — and once you know them, most patches read like a sentence.

1. Series: the chuck operator

=> connects two UGens in series: the output of the left becomes the input of the right. The name and the symbol are borrowed, with affection, from ChucK.

"An electric guitar model into a reverb"
synth := ElecGuitar new => GreyHole new.

dsp := synth asDsp.
dsp init.
dsp start.
dsp displayUI.   "press ElecGuitarTrigger to pluck"
dsp stop.

The long form is chuckInto:, and the two are interchangeable. Use the operator for chains, the keyword message when the expression is already dense with operators.

1.1 The receiver decides

=> does not always mean the same thing. What happens depends on the UGen on the right — each class defines how it takes an input. This is what makes the operator concise rather than ambiguous: an envelope has only one sensible way to combine with a signal, and so does a filter.

ExpressionWhat it means
osc => envThe oscillator is multiplied by the envelope — amplitude shaping
osc => filterThe oscillator becomes the filter's input
synth => reverbThe synth signal feeds the reverb
pulse => PhImpulsifyA gate becomes a one-sample impulse
signal => saturatorThe signal is waveshaped

1.2 Precedence

Pharo binary operators all have equal precedence and associate left to right, so a chain needs no parentheses and reads in signal order:

synth := oscillator => envelope => filter.
"is exactly ((oscillator => envelope) => filter)"
Where this bites Equal precedence also means a + b * c is (a + b) * c, not what arithmetic convention suggests. When mixing + and * in a signal expression, parenthesise deliberately.

2. Parallel: the comma operator

, places UGens side by side as separate channels rather than mixing them. A four-oscillator parallel block has four outputs, not one:

oscillators := sine , tri , sqr , saw.
"four channels, ready for a selector or a multichannel destination"
Parallel is not mixing a , b gives two channels; a + b gives one channel containing their sum. Feeding a parallel block somewhere that expects a single input is the most common connection error in Phausto.

Parallel composition is what selectors, multichannel effects and routing primitives consume. See Synthesis §1 for the waveform selector built this way.

3. Arithmetic

The arithmetic operators work sample by sample on signals, which makes them the workhorses of modulation.

OperatorEffectTypical use
+SumMixing signals; offsetting a modulator
-DifferencePhase cancellation; bipolar offsets
*ProductAmplitude modulation; scaling a control signal
/QuotientNormalising; ratio control
%ModuloWrapping phase or index values
"Mixing: two instruments into one signal"
mix := djembe + marimba.

"Amplitude modulation: a 5 Hz tremolo"
trem := SineOsc new * (LFOTri new freq: 5).

"Scaling a control signal into a useful range"
sweep := ADSREnv new * 2000 asBox.

Comparison operators are available too — <, <=, >, >=, equalTo:, notEqualTo: — each producing a signal that is 1 where the comparison holds and 0 elsewhere. They are how you build gates and conditional logic inside the graph.

3.1 Mixing in plain numbers

FAUST arithmetic needs a signal on both sides. A number has to be promoted first, with asBox:

"correct — the number becomes a constant signal"
freq := 100 asBox + pitchEnv.

"wrong — asks a SmallInteger to add a UGen to itself"
freq := 100 + pitchEnv.
Setters are the exception Setters promote numbers for you, so SineOsc new freq: 440 needs no asBox. You only need it when a bare number is one operand of an arithmetic expression whose other operand is a signal.

Phausto adds a few other conversions to Number:

MessageResult
asBoxA constant signal
asConstA compile-time constant
midiNNToFreqMIDI note number converted to hertz

4. Recursion and feedback

~ is FAUST's recursive composition: it feeds a UGen's output back into its own input, delayed by one sample. This is the primitive underneath every delay line, comb filter and feedback network.

"The recursive operator — output fed back as input"
loop := someUGen ~ someOtherUGen.
Feedback needs a leash A recursive network with gain at or above 1 will grow without limit. Keep a coefficient below unity inside the loop, or place a limiter after it — see Effects, Reverbs & Dynamics. In practice most people reach for a ready-made comb filter or reverb rather than building the loop by hand.

5. Explicit routing

When the operators are not enough — usually because channel counts do not line up — there are explicit routing messages.

MessageEffect
connectTo:Sequential connection, matching outputs to inputs
chuckInto:Keyword form of =>
splitTo:Fan one signal out to several inputs
mergeWith:Fold several channels down into fewer
patchedWith:Supply an input to a UGen that expects one
par:with:Explicit parallel composition
"The FAUST-style routing used by the four-way waveform selector"
selector := index , oscillators connectTo: PhSelect4 new.

Corresponding UGens exist for the same jobs — PhSplitOperation, PhMergeOperation, PhCrossnn, PhBus — when you would rather have an object in the graph than an operator between two.

6. Connecting collections

Phausto extends SequenceableCollection, so an ordinary Pharo array of UGens can be collapsed into a single signal in one message. This is usually clearer than accumulating in a loop:

MessageEffect
asSumOfUGenMix every element into one signal
asChainOfUGenConnect every element in series, left to right
asPhListBuild a PhList from the values
asPhZeroListBuild a zero-filled PhZeroList
"Ten detuned oscillators, summed"
partials := (1 to: 10) collect: [ :i |
  SineOsc new freq: 200 + (i * 14); uLevel: 0.05
].
voice := partials asSumOfUGen.

"A stack of effects, applied one after another"
chain := { CubicNl new. MoogVcf new. GreyHole new } asChainOfUGen.

7. Changing the channel count

Two messages handle almost every channel mismatch you will meet:

MessageEffect
stereoDuplicate one channel into two
monoCollapse two channels into one
numberOfInputsAsk how many channels a UGen consumes
numberOfOutputsAsk how many it produces

Order matters: stereo applied before an effect gives the effect two channels to work with; applied after, it duplicates whatever the effect produced. For genuinely stereo reverbs you want the former.

"true stereo processing"
dsp := (synth stereo => ZitaRevStereo new) asDsp.

"mono processing, widened afterwards"
dsp := (synth => FreeverbMono new) stereo asDsp.

8. Unary maths on a signal

Any signal understands the common mathematical messages, applied sample by sample. They are useful for shaping control signals — rectifying an LFO, converting units, folding a value into a range.

GroupMessages
Sign and roundingabs, absolute, floor, ceil, round, rInt
Powers and logssqrt, exp, log, log10
Trigonometrysin, cos, aSin, aCos, aTan
Rangesmin:, max:, fMod:
Audio unitsdb2linear, linear2db, midikey2hz
Smoothingsmoo — a one-pole smoother for jumpy control values
"smoo takes the zipper noise out of a stepped control"
cutoff := (PhHSlider new label: 'Cutoff' values: #(800 40 12000 1)) smoo.

Each of these has an equivalent UGen class as well, catalogued in Math & Conversion Tools.

9. Where to go next

DocumentWhat it covers
Parameters & ControlNaming what you have connected, and driving it at runtime.
Synthesis TechniquesThese operators applied to complete patches.
Raw FAUST & the Box APIThe FAUST primitives each operator compiles down to.
Math & Conversion ToolsThe UGen form of every operation on this page.

10. Troubleshooting

My expression raises an error about inputs and outputs not matching

A parallel block met something expecting one channel, or a stereo signal met a mono UGen. Check numberOfOutputs on the left and numberOfInputs on the right — §7.

Adding two UGens made them quieter, not louder

Two correlated signals of opposite phase partially cancel. If they are the same source, a + a doubles amplitude and may clip; if they are detuned copies, expect beating rather than a clean sum.

100 + someUGen raises doesNotUnderstand

Promote the number: 100 asBox + someUGen — §3.1.

a + b * c does not do what I expected

Pharo binary operators are all equal precedence, left to right. Write a + (b * c) — §1.2.

The comma operator gave me four voices instead of a mix

That is what it does. Use + or asSumOfUGen to mix — §2.

A feedback patch got loud very fast

Loop gain is at or above 1. See the warning in §4.