146 lines, 19 of them comments ยท 4,935 tokens, written in 65 seconds for $0.049


  1  let particles = [];
  2  let NUM = 1800;
  3  let fieldScale = 0.0015;   // spatial scale of the noise field
  4  let time = 0;
  5  let timeSpeed = 0.0016;    // speed that the field evolves
  6  let paused = false;
  7  let fadeAlpha = 10;        // trail fade (0..255)
  8  let baseSpeed = 55;        // global particle speed gain
  9  let mouseInfluence = 0.9;  // swirl strength near mouse when pressed
 10  
 11  function setup() {
 12    createCanvas(windowWidth, windowHeight);
 13    colorMode(HSB, 360, 100, 100, 100);
 14    background(0);
 15    noiseDetail(4, 0.55);
 16    initParticles();
 17  }
 18  
 19  function initParticles() {
 20    particles = [];
 21    for (let i = 0; i < NUM; i++) {
 22      particles.push(new Particle(random(width), random(height)));
 23    }
 24  }
 25  
 26  class Particle {
 27    constructor(x, y) {
 28      this.x = x;
 29      this.y = y;
 30      this.px = x;
 31      this.py = y;
 32      this.h = random(360);
 33      this.s = 60 + random(30);
 34      this.b = 95;
 35      this.alpha = 48 + random(28);
 36      this.gain = random(0.8, 1.5);
 37      this.w = random(0.6, 1.4);
 38    }
 39  
 40    step() {
 41      // Flow field velocity from curl of noise
 42      const v = curl(this.x, this.y, time);
 43  
 44      // Optional mouse swirl influence
 45      if (mouseIsPressed) {
 46        const dx = this.x - mouseX;
 47        const dy = this.y - mouseY;
 48        const d2 = dx*dx + dy*dy;
 49        const m = 1.0 / (1 + d2 * 0.002);
 50        // perpendicular (swirl) component around mouse
 51        const sx = -dy * m * mouseInfluence;
 52        const sy =  dx * m * mouseInfluence;
 53        v.x += sx;
 54        v.y += sy;
 55      }
 56  
 57      // Speed and small jitter
 58      v.mult(baseSpeed * this.gain);
 59      v.x += (noise(this.x * 0.01, this.y * 0.01, time * 5) - 0.5) * 0.1;
 60      v.y += (noise(this.y * 0.01, this.x * 0.01, time * 5) - 0.5) * 0.1;
 61  
 62      // Integrate
 63      this.px = this.x;
 64      this.py = this.y;
 65      this.x += v.x * 0.016; // dt-ish
 66      this.y += v.y * 0.016;
 67  
 68      // Wrap with seam fix
 69      let wrapped = false;
 70      if (this.x < -2) { this.x = width + 2; wrapped = true; }
 71      if (this.x > width + 2) { this.x = -2; wrapped = true; }
 72      if (this.y < -2) { this.y = height + 2; wrapped = true; }
 73      if (this.y > height + 2) { this.y = -2; wrapped = true; }
 74      if (wrapped) { this.px = this.x; this.py = this.y; }
 75  
 76      // Evolve hue slowly
 77      this.h = (this.h + 0.12 + (v.mag() * 0.0005)) % 360;
 78    }
 79  
 80    draw() {
 81      const spd = dist(this.x, this.y, this.px, this.py);
 82      const w = constrain(map(spd, 0, 4, 0.4, 1.8) * this.w, 0.3, 2.2);
 83  
 84      stroke(this.h, this.s, this.b, this.alpha);
 85      strokeWeight(w);
 86      line(this.px, this.py, this.x, this.y);
 87    }
 88  }
 89  
 90  // Curl of 3D noise n(x,y,t)
 91  function curl(x, y, t) {
 92    const xs = x * fieldScale;
 93    const ys = y * fieldScale;
 94    const e = 0.01; // finite difference step in noise domain
 95  
 96    const n1 = noise(xs + e, ys, t);
 97    const n2 = noise(xs - e, ys, t);
 98    const n3 = noise(xs, ys + e, t);
 99    const n4 = noise(xs, ys - e, t);
100  
101    const dx = n1 - n2;
102    const dy = n3 - n4;
103  
104    // Curl(F) of a scalar field-derived vector; divergence-free swirl
105    return createVector(dy, -dx);
106  }
107  
108  function draw() {
109    if (paused) return;
110  
111    // Gentle fade of trails
112    blendMode(BLEND);
113    noStroke();
114    fill(0, 0, 0, fadeAlpha);
115    rect(0, 0, width, height);
116  
117    // Additive blending for luminous ribbons
118    blendMode(ADD);
119  
120    for (let p of particles) {
121      p.step();
122      p.draw();
123    }
124  
125    // Evolve field time
126    time += timeSpeed;
127  }
128  
129  function keyPressed() {
130    if (key === ' '){
131      paused = !paused;
132      if (!paused) loop();
133    }
134    if (key === 'r' || key === 'R') {
135      background(0);
136      initParticles();
137    }
138    if (key === 's' || key === 'S') {
139      saveCanvas('aurora-curl-ribbons', 'png');
140    }
141  }
142  
143  function windowResized() {
144    resizeCanvas(windowWidth, windowHeight);
145    background(0);
146  }