153 lines, 13 of them comments ยท 1,734 tokens, written in 20 seconds for $0.0077


  1  let particles = [];
  2  const NUM_PARTICLES = 800;
  3  let noiseScale = 0.005;
  4  let time = 0;
  5  let hueOffset = 0;
  6  
  7  function setup() {
  8    createCanvas(windowWidth, windowHeight);
  9    colorMode(HSB, 360, 100, 100, 100);
 10    background(0, 0, 0);
 11    
 12    for (let i = 0; i < NUM_PARTICLES; i++) {
 13      particles.push({
 14        x: random(width),
 15        y: random(height),
 16        prevX: 0,
 17        prevY: 0,
 18        speed: random(1, 3),
 19        life: random(100, 300),
 20        maxLife: 300,
 21        hue: random(360)
 22      });
 23      particles[i].prevX = particles[i].x;
 24      particles[i].prevY = particles[i].y;
 25    }
 26  }
 27  
 28  function draw() {
 29    // Semi-transparent background for trail effect
 30    noStroke();
 31    fill(0, 0, 0, 3);
 32    rect(0, 0, width, height);
 33    
 34    time += 0.002;
 35    hueOffset += 0.2;
 36    
 37    // Calculate mouse influence
 38    let mouseInfluence = 0;
 39    if (mouseIsPressed) {
 40      mouseInfluence = 0.01;
 41    }
 42    
 43    for (let p of particles) {
 44      p.prevX = p.x;
 45      p.prevY = p.y;
 46      
 47      // Calculate noise-based angle
 48      let noiseVal = noise(
 49        p.x * noiseScale,
 50        p.y * noiseScale,
 51        time
 52      );
 53      let angle = noiseVal * TWO_PI * 4;
 54      
 55      // Add mouse attraction when mouse is pressed
 56      if (mouseIsPressed) {
 57        let dx = mouseX - p.x;
 58        let dy = mouseY - p.y;
 59        let dist = sqrt(dx * dx + dy * dy);
 60        if (dist < 200) {
 61          let attractAngle = atan2(dy, dx);
 62          let strength = map(dist, 0, 200, 0.8, 0);
 63          angle = lerp(angle, attractAngle, strength);
 64          p.speed = lerp(p.speed, 4, 0.1);
 65        }
 66      }
 67      
 68      // Move particle
 69      p.x += cos(angle) * p.speed;
 70      p.y += sin(angle) * p.speed;
 71      
 72      // Update life
 73      p.life--;
 74      
 75      // Draw particle trail
 76      let alpha = map(p.life, 0, p.maxLife, 0, 80);
 77      let hue = (p.hue + hueOffset + noiseVal * 120) % 360;
 78      let saturation = 80;
 79      let brightness = 90 + sin(time * 5 + p.hue) * 10;
 80      
 81      stroke(hue, saturation, brightness, alpha);
 82      strokeWeight(1.5);
 83      line(p.prevX, p.prevY, p.x, p.y);
 84      
 85      // Reset particle if out of bounds or life expired
 86      if (p.x < -10 || p.x > width + 10 || 
 87          p.y < -10 || p.y > height + 10 || 
 88          p.life <= 0) {
 89        resetParticle(p);
 90      }
 91      
 92      // Gradually restore speed
 93      p.speed = lerp(p.speed, random(1, 3), 0.01);
 94    }
 95    
 96    // Draw subtle glow at mouse position
 97    if (mouseIsPressed) {
 98      noStroke();
 99      for (let r = 60; r > 0; r -= 10) {
100        fill((hueOffset + 180) % 360, 60, 100, map(r, 0, 60, 15, 2));
101        ellipse(mouseX, mouseY, r, r);
102      }
103    }
104  }
105  
106  function resetParticle(p) {
107    // Spawn from edges or random position
108    if (random() < 0.5) {
109      let side = floor(random(4));
110      switch(side) {
111        case 0: p.x = random(width); p.y = 0; break;
112        case 1: p.x = width; p.y = random(height); break;
113        case 2: p.x = random(width); p.y = height; break;
114        case 3: p.x = 0; p.y = random(height); break;
115      }
116    } else {
117      p.x = random(width);
118      p.y = random(height);
119    }
120    p.prevX = p.x;
121    p.prevY = p.y;
122    p.life = p.maxLife;
123    p.hue = random(360);
124    p.speed = random(1, 3);
125  }
126  
127  function mousePressed() {
128    // Burst of particles from mouse
129    for (let i = 0; i < 20; i++) {
130      let idx = floor(random(particles.length));
131      particles[idx].x = mouseX + random(-20, 20);
132      particles[idx].y = mouseY + random(-20, 20);
133      particles[idx].prevX = particles[idx].x;
134      particles[idx].prevY = particles[idx].y;
135      particles[idx].life = particles[idx].maxLife;
136      particles[idx].speed = random(2, 5);
137    }
138  }
139  
140  function keyPressed() {
141    if (key === ' ') {
142      // Clear canvas
143      background(0, 0, 0);
144    }
145    if (key === 's' || key === 'S') {
146      saveCanvas('flow_field', 'png');
147    }
148  }
149  
150  function windowResized() {
151    resizeCanvas(windowWidth, windowHeight);
152    background(0, 0, 0);
153  }