SuperCollider is a powerful, open-source platform for audio synthesis, composition, and algorithmic sound design. It consists of two main components:
sclang: The SuperCollider programming language, a dynamically-typed, high-level language for controlling the synthesis server and structuring musical ideas.scsynth: The synthesis engine (server), responsible for real-time audio processing, implementing unit generators (UGens) for synthesis, effects, and routing.
Core Architecture
Language (sclang)
SuperCollider’s language (sclang) is a functional, object-oriented language inspired by Smalltalk and Lisp. It includes constructs for defining instruments, scheduling events, and live coding.
Example: Defining a Simple Synth
// Define a SynthDef (synthesis definition)
SynthDef(\sine, {
|freq = 440, amp = 0.5|
var signal = SinOsc.ar(freq, 0, amp); // Sine wave oscillator
Out.ar(0, signal); // Send to output bus 0
}).add; // Add to the server's SynthDef library
// Instantiate the SynthDef on the server
Synth(\sine, [\freq, 880, \amp, 0.2]);Server (scsynth)
The server (scsynth) operates as a separate real-time process, handling low-latency audio computation. Communication with sclang or other clients occurs through Open Sound Control (OSC) messages.
Server Features
- Graph-based Synthesis: All operations are defined as a directed acyclic graph (DAG), connecting UGens for precise audio routing and processing.
- Unit Generators (UGens): Modular building blocks for synthesis, including oscillators, filters, and audio effects.
Example: Understanding Node Graphs
// Visualize the node graph
s.plotTree;
// Create nodes with varying routing
Synth(\sine, [\freq, 440]); // Node 1000
Synth(\sine, [\freq, 660]); // Node 1001The plotTree method visually represents nodes and their connections, invaluable for debugging complex audio graphs.
Best Practices
Using Buses for Modular Routing
SuperCollider uses buses (audio and control) to route signals efficiently. This modular approach enables advanced setups like parallel processing.
// Create an audio bus
~bus = Bus.audio(s, 2); // Stereo bus
// Synth writing audio to the bus
SynthDef(\modulator, {
|outBus, freq = 440|
var signal = SinOsc.ar(freq, 0, 0.5);
Out.ar(outBus, signal);
}).add;
Synth(\modulator, [\outBus, ~bus]); // Write to bus
// Synth reading from the bus
SynthDef(\output, {
|inBus|
var input = In.ar(inBus, 2);
Out.ar(0, input); // Output to speakers
}).add;
Synth(\output, [\inBus, ~bus]);Scheduling with Patterns
Patterns provide a declarative way to sequence events, encapsulating rhythm, pitch, and dynamics.
// Create a pattern
Pbind(
\instrument, \sine,
\freq, Pseq([440, 660, 880], inf), // Sequence of frequencies
\dur, 0.25 // Duration of each event
).play;Integration with External Tools
Using OSC for Interoperability
SuperCollider supports OSC for communicating with other software (e.g., Sonic Pi, Max/MSP). This enables distributed systems and hybrid setups.
// Receive OSC messages
OSCdef(\example, {
|msg, time, addr, port|
"Received OSC message: %".format(msg).postln;
}, '/example');
// Send OSC messages
NetAddr("127.0.0.1", 57120).sendMsg("/example", 1, 2, 3);PipeWire and SuperCollider
While SuperCollider traditionally interacts with JACK or ALSA, PipeWire provides a flexible environment for integrating SuperCollider with modern Linux audio systems. Use tools like qpwgraph to visualize and manage connections.
pw-cli create-link <scsynth-node-id> <sink-id>Advanced Techniques
Custom Unit Generators (UGens)
Develop custom UGens in C++ for specialized audio processing. UGens are defined by extending the UGen class, providing real-time DSP.
#include "SC_PlugIn.h"
struct MySaw : public SCUnit {
float mPhase;
MySaw() { mPhase = 0.0f; }
void next(int nSamples) {
// Generate sawtooth waveform
}
};
PluginLoad(MyUGen) {
ft = inTable;
DefineSimpleUnit(MySaw);
}Buffer Manipulation
Use buffers for sampling and granular synthesis.
// Load a sound file
b = Buffer.read(s, "path/to/sound.wav");
// Play the buffer
SynthDef(\playBuf, {
|bufnum|
var signal = PlayBuf.ar(1, bufnum, BufRateScale.kr(bufnum), loop: 1);
Out.ar(0, signal);
}).add;
Synth(\playBuf, [\bufnum, b]);