let particles = []; let capture; let n, s, maxR; let DETAIL = 20; function setup() { createCanvas(768, 768); background(255); smooth(); capture = createCapture(VIDEO); capture.size(768, 768); capture.hide(); n = 4000; s = 12; DETAIL = s; maxR = height / 2 - height / 20; particles = []; initParticles(); } function draw() { translate(width / 2, height / 2); noStroke(); capture.loadPixels(); if (particles.length != 0) { for (let i = 0; i < particles.length; i++) { let p = particles[i]; p.show(); p.move(); if (p.isOutOfBounds()) { p.reattach(); } } } } function initParticles() { for (let i = 0; i < n; i++) { particles.push(new Particle(s)); } } function keyPressed() { if (key === 's' || key === 'S') { // Check if the 's' key is pressed DETAIL--; for (let i = 0; i < particles.length; i++) { particles[i].shrink(); } } } class Particle { constructor(s_) { this.s = s_; this.bri = 0; this.init(); this.mainsize = random(0.5, 1); } init() { this.pos = createVector(random(-width / 2, width / 2), random(-height / 2, height / 2)); this.vel = createVector(random(-1, 1), random(-1, 1)); // Random initial motion vector this.c = color(255); this.targetColor = color(255); // Initialize target color this.offset = random(-30, 30); // Random brightness or color tone offset this.lifetime = int(random(60, 100)); // Random lifetime in frames (1 to 2 seconds at 60 fps) this.maxLifetime = this.lifetime; // Store the maximum lifetime } show() { // Reduce particle size as its lifetime decreases let sizeFactor = map(this.lifetime, 0, this.maxLifetime, 1, 0.5); let displaySize = (this.s * sizeFactor + this.bri * 0.01) * this.mainsize; // Sample color from the capture pixels directly under the particle let x = int(map(this.pos.x, -width / 2, width / 2, 0, capture.width)); let y = int(map(this.pos.y, -height / 2, height / 2, 0, capture.height)); let index = (x + y * capture.width) * 4; let r = capture.pixels[index]; let g = capture.pixels[index + 1]; let b = capture.pixels[index + 2]; let a = 255; // Set alpha to 255 // Draw the particle using the sampled color with the offset applied fill(r + this.offset, g + this.offset, b + this.offset ); circle(this.pos.x, this.pos.y, displaySize * 2); } updateColor() { // Only update the color if the particle hasn't reached its maximum lifetime yet if (this.lifetime > 0) { let x = int(map(this.pos.x, -width / 2, width / 2, 0, capture.width)); let y = int(map(this.pos.y, -height / 2, height / 2, 0, capture.height)); let index = (x + y * capture.width) * 4; let r = capture.pixels[index]; let g = capture.pixels[index + 1]; let b = capture.pixels[index + 2]; let targetColor = color(r + this.offset, g + this.offset, b + this.offset); // Calculate target color // Smoothly transition to the target color using linear interpolation (lerp) this.c = lerpColor(this.c, targetColor, 0.001); // Try increasing the interpolation amount } } move() { let x = int(map(this.pos.x, -width / 2, width / 2, 0, capture.width)); let y = int(map(this.pos.y, -height / 2, height / 2, 0, capture.height)); let index = (x + y * capture.width) * 4; let r = capture.pixels[index]; let g = capture.pixels[index + 1]; let b = capture.pixels[index + 2]; let brightnessValue = (r + g + b) / 3; this.bri = brightnessValue; let mousey = createVector(0, 0); if (mouseIsPressed) { // Calculate normalized direction from particle to mouse position let dx = mouseX - x; let dy = mouseY - y; mousey = createVector(dx, dy).normalize().mult(2); // Normalize the vector and scale it } let angle = map(brightnessValue, 0, 255, 0, PI); // Introduce slight randomness let noiseAngle = map(noise(this.pos.x * 0.004, this.pos.y * 0.004, millis() * 0.001), 0, 1, -TWO_PI, PI ); let noiseVel = p5.Vector.fromAngle(noiseAngle); // Random motion vector based on Perlin noise this.vel.add(noiseVel); this.vel.setMag(DETAIL/2); // Set magnitude to control speed this.vel.rotate(angle * 0.15); this.vel.add(mousey); // Add mousey vector directly to velocity this.pos.add(this.vel); this.lifetime--; // Decrease lifetime } shrink() { this.s -= 1; // Shrink dot size by 1 unit } isOutOfBounds() { return (this.pos.x < -width / 2 || this.pos.x > width / 2 || this.pos.y < -height / 2 || this.pos.y > height / 2 || this.lifetime <= 0); } reattach() { // Store the current color before resetting the particle let previousColor = this.c; this.init(); // Reset particle properties // Restore the previous color this.c = previousColor; } }