setup()
setup() runs once when the sketch starts. It initializes the canvas, creates all visual objects (nodes), sets up all audio synthesizers and effects chains, and starts playback of the ambient drone and rhythm pattern. Understanding how Tone.js synthesizers are created and chained together here is key to making your own soundscapes.
function setup() {
console.log("Setup start"); // DEBUG LOG
createCanvas(windowWidth, windowHeight);
// Create nodes with random positions
for (let i = 0; i < numNodes; i++) {
nodes.push(createVector(random(width), random(height)));
}
// Set initial insight
currentInsight = random(insights);
insightDisplayTime = frameCount;
// Tone.js setup - Ensure Tone.start() is called on user interaction
// It's already in mousePressed, which is good.
// Main ambient drone representing continuous processing
ambientDuoSynth = new Tone.DuoSynth({
voice0: {
oscillator: { type: "sine" },
envelope: { attack: 2, decay: 4, sustain: 0.5, release: 8 },
volume: -15
},
voice1: {
oscillator: { type: "sine", detune: 5 }, // Subtle detune for richness
envelope: { attack: 2, decay: 4, sustain: 0.5, release: 8 },
volume: -15
},
vibratoAmount: 0.5,
vibratoRate: 0.1,
volume: -10 // Overall volume
});
filter = new Tone.Filter(200, "lowpass").toDestination();
let reverb = new Tone.Reverb(4).toDestination();
let delay = new Tone.FeedbackDelay("8n", 0.5).toDestination();
ambientDuoSynth.chain(filter, reverb, delay, Tone.Destination);
// Play a sustained note (C3) indefinitely
ambientDuoSynth.triggerAttack("C3");
// Setup click synth for subtle connection pulses (data packets)
clickSynth = new Tone.MembraneSynth({
envelope: { attack: 0.005, decay: 0.1, sustain: 0, release: 0.1 },
pitchDecay: 0.05
}).toDestination();
clickSynth.volume.value = -20; // Make clicks very subtle
// Setup rhythmic synth for the "puzzles" aspect - low frequency pulse
rhythmSynth = new Tone.MembraneSynth({
envelope: { attack: 0.01, decay: 0.2, sustain: 0, release: 0.3 },
pitchDecay: 0.01
}).toDestination();
rhythmSynth.volume.value = -22; // Slightly increase volume to make it more noticeable
// Setup synth for interactive query pulse
querySynth = new Tone.PluckSynth({
attackNoise: 1,
envelope: { attack: 0.005, decay: 0.4, sustain: 0, release: 0.5 },
dampening: 4000
}).toDestination();
querySynth.volume.value = -15; // Make query sound audible
// Setup sparkle synth for insight emergence
sparkleSynth = new Tone.NoiseSynth({
noise: { type: "pink" },
envelope: { attack: 0.001, decay: 0.1, sustain: 0, release: 0.2 },
volume: -20
}).toDestination();
// Create a Tone.Sequence for the Euclidean rhythm
let rhythmNotes = euclideanPattern.map(hit => hit ? "C1" : null);
let rhythmSequence = new Tone.Sequence((time, note) => {
if (note) {
rhythmSynth.triggerAttackRelease(note, "8n", time);
}
// Update rhythm step for visual indicator
currentRhythmStep = (currentRhythmStep + 1) % euclideanPattern.length;
}, rhythmNotes, "8n"); // Schedule each step as an 8th note
rhythmSequence.loop = true;
rhythmSequence.start(0); // Start the sequence at the beginning of the transport
console.log("Setup complete"); // DEBUG LOG
}
Line-by-line explanation (5 lines)
🔧 Subcomponents:
for (let i = 0; i < numNodes; i++) {
nodes.push(createVector(random(width), random(height)));
}
Populates the nodes array with 100 p5.Vector objects at random positions across the canvas
ambientDuoSynth = new Tone.DuoSynth({...});
Creates a dual-voice sine wave synthesizer with slow attack/decay envelopes and subtle detuning for a rich, evolving ambient sound
let rhythmSequence = new Tone.Sequence((time, note) => {...}, rhythmNotes, "8n");
Creates a looping rhythmic pattern that triggers the rhythm synth at Euclidean intervals and tracks the current beat step
createCanvas(windowWidth, windowHeight);- Creates a canvas that fills the entire browser window, making the visualization fullscreen and responsive
for (let i = 0; i < numNodes; i++) { nodes.push(createVector(random(width), random(height))); }- Loops 100 times, creating a new p5.Vector (representing x,y position) with random coordinates and adding it to the nodes array
currentInsight = random(insights);- Picks one random philosophical quote from the insights array to display when the sketch starts
ambientDuoSynth.triggerAttack("C3");- Starts the ambient synthesizer playing a low C note indefinitely—this is the foundational drone you hear throughout
rhythmSequence.start(0);- Starts the Euclidean rhythm pattern playing from the beginning of Tone.js's transport (internal clock)