117 lines, 3 of them comments · 1,702 tokens, written in 27 seconds for $0.0009


  1  let particles = [];
  2  let palette = [178, 188, 198, 28, 345];
  3  let flowScale = 0.0022;
  4  let time = 0;
  5  
  6  function setup() {
  7    createCanvas(windowWidth, windowHeight);
  8    colorMode(HSB, 360, 100, 100, 100);
  9    pixelDensity(1);
 10    background(222, 68, 8);
 11    createParticles();
 12  }
 13  
 14  function createParticles() {
 15    particles = [];
 16    const count = min(2600, floor(width * height / 300));
 17    for (let i = 0; i < count; i++) {
 18      particles.push(new DriftParticle(random(width), random(height), i));
 19    }
 20  }
 21  
 22  function draw() {
 23    // A translucent wash preserves the particles' fading paths.
 24    noStroke();
 25    fill(222, 68, 8, 9);
 26    rect(0, 0, width, height);
 27  
 28    time += 0.003;
 29    for (const p of particles) p.moveAndDraw();
 30  
 31    drawCaption();
 32  }
 33  
 34  class DriftParticle {
 35    constructor(x, y, seed) {
 36      this.x = x;
 37      this.y = y;
 38      this.seed = seed;
 39      this.hue = palette[floor(random(palette.length))] + random(-5, 5);
 40      this.weight = random(0.45, 1.35);
 41      this.speed = random(0.7, 1.8);
 42      this.alpha = random(20, 58);
 43    }
 44  
 45    moveAndDraw() {
 46      const oldX = this.x;
 47      const oldY = this.y;
 48      const n = noise(this.x * flowScale, this.y * flowScale, time + this.seed * 0.001);
 49      let angle = n * TWO_PI * 2.2;
 50      let vx = cos(angle);
 51      let vy = sin(angle);
 52  
 53      // A broad, gentle eddy gives the field its tidal shape.
 54      const cx = width * 0.52;
 55      const cy = height * 0.53;
 56      const dx = this.x - cx;
 57      const dy = this.y - cy;
 58      const dist = sqrt(dx * dx + dy * dy) + 40;
 59      vx += (-dy / dist) * 0.48;
 60      vy += (dx / dist) * 0.48;
 61  
 62      // The pointer locally twists the current.
 63      if (mouseIsPressed || (mouseX >= 0 && mouseX <= width && mouseY >= 0 && mouseY <= height)) {
 64        const mx = this.x - mouseX;
 65        const my = this.y - mouseY;
 66        const d = sqrt(mx * mx + my * my);
 67        if (d < 170 && d > 1) {
 68          const force = (1 - d / 170) * 1.8;
 69          vx += (-my / d) * force;
 70          vy += (mx / d) * force;
 71        }
 72      }
 73  
 74      const mag = sqrt(vx * vx + vy * vy) || 1;
 75      this.x += (vx / mag) * this.speed;
 76      this.y += (vy / mag) * this.speed;
 77  
 78      stroke(this.hue, 52, 96, this.alpha);
 79      strokeWeight(this.weight);
 80      line(oldX, oldY, this.x, this.y);
 81  
 82      if (this.x < -2 || this.x > width + 2 || this.y < -2 || this.y > height + 2) {
 83        this.x = random(width);
 84        this.y = random(height);
 85      }
 86    }
 87  }
 88  
 89  function drawCaption() {
 90    noStroke();
 91    fill(188, 20, 92, 74);
 92    textFont('sans-serif');
 93    textSize(11);
 94    drawingContext.letterSpacing = '2px';
 95    text('S I L T   /   B I O L U M I N E S C E N T   C U R R E N T', 24, 30);
 96    drawingContext.letterSpacing = '1px';
 97    fill(188, 12, 80, 48);
 98    textSize(10);
 99    text('MOVE TO STIR  ·  CLICK TO RESET', 24, height - 24);
100    drawingContext.letterSpacing = '0px';
101  }
102  
103  function mousePressed() {
104    if (mouseButton === LEFT) {
105      for (const p of particles) {
106        p.x = random(width);
107        p.y = random(height);
108      }
109      background(222, 68, 8);
110    }
111  }
112  
113  function windowResized() {
114    resizeCanvas(windowWidth, windowHeight);
115    background(222, 68, 8);
116    createParticles();
117  }