102 lines, 3 of them comments ยท 1,170 tokens, written in 15 seconds for $0.0075


  1  let particles = [];
  2  const COUNT = 120;
  3  let attractors = [];
  4  
  5  function setup() {
  6    createCanvas(windowWidth, windowHeight);
  7    colorMode(HSB, 360, 100, 100, 100);
  8    background(0);
  9    for (let i = 0; i < COUNT; i++) {
 10      particles.push(new Particle(random(width), random(height)));
 11    }
 12    for (let i = 0; i < 3; i++) {
 13      attractors.push(createVector(random(width), random(height)));
 14    }
 15  }
 16  
 17  function draw() {
 18    noStroke();
 19    fill(0, 0, 0, 8);
 20    rect(0, 0, width, height);
 21  
 22    // gently drift attractors; mouse pulls the nearest one
 23    for (let i = 0; i < attractors.length; i++) {
 24      attractors[i].x += sin(frameCount * 0.01 + i) * 0.4;
 25      attractors[i].y += cos(frameCount * 0.013 + i * 2) * 0.4;
 26    }
 27    if (mouseIsPressed || (mouseX !== 0 && mouseY !== 0)) {
 28      let nearest = 0;
 29      let best = Infinity;
 30      for (let i = 0; i < attractors.length; i++) {
 31        let d = dist(mouseX, mouseY, attractors[i].x, attractors[i].y);
 32        if (d < best) { best = d; nearest = i; }
 33      }
 34      attractors[nearest].x = lerp(attractors[nearest].x, mouseX, 0.05);
 35      attractors[nearest].y = lerp(attractors[nearest].y, mouseY, 0.05);
 36    }
 37  
 38    for (let p of particles) {
 39      p.follow(attractors);
 40      p.update();
 41      p.show();
 42    }
 43  }
 44  
 45  class Particle {
 46    constructor(x, y) {
 47      this.pos = createVector(x, y);
 48      this.vel = p5.Vector.random2D().mult(random(0.5, 2));
 49      this.acc = createVector(0, 0);
 50      this.hue = random(360);
 51      this.sz = random(1.5, 4);
 52    }
 53  
 54    follow(attractors) {
 55      this.acc.set(0, 0);
 56      for (let a of attractors) {
 57        let dir = p5.Vector.sub(a, this.pos);
 58        let d = constrain(dir.mag(), 40, 400);
 59        dir.normalize();
 60        // orbital-ish force: attract + perpendicular swirl
 61        let strength = 80 / (d * d);
 62        let attract = dir.copy().mult(strength * 18);
 63        let swirl = createVector(-dir.y, dir.x).mult(strength * 12);
 64        this.acc.add(attract);
 65        this.acc.add(swirl);
 66      }
 67    }
 68  
 69    update() {
 70      this.vel.add(this.acc);
 71      this.vel.limit(3.5);
 72      this.pos.add(this.vel);
 73      this.hue = (this.hue + 0.2) % 360;
 74  
 75      // soft wrap
 76      if (this.pos.x < -20) this.pos.x = width + 20;
 77      if (this.pos.x > width + 20) this.pos.x = -20;
 78      if (this.pos.y < -20) this.pos.y = height + 20;
 79      if (this.pos.y > height + 20) this.pos.y = -20;
 80    }
 81  
 82    show() {
 83      stroke(this.hue, 70, 100, 70);
 84      strokeWeight(this.sz);
 85      point(this.pos.x, this.pos.y);
 86    }
 87  }
 88  
 89  function windowResized() {
 90    resizeCanvas(windowWidth, windowHeight);
 91    background(0);
 92  }
 93  
 94  function keyPressed() {
 95    if (key === 'c' || key === 'C') background(0);
 96    if (key === 'r' || key === 'R') {
 97      particles = [];
 98      for (let i = 0; i < COUNT; i++) {
 99        particles.push(new Particle(random(width), random(height)));
100      }
101    }
102  }