114 lines, no comments ยท 3,244 tokens, written in 18 seconds for $0.012


  1  let particles = [];
  2  const NUM_PARTICLES = 750;
  3  let attractors = [];
  4  
  5  function setup() {
  6    createCanvas(windowWidth, windowHeight);
  7    colorMode(HSB, 360, 100, 100, 100);
  8    background(230, 40, 8);
  9  
 10    for (let i = 0; i < 4; i++) {
 11      attractors.push({
 12        angle: (TWO_PI / 4) * i,
 13        speed: random(0.008, 0.018),
 14        radiusX: random(120, 280),
 15        radiusY: random(80, 220),
 16        x: 0,
 17        y: 0
 18      });
 19    }
 20  
 21    for (let i = 0; i < NUM_PARTICLES; i++) {
 22      particles.push(new Particle());
 23    }
 24  }
 25  
 26  function draw() {
 27    background(230, 40, 8, 14);
 28  
 29    let t = frameCount;
 30    for (let i = 0; i < attractors.length; i++) {
 31      let a = attractors[i];
 32      let currentAngle = a.angle + t * a.speed;
 33      a.x = width * 0.5 + cos(currentAngle) * a.radiusX * (1 + 0.3 * sin(t * 0.005));
 34      a.y = height * 0.5 + sin(currentAngle * 1.3) * a.radiusY * (1 + 0.2 * cos(t * 0.007));
 35    }
 36  
 37    for (let p of particles) {
 38      p.update(attractors);
 39      p.show();
 40    }
 41  }
 42  
 43  class Particle {
 44    constructor() {
 45      this.reset();
 46    }
 47  
 48    reset() {
 49      this.pos = createVector(random(width), random(height));
 50      this.prevPos = this.pos.copy();
 51      this.vel = p5.Vector.random2D().mult(random(0.5, 2));
 52      this.acc = createVector();
 53      this.maxSpeed = random(2.5, 5.0);
 54      this.baseHue = random(160, 320);
 55      this.life = random(150, 320);
 56      this.age = 0;
 57    }
 58  
 59    update(attractors) {
 60      this.prevPos.set(this.pos);
 61  
 62      for (let a of attractors) {
 63        let forceVec = createVector(a.x - this.pos.x, a.y - this.pos.y);
 64        let distSq = constrain(forceVec.magSq(), 400, 36000);
 65        let strength = 260 / distSq;
 66        forceVec.normalize().mult(strength);
 67  
 68        let swirl = createVector(-forceVec.y, forceVec.x).mult(1.7);
 69        this.acc.add(forceVec);
 70        this.acc.add(swirl);
 71      }
 72  
 73      if (mouseX > 0 && mouseX < width && mouseY > 0 && mouseY < height) {
 74        let mVec = createVector(mouseX - this.pos.x, mouseY - this.pos.y);
 75        let d = mVec.mag();
 76        if (d < 220 && d > 1) {
 77          mVec.normalize();
 78          let mForce = mouseIsPressed ? -4.0 : 1.2;
 79          mVec.mult(mForce * (1 - d / 220));
 80          this.acc.add(mVec);
 81        }
 82      }
 83  
 84      let n = noise(this.pos.x * 0.003, this.pos.y * 0.003, frameCount * 0.004);
 85      let flowAngle = n * TWO_PI * 4;
 86      let flow = p5.Vector.fromAngle(flowAngle).mult(0.35);
 87      this.acc.add(flow);
 88  
 89      this.vel.add(this.acc);
 90      this.vel.limit(this.maxSpeed);
 91      this.pos.add(this.vel);
 92      this.acc.mult(0);
 93  
 94      this.age++;
 95      if (this.age > this.life || this.pos.x < -20 || this.pos.x > width + 20 || this.pos.y < -20 || this.pos.y > height + 20) {
 96        this.reset();
 97      }
 98    }
 99  
100    show() {
101      let speed = this.vel.mag();
102      let h = (this.baseHue + speed * 12 + frameCount * 0.15) % 360;
103      let progress = this.age / this.life;
104      let alpha = sin(progress * PI) * 85;
105      stroke(h, 85, 95, alpha);
106      strokeWeight(map(speed, 0, this.maxSpeed, 1, 2.5));
107      line(this.prevPos.x, this.prevPos.y, this.pos.x, this.pos.y);
108    }
109  }
110  
111  function windowResized() {
112    resizeCanvas(windowWidth, windowHeight);
113    background(230, 40, 8);
114  }