101 lines, 1 of them comments ยท 1,288 tokens, written in 25 seconds for $0.065


  1  let particles = [];
  2  let time = 0;
  3  let hueOffset = 0;
  4  let paused = false;
  5  
  6  function setup() {
  7    createCanvas(windowWidth, windowHeight);
  8    pixelDensity(Math.min(window.devicePixelRatio || 1, 2));
  9    colorMode(HSB, 360, 100, 100, 1);
 10    strokeCap(ROUND);
 11    regenerate();
 12  }
 13  
 14  function regenerate() {
 15    noiseSeed(floor(random(100000)));
 16    hueOffset = random(360);
 17    time = 0;
 18    background(230, 45, 5);
 19    const count = constrain(floor(width * height / 850), 350, 1600);
 20    particles = Array.from({ length: count }, () => makeParticle());
 21  }
 22  
 23  function makeParticle() {
 24    return {
 25      x: random(width),
 26      y: random(height),
 27      vx: 0,
 28      vy: 0,
 29      speed: random(0.8, 2.6),
 30      weight: random(0.6, 1.7),
 31      age: 0,
 32      life: random(160, 600),
 33      hue: random(75)
 34    };
 35  }
 36  
 37  function draw() {
 38    if (paused) return;
 39  
 40    background(230, 45, 5, 0.065);
 41    const step = Math.min(deltaTime / 16.667, 2);
 42    const scale = 0.0018;
 43    const pointerInside = mouseX > 0 && mouseX < width && mouseY > 0 && mouseY < height;
 44  
 45    for (let i = 0; i < particles.length; i++) {
 46      const p = particles[i];
 47      const previousX = p.x;
 48      const previousY = p.y;
 49      const field = noise(p.x * scale, p.y * scale, time);
 50      const angle = field * TWO_PI * 2.5;
 51      let fx = cos(angle);
 52      let fy = sin(angle);
 53  
 54      // A soft vortex lets the pointer comb and twist the trails.
 55      if (pointerInside) {
 56        const dx = p.x - mouseX;
 57        const dy = p.y - mouseY;
 58        const distance = sqrt(dx * dx + dy * dy);
 59        const influence = 2.8 * exp(-distance * distance / 24000);
 60        const divisor = Math.max(distance, 20);
 61        fx += (-dy / divisor) * influence;
 62        fy += (dx / divisor) * influence;
 63      }
 64  
 65      p.vx = lerp(p.vx, fx * p.speed, 0.09);
 66      p.vy = lerp(p.vy, fy * p.speed, 0.09);
 67      p.x += p.vx * step;
 68      p.y += p.vy * step;
 69      p.age += step;
 70  
 71      if (p.x < 0 || p.x > width || p.y < 0 || p.y > height || p.age > p.life) {
 72        particles[i] = makeParticle();
 73        continue;
 74      }
 75  
 76      const fade = Math.min(p.age / 25, (p.life - p.age) / 45, 1);
 77      const hue = (hueOffset + p.hue + field * 95 + time * 18) % 360;
 78      stroke(hue, 62, 100, 0.52 * fade);
 79      strokeWeight(p.weight);
 80      line(previousX, previousY, p.x, p.y);
 81    }
 82  
 83    time += 0.002 * step;
 84  }
 85  
 86  function mousePressed() {
 87    regenerate();
 88  }
 89  
 90  function keyPressed() {
 91    if (key === ' ') {
 92      paused = !paused;
 93      return false;
 94    }
 95  }
 96  
 97  function windowResized() {
 98    resizeCanvas(windowWidth, windowHeight);
 99    regenerate();
100  }
101