setup()
setup() runs once when the sketch starts. It initializes all three libraries (p5.js for input, Three.js for 3D graphics, and Tone.js for sound), creates the scene and camera, sets up synthesizers, and populates the world with the black hole and initial stars. Understanding this function teaches you how to bootstrap a multi-library creative coding project.
function setup() {
// 1. p5.js setup (for mouse input, hidden canvas)
p5Canvas = createCanvas(windowWidth, windowHeight);
noCanvas(); // Hide the p5.js canvas
// 2. three.js setup
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(75, windowWidth / windowHeight, 0.1, 2000);
camera.position.z = 500; // Initial camera position
renderer = new THREE.WebGLRenderer({ antialias: true }); // Enable antialiasing for smoother edges
renderer.setSize(windowWidth, windowHeight);
document.body.appendChild(renderer.domElement); // Add three.js canvas to the DOM
// OrbitControls for camera navigation
controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.enableDamping = true; // Give a sense of weight to the controls
controls.dampingFactor = 0.05;
// Lights
const ambientLight = new THREE.AmbientLight(0x404040); // Soft ambient light
scene.add(ambientLight);
const pointLight = new THREE.PointLight(0xffffff, 1, 1000); // Point light attached to camera
camera.add(pointLight);
scene.add(camera);
// 3. Tone.js setup
Tone.start(); // Start the Web Audio Context
masterVolume = new THREE.Volume(-10).toDestination(); // Master volume control
// Initialize instruments (PolySynths for multiple notes)
instruments.push(new Tone.PolySynth(Tone.Synth, {
oscillator: { type: 'sine' },
envelope: { attack: 0.01, decay: 0.5, sustain: 0.1, release: 1 }
}).connect(masterVolume)); // Gentle synth
instruments.push(new Tone.PolySynth(Tone.Synth, {
oscillator: { type: 'sawtooth' },
envelope: { attack: 0.01, decay: 0.8, sustain: 0.3, release: 1.5 }
}).connect(masterVolume)); // Brighter synth
instruments.push(new Tone.PolySynth(Tone.MembraneSynth, {
pitchDecay: 0.008,
octaves: 2,
envelope: { attack: 0.001, decay: 0.5, sustain: 0.01, release: 0.8 },
portamento: 0
}).connect(masterVolume)); // Percussive/bass synth
// 4. Populate initial scene
// Add a central black hole
blackHoles.push(new BlackHole(new THREE.Vector3(0, 0, 0), 100000, 30));
scene.add(blackHoles[0].mesh);
scene.add(blackHoles[0].accretionDiskMesh); // Add accretion disk
// Add some initial stars
for (let i = 0; i < 5; i++) {
const pos = new THREE.Vector3(random(-300, 300), random(-300, 300), random(-300, 300));
const star = new Star(pos, random(50, 200), random(5, 15));
stars.push(star);
scene.add(star.mesh);
scene.add(star.trailMesh); // Add star trail
}
// 5. Nebula Background (Shader Material)
const nebulaUniforms = {
time: { value: 0.0 },
color1: { value: new THREE.Color(0x1a0a33) }, // Deep purple
color2: { value: new THREE.Color(0x330a1a) }, // Deep red
color3: { value: new THREE.Color(0x0a331a) }, // Deep green
color4: { value: new THREE.Color(0x0a1a33) } // Deep blue
};
const nebulaMaterial = new THREE.ShaderMaterial({
uniforms: nebulaUniforms,
vertexShader: nebulaVertexShader,
fragmentShader: nebulaFragmentShader,
side: THREE.BackSide, // Render on the inside of the sphere
transparent: true,
opacity: 0.8
});
const nebulaGeometry = new THREE.SphereGeometry(1500, 32, 32); // Large sphere around the scene
nebulaBackgroundMesh = new THREE.Mesh(nebulaGeometry, nebulaMaterial);
scene.add(nebulaBackgroundMesh);
// 6. Start the three.js render loop
drawThreeJS();
}
Line-by-line explanation (18 lines)
🔧 Subcomponents:
p5Canvas = createCanvas(windowWidth, windowHeight);
Creates a hidden p5.js canvas for capturing mouse input without rendering visuals
scene = new THREE.Scene();
Creates the Three.js scene that will hold all 3D objects and the renderer
instruments.push(new Tone.PolySynth(Tone.Synth, {...}).connect(masterVolume));
Creates three different synthesizers with different sound characteristics to be assigned to stars
for (let i = 0; i < 5; i++) { const pos = new THREE.Vector3(...); ... }
Populates the scene with five randomly positioned stars to start the composition
const nebulaMaterial = new THREE.ShaderMaterial({...});
Creates the animated animated background nebula using GLSL shaders that procedurally generate colorful noise
p5Canvas = createCanvas(windowWidth, windowHeight);- Creates a p5.js canvas the size of the window, but the next line hides it—we only use p5.js for mouse input handling
noCanvas();- Hides the p5.js canvas so only the Three.js renderer is visible
scene = new THREE.Scene();- Creates an empty Three.js scene object that will contain all 3D objects
camera = new THREE.PerspectiveCamera(75, windowWidth / windowHeight, 0.1, 2000);- Creates a perspective camera with a 75-degree field of view and the window's aspect ratio
camera.position.z = 500;- Moves the camera 500 units away from the origin so we can see the entire scene
renderer = new THREE.WebGLRenderer({ antialias: true });- Creates a WebGL renderer that will draw the 3D scene; antialias smooths edges for prettier visuals
document.body.appendChild(renderer.domElement);- Adds the Three.js canvas to the web page's DOM so it appears in the browser
controls = new THREE.OrbitControls(camera, renderer.domElement);- Enables interactive camera controls so you can rotate, zoom, and pan the view with the mouse
controls.enableDamping = true;- Adds inertia to camera movement so it feels smooth and weighty instead of snappy
Tone.start();- Initializes the Web Audio API context, required before creating any Tone.js synthesizers
masterVolume = new THREE.Volume(-10).toDestination();- Creates a master volume control and connects it to the speakers; -10 is a moderate starting volume
instruments.push(new Tone.PolySynth(Tone.Synth, { oscillator: { type: 'sine' }, ... }).connect(masterVolume));- Creates the first instrument: a gentle sine-wave synth with smooth attack and release, perfect for melodic notes
blackHoles.push(new BlackHole(new THREE.Vector3(0, 0, 0), 100000, 30));- Creates a black hole at the origin with enormous mass (100000) and a visible size of 30 units
scene.add(blackHoles[0].mesh);- Adds the black hole's visual mesh to the Three.js scene so it will be rendered
for (let i = 0; i < 5; i++) { ... }- Loops 5 times, creating an initial constellation of random stars to demonstrate the system
const nebulaMaterial = new THREE.ShaderMaterial({ uniforms: nebulaUniforms, vertexShader: nebulaVertexShader, fragmentShader: nebulaFragmentShader, ... });- Creates a custom shader material that runs custom GLSL code on the GPU to generate an animated procedural nebula background
nebulaBackgroundMesh = new THREE.Mesh(nebulaGeometry, nebulaMaterial);- Wraps the nebula geometry and shader material into a mesh, and adds it to the scene as a backdrop
drawThreeJS();- Starts the Three.js render loop, which will continuously update and draw the 3D scene