Nova Noise Composer
//)()()()()()()()()()()()()()()()()()()()()()()(
////////////////////////////////////////////////
///////////composer version V1/2025/////////////
////////////////////////////////////////////////
//Created by Mikael Madsen // MikaelMadsen.com//
////////////////////////////////////////////////
//)()()()()()()()()()()()()()()()()()()()()()()(
//
//
//
//
//
//
//)()()()()()()()()()()()()()()()()()()()()()()(
let squares = []; // Array to store square objects
let canvasSize = 800; // Canvas size for the sketch
let numSquares = 12; // Number of basic squares to create
let soundOn = true; // Flag to control if sounds should be played
let isRunning = true; // Flag to control the execution of the sketch
let phase = “intro”; // Initial phase of the composer
let startTime; // Start time for the composer
let audioCtx; // Audio context for managing sounds
let totalDuration = 600000; // Total duration of the composition (10 minutes)
let collisions = []; // Array to store collision effects
let draggedSquare = null; // Store the currently dragged square
let playedPairs = new Set(); // Set to track played pairs of squares that collided
function setup() {
// Set up the canvas, audio context, and initial conditions
createCanvas(canvasSize, canvasSize + 30);
userStartAudio(); // Ensure the audio context starts
audioCtx = getAudioContext(); // Initialize the audio context
startTime = millis(); // Capture the start time for timing calculations
// Create basic squares
for (let i = 0; i < numSquares; i++) {
squares.push(new Square(i)); // Add a square to the array
}
// Add machine squares with different properties
for (let i = 0; i < 3; i++) {
squares.push(new MachineSquare(i + numSquares)); // Add machine squares
}
// Add 3 AudioSquares with colors and .wav links
squares.push(new AudioSquare(numSquares + 3, color(0, 0, 255), "https://example.com/blue.wav"));
squares.push(new AudioSquare(numSquares + 4, color(255, 255, 0), "https://example.com/yellow.wav"));
squares.push(new AudioSquare(numSquares + 5, color(0, 200, 0), "https://example.com/green.wav"));
}
function draw() {
if (!isRunning) return; // If the sketch is not running, skip the draw cycle
let elapsed = millis() - startTime; // Calculate the elapsed time
if (elapsed > totalDuration) noLoop(); // Stop the sketch after the total duration
// Phase transition based on elapsed time
if (elapsed < 120000) phase = "intro";
else if (elapsed < 300000) phase = "build";
else if (elapsed < 480000) phase = "chaos";
else if (elapsed < 590000) phase = "fadeout";
else phase = "end";
// Render the background and strokes based on the phase
background(0);
stroke(phase === "chaos" ? 255 : 150);
strokeWeight(5);
noFill();
rect(0, 0, canvasSize, canvasSize);
// Update and display each square
for (let sq of squares) {
if (!sq.beingDragged) sq.update(); // Update square's position
sq.display(); // Display the square
}
playedPairs.clear(); // Clear the set of played pairs for collision detection
// Collision detection and sound triggering
for (let i = 0; i < squares.length; i++) {
for (let j = i + 1; j < squares.length; j++) {
if (squares[i].collidesWith(squares[j])) { // Check for collision between squares
let pairKey = `${i}-${j}`;
if (!playedPairs.has(pairKey)) { // If the pair hasn't collided before
if (soundOn) {
squares[i].playHarshNoise(); // Play noise for both squares
squares[j].playHarshNoise();
}
collisions.push(new CollisionEffect(
(squares[i].x + squares[j].x) / 2,
(squares[i].y + squares[j].y) / 2
));
playedPairs.add(pairKey); // Add this pair to the set of played pairs
}
}
}
}
// Update and display collision effects
for (let i = collisions.length - 1; i >= 0; i–) {
collisions[i].update();
collisions[i].display();
if (collisions[i].finished) collisions.splice(i, 1); // Remove finished collisions
}
drawTimeline(elapsed); // Draw the timeline of the composition
}
// MOUSE CONTROL BEGINS
function mousePressed() {
// Check if a square is clicked and start dragging it
for (let sq of squares) {
if (sq.contains(mouseX, mouseY)) { // If the mouse is over a square
draggedSquare = sq;
sq.beingDragged = true;
break;
}
}
}
function mouseDragged() {
// Drag the square with the mouse
if (draggedSquare) {
draggedSquare.x = constrain(mouseX – draggedSquare.size / 2, 0, canvasSize – draggedSquare.size);
draggedSquare.y = constrain(mouseY – draggedSquare.size / 2, 0, canvasSize – draggedSquare.size);
}
}
function mouseReleased() {
// Stop dragging the square when the mouse is released
if (draggedSquare) {
draggedSquare.beingDragged = false;
draggedSquare = null;
}
}
// MOUSE CONTROL ENDS
// Function to draw the timeline of the composition
function drawTimeline(elapsed) {
let progress = map(elapsed, 0, totalDuration, 0, canvasSize); // Map the elapsed time to the canvas size
noStroke();
fill(0);
rect(0, canvasSize, progress, 30); // Draw the timeline progress bar
noFill();
stroke(100);
rect(0, canvasSize, canvasSize, 30); // Draw the timeline background
}
// Collision effect class to visualize collisions between squares
class CollisionEffect {
constructor(x, y) {
this.x = x; // X position of the collision
this.y = y; // Y position of the collision
this.life = 15; // Lifespan of the collision effect
this.finished = false; // Flag to mark when the effect is finished
}
update() {
this.life–; // Decrease the life of the collision effect
if (this.life <= 0) this.finished = true; // Mark the effect as finished when life reaches zero
}
display() {
noFill();
stroke(255, 0, 0, map(this.life, 0, 15, 0, 255)); // Red color with fading effect
strokeWeight(2);
ellipse(this.x, this.y, 20 + (15 - this.life) * 2); // Draw the collision effect as a growing circle
}
}
// Class for basic squares that move around and bounce off edges
class Square {
constructor(id) {
this.id = id; // Unique ID for the square
this.size = 50; // Size of the square
this.x = random(canvasSize - this.size); // Random X position
this.y = random(canvasSize - this.size); // Random Y position
this.vx = random(-1.5, 1.5); // Random X velocity
this.vy = random(-1.5, 1.5); // Random Y velocity
this.color = color(random(255), random(255), random(255)); // Random color for the square
this.noiseType = random(["white", "pink", "brown"]); // Random noise type
this.beingDragged = false; // Flag to indicate if the square is being dragged
}
// Update the square's position based on its velocity
update() {
this.x += this.vx;
this.y += this.vy;
// Bounce off edges of the canvas
if (this.x <= 0 || this.x + this.size >= canvasSize) this.vx *= -1;
if (this.y <= 0 || this.y + this.size >= canvasSize) this.vy *= -1;
}
// Display the square
display() {
fill(this.color); // Fill with the square’s color
noStroke();
rect(this.x, this.y, this.size, this.size); // Draw the square
}
// Check if a point is inside the square
contains(mx, my) {
return mx >= this.x && mx <= this.x + this.size &&
my >= this.y && my <= this.y + this.size;
}
// Check if this square collides with another square
collidesWith(other) {
return !(this.x + this.size < other.x || this.x > other.x + other.size ||
this.y + this.size < other.y || this.y > other.y + other.size);
}
// Play noise when the square collides
playHarshNoise() {
let dur = 0.1; // Duration of the noise
let gainNode = audioCtx.createGain(); // Create a gain node to control volume
gainNode.gain.value = 0.25; // Set volume
gainNode.connect(audioCtx.destination); // Connect to the audio context output
let bufferSize = 2 * audioCtx.sampleRate * dur; // Size of the noise buffer
let noiseBuffer = audioCtx.createBuffer(1, bufferSize, audioCtx.sampleRate); // Create the noise buffer
let output = noiseBuffer.getChannelData(0);
// Generate noise based on the noise type
for (let i = 0; i < bufferSize; i++) {
if (this.noiseType === "white") output[i] = Math.random() * 2 - 1; // White noise
else if (this.noiseType === "pink") output[i] = (output[i - 1] || 0) * 0.9 + (Math.random() * 2 - 1) * 0.1; // Pink noise
else if (this.noiseType === "brown") output[i] = (output[i - 1] || 0) + (Math.random() * 2 - 1) * 0.02; // Brown noise
}
// Play the noise
let noise = audioCtx.createBufferSource();
noise.buffer = noiseBuffer;
noise.connect(gainNode);
noise.start();
}
}
// Class for machine squares with specific behavior
class MachineSquare extends Square {
constructor(id) {
super(id); // Call the constructor of the parent class
this.color = color(255, 0, 0); // Set the color of the machine square to red
this.noiseType = "machine"; // Set noise type to "machine"
}
// Display the machine square with a border
display() {
fill(this.color);
stroke(0);
strokeWeight(2);
rect(this.x, this.y, this.size, this.size);
}
// Play harsh noise using an oscillator
playHarshNoise() {
let osc = audioCtx.createOscillator(); // Create an oscillator
osc.type = "sawtooth"; // Set oscillator type to sawtooth wave
osc.frequency.setValueAtTime(random(40, 70), audioCtx.currentTime); // Set a random frequency
let gainNode = audioCtx.createGain(); // Create a gain node
gainNode.gain.setValueAtTime(0.65, audioCtx.currentTime); // Set the gain (volume)
osc.connect(gainNode); // Connect oscillator to gain node
gainNode.connect(audioCtx.destination); // Connect to the output
osc.start(); // Start the oscillator
osc.stop(audioCtx.currentTime + 0.2); // Stop after 0.2 seconds
}
}
// Class for audio squares with custom sound and colors
class AudioSquare extends Square {
constructor(id, col, url) {
super(id); // Call the constructor of the parent class
this.color = col; // Set the color of the audio square
this.audioURL = url; // Set the audio URL for the square
this.soundBuffer = null; // Initialize the sound buffer as null
this.loadSound(); // Load the sound file
}
// Load the sound file asynchronously
loadSound() {
if (this.audioURL) {
fetch(this.audioURL) // Fetch the audio file
.then(res => res.arrayBuffer()) // Get the audio as an array buffer
.then(buf => audioCtx.decodeAudioData(buf)) // Decode the audio data
.then(decoded => this.soundBuffer = decoded) // Store the decoded audio
.catch(err => console.error(“Audio load error:”, err)); // Handle errors
}
}
// Play the sound when the square is triggered
playHarshNoise() {
if (this.soundBuffer) {
let source = audioCtx.createBufferSource(); // Create a buffer source
source.buffer = this.soundBuffer; // Set the sound buffer
source.connect(audioCtx.destination); // Connect to the audio context output
source.start(); // Play the sound
}
}
}
//)()()()()()()()()()()()()()()()()()()()()()()(
//)()()()()()()()()()()()()()()()()()()()()()()(
//)()()()()()()()()()()()()()()()()()()()()()()(
//)()()()()()()()()()()()()()()()()()()()()()()(
//)()()()()()()()()()()()()()()()()()()()()()()(
//)()()()()()()()()()()()()()()()()()()()()()()(
//)()()()()()()()()()()()()()()()()()()()()()()(
//)()()()()()()()()()()()()()()()()()()()()()()(
//)()()()()()()()()()()()()()()()()()()()()()()(
//)()()()()()()()()()()()()()()()()()()()()()()(