Source of: Bioluminescent Flow Garden, by GPT-5.5
136 lines, 2 of them comments ยท 1,395 tokens, written in 14 seconds for $0.042
1 let particles = [];
2 let seed;
3 let palette;
4
5 function setup() {
6 createCanvas(windowWidth, windowHeight);
7 pixelDensity(1);
8 colorMode(HSB, 360, 100, 100, 100);
9 blendMode(ADD);
10 resetSketch();
11 }
12
13 function resetSketch() {
14 seed = floor(random(100000));
15 randomSeed(seed);
16 noiseSeed(seed);
17
18 palette = [
19 color(175, 85, 95, 18),
20 color(205, 90, 100, 16),
21 color(265, 70, 95, 13),
22 color(320, 70, 100, 12),
23 color(95, 70, 95, 10)
24 ];
25
26 particles = [];
27 const count = floor((width * height) / 5200);
28 for (let i = 0; i < count; i++) {
29 particles.push(new Particle(random(width), random(height)));
30 }
31
32 background(230, 55, 5);
33 }
34
35 function draw() {
36 // Soft fade for trails
37 blendMode(BLEND);
38 noStroke();
39 fill(230, 55, 5, 7);
40 rect(0, 0, width, height);
41
42 blendMode(ADD);
43 for (let p of particles) {
44 p.update();
45 p.display();
46 }
47
48 // Central glow near mouse
49 const mx = mouseX || width / 2;
50 const my = mouseY || height / 2;
51 noStroke();
52 for (let r = 80; r > 0; r -= 10) {
53 fill(185, 90, 100, map(r, 80, 0, 0.8, 5));
54 circle(mx, my, r);
55 }
56 }
57
58 class Particle {
59 constructor(x, y) {
60 this.pos = createVector(x, y);
61 this.prev = this.pos.copy();
62 this.vel = p5.Vector.random2D().mult(random(0.5, 2));
63 this.life = random(80, 260);
64 this.maxLife = this.life;
65 this.size = random(0.8, 2.7);
66 this.col = random(palette);
67 this.wander = random(0.002, 0.006);
68 }
69
70 update() {
71 this.prev.set(this.pos);
72
73 const n = noise(this.pos.x * this.wander, this.pos.y * this.wander, frameCount * 0.003);
74 const angle = n * TWO_PI * 4.0;
75 const flow = p5.Vector.fromAngle(angle).mult(0.16);
76
77 const mouse = createVector(mouseX || width / 2, mouseY || height / 2);
78 const away = p5.Vector.sub(this.pos, mouse);
79 const d = away.mag();
80 if (d < 180) {
81 away.normalize().mult(map(d, 0, 180, 0.55, 0.0));
82 flow.add(away);
83 }
84
85 this.vel.add(flow);
86 this.vel.limit(2.6);
87 this.pos.add(this.vel);
88 this.life--;
89
90 if (this.pos.x < -40 || this.pos.x > width + 40 || this.pos.y < -40 || this.pos.y > height + 40 || this.life <= 0) {
91 this.respawn();
92 }
93 }
94
95 display() {
96 const a = map(this.life, 0, this.maxLife, 0, alpha(this.col));
97 stroke(hue(this.col), saturation(this.col), brightness(this.col), a);
98 strokeWeight(this.size);
99 line(this.prev.x, this.prev.y, this.pos.x, this.pos.y);
100
101 if (random() < 0.025) {
102 noStroke();
103 fill(hue(this.col), saturation(this.col), 100, a * 1.8);
104 circle(this.pos.x, this.pos.y, this.size * random(2, 5));
105 }
106 }
107
108 respawn() {
109 const edge = floor(random(4));
110 if (edge === 0) this.pos.set(random(width), -10);
111 if (edge === 1) this.pos.set(width + 10, random(height));
112 if (edge === 2) this.pos.set(random(width), height + 10);
113 if (edge === 3) this.pos.set(-10, random(height));
114
115 this.prev.set(this.pos);
116 this.vel = p5.Vector.random2D().mult(random(0.5, 2));
117 this.life = random(90, 280);
118 this.maxLife = this.life;
119 this.size = random(0.8, 2.7);
120 this.col = random(palette);
121 }
122 }
123
124 function mousePressed() {
125 resetSketch();
126 }
127
128 function keyPressed() {
129 if (key === 's' || key === 'S') saveCanvas('bioluminescent-flow-garden', 'png');
130 if (key === 'r' || key === 'R') resetSketch();
131 }
132
133 function windowResized() {
134 resizeCanvas(windowWidth, windowHeight);
135 resetSketch();
136 }