xxxxxxxxxx
321
let particles = [];
let step = 1; // start with step 1
let threshold = 600; // max number of particles
let particleSlider; // slider for adjusting particle count
let rotationAngle = 0; // angle for rotating concentric shapes
let rotationSpeed = 0.01; // speed of rotation
let noiseOffset = 0; // offset for noise movement
class Particle {
constructor(r, age) {
this.pos = createVector(random(width), random(height));
this.initialPos = this.pos.copy(); // store the initial position
this.r = r || random(2) + 0.5; // smaller particle size
this.noiseSeedX = random(1000); // unique seed for noise-based movement
this.noiseSeedY = random(1000);
this.lifetime = 255 * age; // lifetime starts fully opaque
}
update() {
if (step === 1) {
// normal random movement in step 1
this.pos.x += map(noise(this.noiseSeedX), 0, 1, -1, 1);
this.pos.y += map(noise(this.noiseSeedY), 0, 1, -1, 1);
this.noiseSeedX += 0.01;
this.noiseSeedY += 0.01;
} else if (step === 2 || step === 5) {
// slight noise-based movement to keep particles in place
let noiseMoveX = map(noise(this.noiseSeedX + noiseOffset), 0, 1, -2, 2);
let noiseMoveY = map(noise(this.noiseSeedY + noiseOffset), 0, 1, -2, 2);
this.pos.x += noiseMoveX;
this.pos.y += noiseMoveY;
}
this.lifetime -= 2; // lifetime reduces over time
}
checkEdges() {
if (this.pos.x <= -this.r) this.pos.x = width;
else if (this.pos.x >= width + this.r) this.pos.x = -this.r;
if (this.pos.y <= -this.r) this.pos.y = height;
else if (this.pos.y >= height + this.r) this.pos.y = -this.r;
}
display() {
let hueValue = map(this.lifetime, 0, 255, 0, 360); // hue changes over time
// add particle trails and glow effect
stroke(hueValue, 100, 100, this.lifetime / 2);
strokeWeight(0.5); // thinner trails
point(this.pos.x, this.pos.y);
fill(hueValue, 100, 100, this.lifetime);
noStroke();
ellipse(this.pos.x, this.pos.y, this.r * 2); // smaller circle
}
isDead() {
return this.lifetime <= 0; // check if particle's lifetime ended
}
explode() {
// only spawn new particles if below the threshold
if (particles.length < threshold) {
let newParticles = floor(random(2, 5)); // spawn 2 to 5 new particles
for (let i = 0; i < newParticles; i++) {
particles.push(new Particle(random(2) + 0.5, random(0.5, 1))); // smaller new particles
}
}
}
// smoothly move particles to target positions
moveTo(targetX, targetY) {
let target = createVector(targetX, targetY);
let dir = p5.Vector.sub(target, this.pos);
dir.mult(0.05); // adjust speed of movement
this.pos.add(dir);
}
}
// create particles
function createParticles(amount, radius) {
particles = [];
for (let i = 0; i < amount; i++) {
particles.push(new Particle(radius, i / 4));
}
}
// basic movement with lifetime management (step 1)
function step1() {
updateParticles();
}
// connecting particles with lines and slight movement (step 2)
function step2() {
updateParticles();
connectParticles(100);
noiseOffset += 0.01; // add noise offset to create subtle movement
}
// arrange particles in concentric circles (step 3)
function step3() {
arrangeInConcentricCircles(rotationAngle);
displayParticles();
rotationAngle += rotationSpeed; // increase angle to rotate the circles
}
// arrange particles in concentric triangles (step 4)
function step4() {
arrangeInConcentricTriangles(rotationAngle);
displayParticles();
rotationAngle += rotationSpeed; // increase angle to rotate the triangles
}
// connect circles in step 5
function step5() {
arrangeInConcentricCircles(rotationAngle); // Arrange the particles in concentric circles first
connectCircleLayers(); // Connect particles across layers
noiseOffset += 0.01; // slight movement with noise
}
// update and display particles (common for steps 1, 2, 3, 4, and 5)
function updateParticles() {
for (let i = particles.length - 1; i >= 0; i--) {
let p = particles[i];
p.update();
p.checkEdges();
p.display();
if (p.isDead()) {
p.explode(); // trigger explosion before removal
particles.splice(i, 1); // remove particle from array
}
}
}
// display particles without updating (for static arrangements)
function displayParticles() {
particles.forEach(p => {
p.display();
});
}
// connect particles within a certain distance
function connectParticles(maxDistance) {
for (let i = 0; i < particles.length; i++) {
for (let j = i + 1; j < particles.length; j++) {
let p1 = particles[i];
let p2 = particles[j];
let distBetween = p1.pos.dist(p2.pos);
if (distBetween < maxDistance) {
stroke(255, 255, 255, map(distBetween, 0, maxDistance, 255, 0));
strokeWeight(5);
line(p1.pos.x, p1.pos.y, p2.pos.x, p2.pos.y);
}
}
}
}
// connect particles across concentric circle layers to create a web-like mesh
function connectCircleLayers() {
let layerCount = 5; // Number of layers
let particlesPerLayer = floor(particles.length / layerCount);
for (let i = 0; i < particles.length; i++) {
let p1 = particles[i];
for (let j = i + particlesPerLayer; j < particles.length; j += particlesPerLayer) {
let p2 = particles[j % particles.length]; // wrap around if needed
stroke(255, 255, 255, 150); // lighter connections
strokeWeight(0.5);
line(p1.pos.x, p1.pos.y, p2.pos.x, p2.pos.y);
}
}
}
// arrange particles in concentric circles
function arrangeInConcentricCircles(angle) {
let centerX = width / 2;
let centerY = height / 2;
let layers = 5; // number of concentric circles
let particlesPerLayer = floor(particles.length / layers);
let index = 0;
for (let i = 1; i <= layers; i++) {
let radius = i * 50; // distance between circles
let angleStep = TWO_PI / particlesPerLayer;
for (let j = 0; j < particlesPerLayer && index < particles.length; j++) {
let theta = j * angleStep + angle; // add rotation angle
let x = centerX + cos(theta) * radius;
let y = centerY + sin(theta) * radius;
particles[index].moveTo(x, y);
index++;
}
}
}
// arrange particles in concentric triangles
function arrangeInConcentricTriangles(angle) {
let centerX = width / 2;
let centerY = height / 2;
let layers = 5; // number of concentric triangles
let particlesPerLayer = floor(particles.length / layers);
let index = 0;
for (let i = 1; i <= layers; i++) {
let size = i * 100; // adjust size between triangles
let vertices = [
createVector(centerX, centerY - size / 2),
createVector(centerX - size / 2, centerY + size / 2),
createVector(centerX + size / 2, centerY + size / 2)
];
for (let j = 0; j < particlesPerLayer && index < particles.length; j++) {
let t = j / particlesPerLayer;
let pos = getPointOnTriangle(vertices, t);
let rotatedPos = rotatePoint(pos, centerX, centerY, angle); // rotate position
particles[index].moveTo(rotatedPos.x, rotatedPos.y);
index++;
}
}
}
// helper to rotate a point around a center
function rotatePoint(pos, cx, cy, angle) {
let x = pos.x - cx;
let y = pos.y - cy;
let newX = x * cos(angle) - y * sin(angle);
let newY = x * sin(angle) + y * cos(angle);
return createVector(newX + cx, newY + cy);
}
// helper to get a point on the triangle perimeter
function getPointOnTriangle(vertices, t) {
let totalPerimeter = 0;
let edges = [];
// calculate edges and their lengths
for (let i = 0; i < 3; i++) {
let start = vertices[i];
let end = vertices[(i + 1) % 3];
let edge = p5.Vector.sub(end, start);
let length = edge.mag();
edges.push({ start, edge, length });
totalPerimeter += length;
}
let distance = t * totalPerimeter;
for (let i = 0; i < edges.length; i++) {
if (distance < edges[i].length) {
let point = p5.Vector.add(edges[i].start, edges[i].edge.setMag(distance));
return point;
}
distance -= edges[i].length;
}
return vertices[0]; // fallback
}
// dynamically adjust particle count based on frame rate
function adjustThreshold() {
let currentParticles = particleSlider.value();
if (frameRate() < 30 && threshold > 50) {
threshold = max(threshold - 10, 50);
} else if (frameRate() > 45 && threshold < 500) {
threshold = min(threshold + 10, currentParticles);
}
}
function setup() {
createCanvas(windowWidth, windowHeight);
colorMode(HSB, 360, 100, 100, 255); // use HSB color mode for smooth transitions
createParticles(100, 10); // create initial particles
// create slider for particle count
//particleSlider = createSlider(50, 500, threshold);
//particleSlider.position(20, height - 40);
}
function draw() {
background(0);
adjustThreshold(); // adjust particle count dynamically
if (step === 1) {
step1();
} else if (step === 2) {
step2();
} else if (step === 3) {
step3();
} else if (step === 4) {
step4();
} else if (step === 5) {
step5();
}
// display particle count and frame rate
//fill(255);
//textSize(16);
//text(`Particles: ${particles.length}`, 10, 20);
//text(`Frame Rate: ${floor(frameRate())}`, 10, 40);
//text(`Threshold: ${threshold}`, 10, 60);
//text(`Step: ${step}`, 10, 80);
}
// switch between steps using keys
function keyPressed() {
if (key === '1') {
step = 1;
} else if (key === '2') {
step = 2;
} else if (key === '3') {
step = 3;
} else if (key === '4') {
step = 4;
} else if (key === '5') {
step = 5;
}
}