Kieran Klaassen

I'm Kieran, a creator and engineer, composer and baker. I craft with love, creating code to croissants. Curiosity and discovery shape my life. I try to inspire others and stay true to myself.

← All thoughts

Filtering Noise with SuperCollider

AI-assisted writing. The original ideas and experiences are mine.

Sometimes I need to switch off the goals and expectations and play. For me, that usually means making music. Lately, I've had an itch to get back into SuperCollider, so I gave myself a simple constraint: start with pink noise and use filtering to shape an evolving tone. This is what came out:

How it works

The piece has three parts: filtered noise, a drone, and two random sequencers that choose notes and chords.

1. Filtered noise

// Define a SynthDef for filtered pink noise
SynthDef(\filteredPinkNoise, { |out=0, freq=440, pan=0|
    var signal, env;

    // Generate pink noise
    signal = PinkNoise.ar(1);

    // Apply band-pass filter
    signal = BPF.ar(signal, freq, 0.05);

    // ADSR envelope like a raindrop with a long decay
    env = EnvGen.ar(Env.perc(0.01, 2, 1, -4), doneAction:2);
    signal = signal * env;

    // Pan the signal
    Out.ar(out, Pan2.ar(signal, pan));
}).add;

2. The drone

// Define a SynthDef for a drone
SynthDef(\drone, { |out=0, freqs=#[440, 550, 660, 770], amp=0.15, atk=3, rel=7|
    var signal, env, leftSignal, rightSignal;

    // Create a sum of sine waves for each frequency
    signal = Mix(SinOsc.ar(freqs, 0, amp));

    // ADSR envelope with long attack and decay
    env = EnvGen.ar(Env.linen(atk, 5, rel, 1, -4), doneAction:2);
    signal = signal * env;

    // Stereo widening effect by slightly detuning left and right channels
    leftSignal = signal * LFNoise2.kr(0.4).range(0.98, 1.02);
    rightSignal = signal * LFNoise2.kr(0.4).range(0.98, 1.02);

    // Add reverb (optional, add if needed)
    signal = FreeVerb.ar(signal, 0.4, 0.9, 0.5);

    Out.ar(out, [leftSignal, rightSignal]);
}).add;

3. Randomization and sequencing

// Function to randomly choose a frequency from the A minor scale
~randomFreq = {
    var scale = Scale.minorPentatonic.degrees;
    var root = 69; // MIDI note number for A4
    var note = root + scale.choose;
    440 * (2 ** ((note - 69) / 12));
};

// Function to generate random chord frequencies
~randomChord = {
    var scale = Scale.minorPentatonic.degrees;
    var root = 69; // MIDI note number for A4
    var notes = List.fill(4, { root + scale.choose });
    notes = notes.scramble; // Randomize note order
    notes.collect({ |note| 440 * (2 ** ((note - 69) / 12)) });
};

// Random Sequencer for filteredPinkNoise
{
    var freq, pan;
    inf.do {
        freq = ~randomFreq.value;
        pan = 2.rand - 1; // Random pan between -1 and 1
        Synth(\filteredPinkNoise, [freq: freq, pan: pan]);
        0.5.wait; // Wait for 500 ms
    };
}.fork;

// Random Sequencer for drone
{
    var chordFreqs;
    inf.do {
        chordFreqs = ~randomChord.value;
        Synth(\drone, [freqs: chordFreqs]);
        10.wait; // Change chord every 10 seconds
    };
}.fork;

Back to playing

This project got me back into open-ended sound design. SuperCollider let me define each layer precisely, then hand some control back to the random sequencing.

If you want to play with it, change the filter settings or envelope shapes, or add another layer or effect. See where it goes.

Source code: https://github.com/kieranklaassen/Supercollider/blob/main/01_pinkoise_drone.scd