122 lines, 5 of them comments ยท 1,136 tokens, written in 52 seconds for $0.092


  1  let particles = [];
  2  let flowfield = [];
  3  let cols, rows;
  4  let zoff = 0;
  5  let scl = 20;
  6  let inc = 0.1;
  7  
  8  function setup() {
  9    createCanvas(windowWidth, windowHeight);
 10    colorMode(HSB, 360, 100, 100, 100);
 11    
 12    cols = floor(width / scl);
 13    rows = floor(height / scl);
 14    flowfield = new Array(cols * rows);
 15    
 16    // Create particles
 17    for (let i = 0; i < 500; i++) {
 18      particles.push(new Particle());
 19    }
 20    
 21    background(0, 0, 10);
 22  }
 23  
 24  function draw() {
 25    // Semi-transparent background for trail effect
 26    fill(0, 0, 10, 10);
 27    noStroke();
 28    rect(0, 0, width, height);
 29    
 30    // Calculate flow field vectors
 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 * 2;
 37        let v = p5.Vector.fromAngle(angle);
 38        v.setMag(1);
 39        flowfield[index] = v;
 40        xoff += inc;
 41      }
 42      yoff += inc;
 43    }
 44    zoff += 0.003;
 45    
 46    // Update and display particles
 47    for (let particle of particles) {
 48      particle.follow(flowfield);
 49      particle.update();
 50      particle.edges();
 51      particle.show();
 52    }
 53  }
 54  
 55  class Particle {
 56    constructor() {
 57      this.pos = createVector(random(width), random(height));
 58      this.vel = createVector(0, 0);
 59      this.acc = createVector(0, 0);
 60      this.maxspeed = 4;
 61      this.hue = random(360);
 62      this.prevPos = this.pos.copy();
 63    }
 64    
 65    follow(vectors) {
 66      let x = floor(this.pos.x / scl);
 67      let y = floor(this.pos.y / scl);
 68      let index = x + y * cols;
 69      let force = vectors[index];
 70      if (force) {
 71        this.applyForce(force);
 72      }
 73    }
 74    
 75    applyForce(force) {
 76      this.acc.add(force);
 77    }
 78    
 79    update() {
 80      this.vel.add(this.acc);
 81      this.vel.limit(this.maxspeed);
 82      this.pos.add(this.vel);
 83      this.acc.mult(0);
 84    }
 85    
 86    show() {
 87      stroke(this.hue, 80, 100, 25);
 88      strokeWeight(1);
 89      line(this.pos.x, this.pos.y, this.prevPos.x, this.prevPos.y);
 90      this.prevPos = this.pos.copy();
 91      
 92      // Slowly shift hue
 93      this.hue = (this.hue + 0.1) % 360;
 94    }
 95    
 96    edges() {
 97      if (this.pos.x > width) {
 98        this.pos.x = 0;
 99        this.prevPos = this.pos.copy();
100      }
101      if (this.pos.x < 0) {
102        this.pos.x = width;
103        this.prevPos = this.pos.copy();
104      }
105      if (this.pos.y > height) {
106        this.pos.y = 0;
107        this.prevPos = this.pos.copy();
108      }
109      if (this.pos.y < 0) {
110        this.pos.y = height;
111        this.prevPos = this.pos.copy();
112      }
113    }
114  }
115  
116  function windowResized() {
117    resizeCanvas(windowWidth, windowHeight);
118    cols = floor(width / scl);
119    rows = floor(height / scl);
120    flowfield = new Array(cols * rows);
121    background(0, 0, 10);
122  }