95 lines, 10 of them comments ยท 2,431 tokens, written in 14 seconds for $0.0013


 1  // ----------------------------------------------------------
 2  // Generative Flow Field
 3  // Particles drift along a Perlin noise vector field and
 4  // leave glowing trails. Move the mouse to disturb the flow.
 5  // ----------------------------------------------------------
 6  
 7  let particles = [];
 8  const NUM = 1400;
 9  let t = 0; // noise time axis
10  
11  function setup() {
12    createCanvas(windowWidth, windowHeight);
13    colorMode(HSB, 360, 100, 100, 1);
14    background(0);
15    seedParticles();
16  }
17  
18  function seedParticles() {
19    particles = [];
20    for (let i = 0; i < NUM; i++) particles.push(new Particle());
21  }
22  
23  function draw() {
24    // Soft fade of the previous frame -> long persistent trails
25    noStroke();
26    fill(0, 0, 0, 0.045);
27    rect(0, 0, width, height);
28  
29    t += 0.0025;
30  
31    for (const p of particles) {
32      p.update();
33      p.display();
34    }
35  }
36  
37  class Particle {
38    constructor() {
39      this.spawn();
40    }
41  
42    spawn() {
43      this.x = random(width);
44      this.y = random(height);
45      this.px = this.x;
46      this.py = this.y;
47      this.age = 0;
48      this.life = random(150, 500);
49      this.hue = random(170, 320);
50      this.speed = random(0.8, 2.4);
51    }
52  
53    update() {
54      this.px = this.x;
55      this.py = this.y;
56  
57      // Base direction from the noise field
58      const n = noise(this.x * 0.0016, this.y * 0.0016, t);
59      let angle = n * TWO_PI * 2;
60  
61      // Mouse interaction: repel + speed boost
62      const d = dist(this.x, this.y, mouseX, mouseY);
63      let boost = 1;
64      if (d < 200) {
65        const f = 1 - d / 200;
66        const away = atan2(this.y - mouseY, this.x - mouseX);
67        angle = lerp(angle, away, f * 0.8);
68        boost = 1 + f * 3;
69      }
70  
71      this.x += cos(angle) * this.speed * boost;
72      this.y += sin(angle) * this.speed * boost;
73  
74      this.age++;
75      const off = this.x < 0 || this.x > width || this.y < 0 || this.y > height;
76      if (off || this.age > this.life) this.spawn();
77    }
78  
79    display() {
80      // Hue slowly shifts along the field for a gradient feel
81      const n = noise(this.x * 0.004, this.y * 0.004, t * 2);
82      const h = (this.hue + n * 120) % 360;
83  
84      stroke(h, 75, 95, 0.35);
85      strokeWeight(1);
86      line(this.px, this.py, this.x, this.y);
87    }
88  }
89  
90  function windowResized() {
91    resizeCanvas(windowWidth, windowHeight);
92    background(0);
93    seedParticles();
94  }
95