function draw() {
// Clear the background to black
background(0);
// Add lighting for a 3D effect in WebGL mode
// Point light from the top-left
pointLight(255, 255, 255, -windowWidth/4, -windowHeight/4, 200);
// Point light from the bottom-right with a slight blue tint
pointLight(200, 200, 255, windowWidth/4, windowHeight/4, 200);
// Directional light from the front
directionalLight(255, 255, 255, 0, 0, -1);
// Analyze the audio spectrum only after audio has started
if (audioStarted) {
fft.analyze();
// Get energy levels for different frequency bands
let bassEnergy = fft.getEnergy("bass"); // Low frequencies (0-140 Hz)
let midEnergy = fft.getEnergy("mid"); // Mid frequencies (140-6000 Hz)
let trebleEnergy = fft.getEnergy("treble"); // High frequencies (6000-20000 Hz)
let overallEnergy = fft.getEnergy("overall"); // Total energy across the spectrum
let lowMidEnergy = fft.getEnergy("lowMid"); // Low-mid frequencies
let highMidEnergy = fft.getEnergy("highMid"); // High-mid frequencies
// --- Central Reactive Shape (Torus) ---
push();
// Rotate the central shape based on time for continuous movement
rotateX(millis() * 0.0005);
rotateY(millis() * 0.001);
// Map overall energy to size for the torus
let torusSize = map(overallEnergy, 0, 255, 100, 300); // Overall size reactive to sound
let torusThickness = map(midEnergy, 0, 255, 20, 80); // Thickness reactive to mid frequencies
// Set material properties with HSB colors reactive to frequency bands
// Hue: Rotate through the spectrum based on bass and highMid energies
let torusHue = map(bassEnergy + highMidEnergy, 0, 510, 0, 360);
// Saturation: More vibrant when mid and treble energies are high
let torusSaturation = map(midEnergy + trebleEnergy, 0, 510, 50, 100);
// Brightness: Brighter when overall energy is high
let torusBrightness = map(overallEnergy, 0, 255, 50, 100);
specularMaterial(torusHue, torusSaturation, torusBrightness);
shininess(map(trebleEnergy, 0, 255, 1, 50)); // Shine reactive to treble
// Draw the torus
torus(torusSize * 0.7, torusThickness, 24, 16);
pop();
// --- Peripheral Reactive Shapes (Boxes) ---
let numShapes = 16; // Number of shapes arranged in a circle
let radius = min(width, height) * 0.4; // Radius for the circle of shapes
for (let i = 0; i < numShapes; i++) {
push();
let angle = map(i, 0, numShapes, 0, TWO_PI); // Calculate angle for each shape
// Position the shape in a circle around the center
translate(radius * cos(angle), radius * sin(angle), 0);
// Rotate the shape
rotateZ(angle);
rotateX(bassEnergy * 0.01); // Bass energy makes them rotate on X-axis
rotateY(midEnergy * 0.01); // Mid energy makes them rotate on Y-axis
// Get energy for a specific frequency band for this peripheral shape
// We're using a frequency band based on 'i' to get different reactions
// fft.getEnergy() can also take a frequency value directly (e.g., 20, 40, 60...)
let freqBandEnergy = fft.getEnergy(i * 20 + 20); // Each shape reacts to a different part of the spectrum
let shapeSize = map(freqBandEnergy, 0, 255, 20, 150); // Size reactive to its frequency band
// Set material for peripheral shapes with HSB colors
// Hue: Maps to the individual frequency band, creating a spectrum effect
let shapeHue = map(freqBandEnergy, 0, 255, 0, 360);
// Saturation: More saturated when overall energy is high
let shapeSaturation = map(overallEnergy, 0, 255, 60, 100);
// Brightness: Brighter when the specific band energy is high
let shapeBrightness = map(freqBandEnergy, 0, 255, 50, 100);
specularMaterial(shapeHue, shapeSaturation, shapeBrightness);
shininess(map(lowMidEnergy, 0, 255, 1, 30)); // Shine reactive to lowMid
// Draw a box for each peripheral shape
box(shapeSize, shapeSize, shapeSize / 2);
pop();
}
} else {
// Display an instruction for mobile users until audio is activated
push();
noLights(); // Disable lights for the text
fill(255);
textSize(20);
textAlign(CENTER, CENTER);
text("Touch screen to start audio input", 0, 0, 0); // Display instruction
pop();
}
}