129 lines, 10 of them comments ยท 1,250 tokens, written in 12 seconds for $0.0011


  1  // Interactive Flowing Particle System
  2  let particles = [];
  3  let flowField = [];
  4  let cols, rows;
  5  let scl = 10;
  6  let zOff = 0;
  7  let canvasWidth, canvasHeight;
  8  
  9  function setup() {
 10    createCanvas(800, 600);
 11    canvasWidth = width;
 12    canvasHeight = height;
 13    cols = canvasWidth / scl;
 14    rows = canvasHeight / scl;
 15    
 16    // Create initial particles
 17    for (let i = 0; i < 500; i++) {
 18      particles.push(new Particle());
 19    }
 20    
 21    background(10);
 22  }
 23  
 24  function draw() {
 25    // Semi-transparent background for trail effect
 26    fill(10, 10, 20, 25);
 27    noStroke();
 28    rect(0, 0, canvasWidth, canvasHeight);
 29    
 30    // Draw flow field
 31    let yoff = 0;
 32    for (let y = 0; y < rows; y++) {
 33      let xoff = 0;
 34      for (let x = 0; x < cols; x++) {
 35        let index = x + y * cols;
 36        let angle = noise(xoff, yoff, zOff) * TWO_PI * 4;
 37        flowField[index] = p5.Vector.fromAngle(angle);
 38        flowField[index].mult(0.5);
 39        xoff += 0.15;
 40      }
 41      yoff += 0.15;
 42    }
 43    zOff += 0.003;
 44    
 45    // Mouse attraction
 46    let mouse = createVector(mouseX, mouseY);
 47    
 48    // Update and draw particles
 49    for (let particle of particles) {
 50      let pos = particle.pos;
 51      let col = constrain(floor(pos.x / scl), 0, cols - 1);
 52      let row = constrain(floor(pos.y / scl), 0, rows - 1);
 53      let index = col + row * cols;
 54      let force = flowField[index] || p5.Vector.fromAngle(0);
 55      
 56      // Attract particles toward mouse
 57      let mouseForce = p5.Vector.sub(mouse, pos);
 58      let dist = mouseForce.mag();
 59      if (dist < 200) {
 60        mouseForce.normalize();
 61        mouseForce.mult(0.3 * (1 - dist / 200));
 62        force.add(mouseForce);
 63      }
 64      
 65      particle.applyForce(force);
 66      particle.update();
 67      particle.edges();
 68      particle.show();
 69    }
 70  }
 71  
 72  // Particle class
 73  class Particle {
 74    constructor() {
 75      this.pos = createVector(random(width), random(height));
 76      this.vel = createVector(random(-1, 1), random(-1, 1));
 77      this.acc = createVector(0, 0);
 78      this.maxSpeed = 4;
 79      this.hueVal = random(360);
 80      this.life = 255;
 81    }
 82    
 83    applyForce(force) {
 84      this.acc.add(force);
 85    }
 86    
 87    update() {
 88      this.vel.add(this.acc);
 89      this.vel.limit(this.maxSpeed);
 90      this.pos.add(this.vel);
 91      this.acc.mult(0);
 92      this.hueVal += 1;
 93    }
 94    
 95    edges() {
 96      if (this.pos.x > width) this.pos.x = 0;
 97      if (this.pos.x < 0) this.pos.x = width;
 98      if (this.pos.y > height) this.pos.y = 0;
 99      if (this.pos.y < 0) this.pos.y = height;
100    }
101    
102    show() {
103      let speed = this.vel.mag();
104      let h = this.hueVal % 360;
105      let s = 80;
106      let b = min(255, 100 + speed * 50);
107      
108      noStroke();
109      fill(h, s, b, 180);
110      ellipse(this.pos.x, this.pos.y, 3 + speed * 0.5);
111    }
112  }
113  
114  // Add particles on mouse click
115  function mousePressed() {
116    for (let i = 0; i < 30; i++) {
117      particles.push(new Particle());
118    }
119  }
120  
121  // Remove excess particles if too many
122  function mouseDragged() {
123    if (particles.length > 1500) {
124      particles.splice(0, 30);
125    }
126    for (let i = 0; i < 3; i++) {
127      particles.push(new Particle());
128    }
129  }