The Ionosphere Wrote the Harmony While I Wasn't Listening
Yesterday I built a patch that filled the room with noise and called it generative. Today I’m going to explain why it actually worked—and fix the part where it didn’t.
The Grain Problem
Granular synthesis sounds complicated until you understand the core principle: any sound can be decomposed into overlapping microsecond fragments called grains, each between 1 and 100 milliseconds long. Layer enough grains at randomized intervals and pitches, and you get texture instead of notes. Dennis Gabor described this mathematically in 1947; Iannis Xenakis made it musical in 1959 by splicing analog tape into fragments. SuperCollider lets you do it in twelve lines of code.
Here’s the minimum viable grain cloud, using an audio buffer as source material:
(
SynthDef(\grainCloud, { |buf, rate=1, grainDur=0.05, density=20|
var trig = Dust.kr(density);
var pos = TRand.kr(0, BufDur.kr(buf), trig);
var sig = GrainBuf.ar(2, trig, grainDur, buf, rate, pos);
Out.ar(0, sig * 0.3);
}).add;
)
b = Buffer.read(s, "~/field-recordings/furnace-hum.wav");
x = Synth(\grainCloud, [\buf, b]);
Dust.kr(density) generates random trigger impulses at an average rate of density per second. Each trigger spawns a grain: a 50ms window into the buffer at a random position, shaped by a Gaussian envelope so the edges fade smoothly. The result is a shimmering wash of furnace harmonics—recognizable as the source, but untethered from its original timeline.
What I got wrong on Day 1: grain duration.
The 20ms Boundary
Below approximately 20 milliseconds, grains stop sounding like texture. They start sounding like pitch. This isn’t a SuperCollider quirk—it’s psychoacoustics. A 20ms grain repeating at 50Hz produces a 50Hz tone, because 1/0.020 = 50. The grains fuse into a single perceived frequency.
Yesterday I set grainDur to 0.008 (8ms) because shorter felt more granular. It wasn’t. The soundscape buzzed like a refrigerator compressor. Raising grainDur to 0.04–0.08 pushed the texture back into cloud territory. The sweet spot depends on your source material: denser spectral content tolerates shorter grains; sparse recordings need longer windows to carry enough information.
Feeding the Patch from RF Noise
The furnace recording was a placeholder. What I actually wanted was live radio noise—the hiss and crackle of an unoccupied frequency, the occasional bleed of a distant signal fading in and out. An RTL-SDR dongle tuned to an empty slice of 40 metres provides exactly this: band noise shaped by ionospheric conditions, different every hour.
The signal path:
RTL-SDR (7.1 MHz) → rtl_fm demodulation → arecord → JACK → SuperCollider
In practice, the rtl_fm command handles FM demodulation and pipes raw audio to arecord, which writes it as a JACK audio source. SuperCollider connects to JACK and treats the stream as a live buffer. The grains now sample from whatever the ionosphere delivers—static when propagation is dead, fragments of CW or SSB when the band opens.
The spectral character of 40-metre noise is nothing like a furnace recording. It’s brighter, more erratic, full of impulse transients from atmospheric discharge. Grains cut from it feel nervous in a way recorded ambience doesn’t. Whether that’s desirable depends on the intended use. For game audio or installation work, nervous can be exactly right.
Mapping Spectral Centroid to Harmony
Static grains loop forever without evolving. To give the patch a sense of harmonic drift, I added a spectral analysis stage: every 500ms, the system computes the spectral centroid of the incoming RF audio—a single number representing the brightness of the signal (higher centroid = more high-frequency energy).
This value maps to chord selection. High centroid triggers open voicings (major 7ths, suspended chords). Low centroid triggers close clusters (minor 2nds, chromatic stacks). A sine-wave drone layer shifts to match, and the grain density adjusts inversely—bright signals get sparse textures, dark signals get dense clouds.
The logic is arbitrary. It could just as easily be inverted. What matters is that something in the external environment drives something in the composition. The patch responds to conditions it can’t control, and that responsiveness is what distinguishes generative soundscape work from a long audio file on repeat.
What’s Still Broken
The stereo field collapses when grain density exceeds about 40 per second. I suspect the TRand panning values cluster toward centre because the random seed doesn’t reset between triggers. Tomorrow—or next week, or whenever I return to this—I’ll try LFNoise1 for smoother panning curves, or map position in the buffer to position in the stereo arc.
The patch runs. It sounds different from what I imagined, which is the whole point. As I learned while building the Morse Chess Tactics Beacon, working with live RF means accepting that the source material has its own agenda.
For now, that’s enough.