112 lines, no comments ยท 1,189 tokens, written in 33 seconds for $0.012


  1  let particles = [];
  2  const numParticles = 1800;
  3  const noiseScale = 0.004;
  4  let zOff = 0;
  5  let baseHue = 190;
  6  
  7  function setup() {
  8    createCanvas(windowWidth, windowHeight);
  9    colorMode(HSB, 360, 100, 100, 100);
 10    background(240, 60, 5);
 11    
 12    for (let i = 0; i < numParticles; i++) {
 13      particles.push(new Particle());
 14    }
 15  }
 16  
 17  function draw() {
 18    blendMode(BLEND);
 19    noStroke();
 20    fill(240, 60, 5, 8);
 21    rect(0, 0, width, height);
 22    
 23    blendMode(ADD);
 24    
 25    for (let p of particles) {
 26      p.follow();
 27      p.interact();
 28      p.update();
 29      p.edges();
 30      p.show();
 31    }
 32    
 33    zOff += 0.002;
 34  }
 35  
 36  function mousePressed() {
 37    baseHue = (baseHue + 65) % 360;
 38  }
 39  
 40  function windowResized() {
 41    resizeCanvas(windowWidth, windowHeight);
 42    blendMode(BLEND);
 43    background(240, 60, 5);
 44  }
 45  
 46  class Particle {
 47    constructor() {
 48      this.pos = createVector(random(width), random(height));
 49      this.vel = createVector(0, 0);
 50      this.acc = createVector(0, 0);
 51      this.maxSpeed = random(1.5, 3.5);
 52      this.prevPos = this.pos.copy();
 53      this.hueOffset = random(-35, 35);
 54      this.strokeW = random(0.8, 2.2);
 55    }
 56    
 57    follow() {
 58      let angle = noise(this.pos.x * noiseScale, this.pos.y * noiseScale, zOff) * TWO_PI * 4;
 59      let force = p5.Vector.fromAngle(angle);
 60      force.mult(0.25);
 61      this.applyForce(force);
 62    }
 63    
 64    interact() {
 65      if (mouseX > 0 && mouseX < width && mouseY > 0 && mouseY < height) {
 66        let mouse = createVector(mouseX, mouseY);
 67        let dir = p5.Vector.sub(this.pos, mouse);
 68        let d = dir.mag();
 69        if (d < 160) {
 70          dir.normalize();
 71          let strength = map(d, 0, 160, 1.5, 0);
 72          dir.mult(strength);
 73          this.applyForce(dir);
 74        }
 75      }
 76    }
 77    
 78    applyForce(force) {
 79      this.acc.add(force);
 80    }
 81    
 82    update() {
 83      this.prevPos.set(this.pos);
 84      this.vel.add(this.acc);
 85      this.vel.limit(this.maxSpeed);
 86      this.pos.add(this.vel);
 87      this.acc.mult(0);
 88    }
 89    
 90    show() {
 91      let h = (baseHue + this.hueOffset + frameCount * 0.15) % 360;
 92      if (h < 0) h += 360;
 93      let s = map(this.vel.mag(), 0, this.maxSpeed, 50, 90);
 94      let b = map(this.vel.mag(), 0, this.maxSpeed, 40, 100);
 95      
 96      stroke(h, s, b, 35);
 97      strokeWeight(this.strokeW);
 98      line(this.prevPos.x, this.prevPos.y, this.pos.x, this.pos.y);
 99    }
100    
101    edges() {
102      let wrapped = false;
103      if (this.pos.x > width) { this.pos.x = 0; wrapped = true; }
104      if (this.pos.x < 0) { this.pos.x = width; wrapped = true; }
105      if (this.pos.y > height) { this.pos.y = 0; wrapped = true; }
106      if (this.pos.y < 0) { this.pos.y = height; wrapped = true; }
107      
108      if (wrapped) {
109        this.prevPos.set(this.pos);
110      }
111    }
112  }