Click to start sounds and then slowly mouse around to discover harmonies and disharmonies.
A fork of the glass bead game: a sound toy by Aaron Reuland (a_ soluble_fish)
xxxxxxxxxx
let colors = ["#e1d89f", "#cd8b76", "#c45baa", "#CA9DCA", "#86A4AA"];
let size = 100;
let rs;
let blendModes;
let currentBlendMode;
let mousePressedBlendMode;
let zoomFactors = []; // Array to store zoom factors for each ellipse
let zoomSpeeds = []; // Array to store zoom speeds for each ellipse
let numRows, numCols;
let maskShape;
function setup() {
createCanvas(900, 900);
fill(0);
maskShape = new MaskShape();
pixelDensity(2); // delay effect
noStroke();
rs = random(10); // Random color
// List of available blend modes
blendModes = [
BLEND,
SCREEN,
BURN,
OVERLAY,
HARD_LIGHT,
];
// Set initial blend modes
currentBlendMode = random(blendModes);
mousePressedBlendMode = random(blendModes);
// Ensure that the mousePressedBlendMode is different
while (mousePressedBlendMode === currentBlendMode) {
mousePressedBlendMode = random(blendModes);
}
// Calculate number of rows and columns
numRows = Math.ceil(height / size);
numCols = Math.ceil(width / size);
// Initialize zoom factors and speeds for each ellipse
for (let i = 0; i < numRows * numCols; i++) {
zoomFactors[i] = random(1.0, 1.4); // Initial zoom factor
zoomSpeeds[i] = (0.005); // Random zoom speed for each ellipse
}
}
function draw() {
background(25); //
fill(0,0,0,20);
addClipMask(maskShape); // Apply the clipping mask
// Apply the blend mode
if (mouseIsPressed) {
blendMode(mousePressedBlendMode);
} else {
blendMode(currentBlendMode);
}
randomSeed(rs); // Ensure randomness is consistent
// Update zoom factors
zoomEllipses();
// Draw ellipses with zoom factors
drawEllipses();
removeClipMask(); // Remove the clipping mask
// Draw the instruction text
fill(0); // Text color
textSize(24); // Text size
textAlign(CENTER, CENTER); // Center alignment
text("Click and hold for effect", width / 2, height - 30); // Text position
}
function addClipMask(shape) {
drawingContext.save(); // Save the current canvas state
beginShape();
drawMaskShape(shape);
endShape(CLOSE);
drawingContext.clip(); // Apply the clipping mask
}
function drawMaskShape(shape) {
// Draw the mask shape
beginShape();
for (let i = 0; i < shape.points.length; i++) {
vertex(shape.points[i].x, shape.points[i].y);
}
endShape(CLOSE);
}
function removeClipMask() {
drawingContext.restore(); // Restore the canvas state
}
function drawEllipses() {
for (let i = 0; i < numRows; i++) {
for (let j = 0; j < numCols; j++) {
let x = size / 2 + j * size;
let y = size / 2 + i * size;
let gradient = drawingContext.createRadialGradient(x, y, 0, mouseX, mouseY, size + dist(mouseX, mouseY, x, y));
gradient.addColorStop(0, random(colors));
gradient.addColorStop(0.25, random(colors));
gradient.addColorStop(0.5, random(colors));
gradient.addColorStop(1, random(colors));
drawingContext.fillStyle = gradient;
let index = i * numCols + j; // Calculate index for zoom factors
ellipse(x, y, size * zoomFactors[index]);
}
}
}
// Function to update zoom factors
function zoomEllipses() {
for (let i = 0; i < zoomFactors.length; i++) {
zoomFactors[i] += zoomSpeeds[i]; // Update zoom factor
if (zoomFactors[i] > 1.4 || zoomFactors[i] < 1.0) {
zoomSpeeds[i] *= -1; // Reverse zoom direction if limits are reached
}
}
}
class MaskShape {
constructor() {
this.points = [
createVector(10, 10),
createVector(90, 10),
createVector(900, 700),
createVector(10, 900)
];
}
drawMask() {
beginShape();
for (let i = 0; i < this.points.length; i++) {
vertex(this.points[i].x, this.points[i].y);
}
endShape(CLOSE);
}
}