Source of: Silt / Bioluminescent Current, by GPT-6 Luna
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 }