110 lines, 16 of them comments ยท 2,174 tokens, written in 11 seconds for $0.018


  1  let inc = 0.1;          // noise increment
  2  let scl = 20;           // size of each grid cell
  3  let cols, rows;
  4  let zoff = 0;           // noise z-offset (time)
  5  let particles = [];     // array of moving dots
  6  let flowfield;          // array of vectors
  7  
  8  function setup() {
  9    createCanvas(windowWidth, windowHeight);
 10    colorMode(HSB, 255);
 11    background(255);
 12    cols = floor(width / scl);
 13    rows = floor(height / scl);
 14    flowfield = new Array(cols * rows);
 15    // seed the canvas with particles
 16    for (let i = 0; i < 4000; i++) particles.push(new Particle());
 17    strokeWeight(1);
 18  }
 19  
 20  function draw() {
 21    // ----- build the flow field -----
 22    let yoff = 0;
 23    for (let y = 0; y < rows; y++) {
 24      let xoff = 0;
 25      for (let x = 0; x < cols; x++) {
 26        const idx = x + y * cols;
 27        const angle = noise(xoff, yoff, zoff) * TWO_PI * 4; // more curls
 28        const v = p5.Vector.fromAngle(angle).setMag(1);
 29        flowfield[idx] = v;
 30        xoff += inc;
 31      }
 32      yoff += inc;
 33    }
 34    zoff += 0.003; // animate field
 35  
 36    // ----- move and draw particles -----
 37    for (const p of particles) {
 38      p.follow(flowfield);
 39      p.update();
 40      p.edges();
 41      p.show();
 42    }
 43  }
 44  
 45  function mousePressed() {
 46    // burst of new particles at mouse position
 47    for (let i = 0; i < 250; i++) particles.push(new Particle(mouseX, mouseY));
 48  }
 49  
 50  function keyPressed() {
 51    if (key === ' ') background(255); // clear trails
 52  }
 53  
 54  function windowResized() {
 55    resizeCanvas(windowWidth, windowHeight);
 56    cols = floor(width / scl);
 57    rows = floor(height / scl);
 58    flowfield = new Array(cols * rows);
 59  }
 60  
 61  //--------------------------------------------------
 62  // Particle class
 63  //--------------------------------------------------
 64  class Particle {
 65    constructor(x = random(width), y = random(height)) {
 66      this.pos = createVector(x, y);
 67      this.prev = this.pos.copy();
 68      this.vel = createVector();
 69      this.acc = createVector();
 70      this.maxspeed = 2;
 71      this.hue = random(255);
 72    }
 73  
 74    follow(vectors) {
 75      const x = floor(this.pos.x / scl);
 76      const y = floor(this.pos.y / scl);
 77      const idx = x + y * cols;
 78      const force = vectors[idx];
 79      if (force) this.applyForce(force);
 80    }
 81  
 82    applyForce(f) {
 83      this.acc.add(f);
 84    }
 85  
 86    update() {
 87      this.vel.add(this.acc);
 88      this.vel.limit(this.maxspeed);
 89      this.pos.add(this.vel);
 90      this.acc.mult(0);
 91    }
 92  
 93    show() {
 94      stroke(this.hue, 200, 255, 25);
 95      line(this.pos.x, this.pos.y, this.prev.x, this.prev.y);
 96      this.updatePrev();
 97      this.hue = (this.hue + 0.2) % 255; // slow color cycle
 98    }
 99  
100    updatePrev() {
101      this.prev.set(this.pos);
102    }
103  
104    edges() {
105      if (this.pos.x > width) { this.pos.x = 0; this.updatePrev(); }
106      if (this.pos.x < 0)    { this.pos.x = width; this.updatePrev(); }
107      if (this.pos.y > height){ this.pos.y = 0; this.updatePrev(); }
108      if (this.pos.y < 0)    { this.pos.y = height; this.updatePrev(); }
109    }
110  }