Last Friday I tweeted out the following bit of Supercollider code.

play{x=LFSaw.ar([0.3,0.6])*LFSaw.ar([1,1.5])*SinOsc.ar([440,220]*LFPulse.kr(0.12).range(1,2));CombC.ar(x,0.5,0.5,9,0.5,x)/12}

Fitting Supercollider code in a tweet has always been an interesting challenge. I thought I might take a few minutes to deconstruct what I am doing with this tweet. Even if you are new to Supercollider I encourage you to follow along step by step and try to understand what I am doing with this.

First things first, below is the same code with the whitespace added back in:

( play{ x = LFSaw.ar([0.3, 0.6]) * LFSaw.ar([1, 1.5]) * SinOsc.ar([440, 220] * LFPulse.kr(0.12).range(1, 2)); CombC.ar(x, 0.5, 0.5, 9, 0.5, x) / 12 } )

We can see above that I assign my main pattern to the variable x and then I reuse the value x in a comb filter. Let’s ignore the comb filter for now and break down what I am doing to make up variable x. The main tone of the piece is sine wave. By passing the frequency as an array I am making two copies of the sine wave, one for each speaker. Since one frequency is twice the value of the other the tones are an octave apart:

SinOsc.ar([440, 220])

The next piece of code we want to look at is the LFPulse:

LFPulse.kr(0.12).range(1, 2)

This generates a low frequency square wave, I use range to scale it to alternate between the values of 1 and 2.

This is multiplied by the frequency array to alternate the frequency between 440 to 880 on the left speaker and 220 to 440 on the right.

Listening to the code below you can already hear the track taking shape:

{SinOsc.ar([440, 220] * LFPulse.kr(0.12).range(1, 2))}.play

To avoid having an endless unchanging drone we need to add rhythmic interest. I do this by using two saw LFOs controlling the amplitude. As an added bonus the pops caused by the amplitude jumps in the saw LFOs provide a rhythmic clicking:

LFSaw.ar([0.3, 0.6]) * LFSaw.ar([1, 1.5])

The values are not quite multiples of each other. This allows us to have a pattern that seems to make sense but does not remain constant. We again use separate frequency values for each speaker.

Listening to the code in the current state we can already hear a pretty close representation of the track:

LFSaw.ar([0.3, 0.6]) * LFSaw.ar([1, 1.5]) * SinOsc.ar([440, 220] * LFPulse.kr(0.12).range(1, 2))

Now that we have broken down the code that makes up the x variable we can look into the comb filter:

CombC.ar(x, 0.5, 0.5, 9, 0.5, x) / 12

I am using the comb filter as a simple delay with feedback. The speed of the delay is a multiple of one of the saw LFOs to further accentuate the rhythm of the track. I pass x to the add parameter of the comb filter in order to mix the dry signal with the wet. Due to the fact that the comb filter has a built in feedback loop I divide the final output by 12 to avoid clipping. NOTE: I could have used a CombN or CombL here to be more efficient as I am not modulating the delay time. I chose CombC simply for future expansion of this code.

This code demonstrates one of the most important thing I have learned while experimenting with Supercollider and modular synthesis. You do not need to have an explicit sequencer to generate rhythmic or melodic material. Through proper scaling of oscillators you can create quite complicated patterns.