Source of: Neon Current, by GPT-5.6 Sol
174 lines, 2 of them comments ยท 1,756 tokens, written in 18 seconds for $0.036
1 let particles = [];
2 let fieldScale = 0.0026;
3 let timeOffset = 0;
4 let paletteShift = 0;
5
6 function setup() {
7 createCanvas(windowWidth, windowHeight);
8 pixelDensity(min(2, pixelDensity()));
9 colorMode(HSB, 360, 100, 100, 100);
10 background(230, 45, 5);
11 strokeCap(ROUND);
12 createParticles();
13 }
14
15 function createParticles() {
16 particles = [];
17 const count = constrain(floor((width * height) / 4200), 180, 700);
18
19 for (let i = 0; i < count; i++) {
20 particles.push(new Particle(random(width), random(height)));
21 }
22 }
23
24 function draw() {
25 // Translucent paint slowly erases old trails.
26 blendMode(BLEND);
27 noStroke();
28 fill(230, 45, 5, 7);
29 rect(0, 0, width, height);
30
31 blendMode(ADD);
32 timeOffset += 0.0014;
33
34 for (const particle of particles) {
35 particle.followFlow();
36 particle.interact(mouseX, mouseY);
37 particle.update();
38 particle.wrap();
39 particle.display();
40 }
41
42 blendMode(BLEND);
43 drawPointerGlow();
44 }
45
46 function drawPointerGlow() {
47 if (mouseX < 0 || mouseX > width || mouseY < 0 || mouseY > height) return;
48
49 noFill();
50 const hue = (paletteShift + frameCount * 0.4) % 360;
51
52 for (let r = 36; r > 4; r -= 8) {
53 stroke(hue, 65, 100, map(r, 4, 36, 20, 2));
54 strokeWeight(1);
55 circle(mouseX, mouseY, r);
56 }
57 }
58
59 class Particle {
60 constructor(x, y) {
61 this.pos = createVector(x, y);
62 this.prev = this.pos.copy();
63 this.vel = p5.Vector.random2D().mult(random(0.2, 1.2));
64 this.acc = createVector();
65 this.speed = random(1.2, 3.5);
66 this.weight = random(0.5, 1.8);
67 this.seed = random(1000);
68 this.life = random(1000);
69 }
70
71 followFlow() {
72 const n = noise(
73 this.pos.x * fieldScale,
74 this.pos.y * fieldScale,
75 timeOffset + this.seed * 0.001
76 );
77
78 const angle = n * TWO_PI * 3.2 + sin(timeOffset * 5) * 0.35;
79 const force = p5.Vector.fromAngle(angle).mult(0.075);
80 this.acc.add(force);
81 }
82
83 interact(x, y) {
84 const pointer = createVector(x, y);
85 const delta = p5.Vector.sub(pointer, this.pos);
86 const distance = delta.mag();
87 const radius = min(width, height) * 0.22;
88
89 if (distance > 1 && distance < radius) {
90 const strength = pow(1 - distance / radius, 2) * 0.38;
91 delta.normalize();
92
93 // Hold the pointer to attract; otherwise it gently pushes away.
94 if (mouseIsPressed) {
95 this.acc.add(delta.mult(strength));
96 } else {
97 this.acc.sub(delta.mult(strength * 0.45));
98 }
99 }
100 }
101
102 update() {
103 this.prev.set(this.pos);
104 this.vel.add(this.acc);
105 this.vel.limit(this.speed);
106 this.pos.add(this.vel);
107 this.acc.mult(0);
108 this.life += 0.01;
109 }
110
111 wrap() {
112 let wrapped = false;
113
114 if (this.pos.x < -5) {
115 this.pos.x = width + 5;
116 wrapped = true;
117 } else if (this.pos.x > width + 5) {
118 this.pos.x = -5;
119 wrapped = true;
120 }
121
122 if (this.pos.y < -5) {
123 this.pos.y = height + 5;
124 wrapped = true;
125 } else if (this.pos.y > height + 5) {
126 this.pos.y = -5;
127 wrapped = true;
128 }
129
130 if (wrapped) this.prev.set(this.pos);
131 }
132
133 display() {
134 const directionHue = map(this.vel.heading(), -PI, PI, 0, 180);
135 const hue = (paletteShift + directionHue + noise(this.life) * 90) % 360;
136 const alpha = map(this.vel.mag(), 0, this.speed, 12, 55);
137
138 stroke(hue, 75, 100, alpha);
139 strokeWeight(this.weight);
140 line(this.prev.x, this.prev.y, this.pos.x, this.pos.y);
141
142 if (this.weight > 1.45) {
143 stroke(hue, 30, 100, alpha * 0.35);
144 strokeWeight(this.weight * 3);
145 point(this.pos.x, this.pos.y);
146 }
147 }
148 }
149
150 function mouseClicked() {
151 paletteShift = (paletteShift + random(55, 160)) % 360;
152 noiseSeed(floor(random(100000)));
153 background(230, 45, 5);
154 createParticles();
155 }
156
157 function keyPressed() {
158 if (key === 'r' || key === 'R') {
159 paletteShift = random(360);
160 noiseSeed(floor(random(100000)));
161 background(230, 45, 5);
162 createParticles();
163 }
164
165 if (key === 's' || key === 'S') {
166 saveCanvas('neon-current', 'png');
167 }
168 }
169
170 function windowResized() {
171 resizeCanvas(windowWidth, windowHeight);
172 background(230, 45, 5);
173 createParticles();
174 }