Source of: Orbiting Particle Field, by Grok 4.5
102 lines, 3 of them comments ยท 1,170 tokens, written in 15 seconds for $0.0075
1 let particles = [];
2 const COUNT = 120;
3 let attractors = [];
4
5 function setup() {
6 createCanvas(windowWidth, windowHeight);
7 colorMode(HSB, 360, 100, 100, 100);
8 background(0);
9 for (let i = 0; i < COUNT; i++) {
10 particles.push(new Particle(random(width), random(height)));
11 }
12 for (let i = 0; i < 3; i++) {
13 attractors.push(createVector(random(width), random(height)));
14 }
15 }
16
17 function draw() {
18 noStroke();
19 fill(0, 0, 0, 8);
20 rect(0, 0, width, height);
21
22 // gently drift attractors; mouse pulls the nearest one
23 for (let i = 0; i < attractors.length; i++) {
24 attractors[i].x += sin(frameCount * 0.01 + i) * 0.4;
25 attractors[i].y += cos(frameCount * 0.013 + i * 2) * 0.4;
26 }
27 if (mouseIsPressed || (mouseX !== 0 && mouseY !== 0)) {
28 let nearest = 0;
29 let best = Infinity;
30 for (let i = 0; i < attractors.length; i++) {
31 let d = dist(mouseX, mouseY, attractors[i].x, attractors[i].y);
32 if (d < best) { best = d; nearest = i; }
33 }
34 attractors[nearest].x = lerp(attractors[nearest].x, mouseX, 0.05);
35 attractors[nearest].y = lerp(attractors[nearest].y, mouseY, 0.05);
36 }
37
38 for (let p of particles) {
39 p.follow(attractors);
40 p.update();
41 p.show();
42 }
43 }
44
45 class Particle {
46 constructor(x, y) {
47 this.pos = createVector(x, y);
48 this.vel = p5.Vector.random2D().mult(random(0.5, 2));
49 this.acc = createVector(0, 0);
50 this.hue = random(360);
51 this.sz = random(1.5, 4);
52 }
53
54 follow(attractors) {
55 this.acc.set(0, 0);
56 for (let a of attractors) {
57 let dir = p5.Vector.sub(a, this.pos);
58 let d = constrain(dir.mag(), 40, 400);
59 dir.normalize();
60 // orbital-ish force: attract + perpendicular swirl
61 let strength = 80 / (d * d);
62 let attract = dir.copy().mult(strength * 18);
63 let swirl = createVector(-dir.y, dir.x).mult(strength * 12);
64 this.acc.add(attract);
65 this.acc.add(swirl);
66 }
67 }
68
69 update() {
70 this.vel.add(this.acc);
71 this.vel.limit(3.5);
72 this.pos.add(this.vel);
73 this.hue = (this.hue + 0.2) % 360;
74
75 // soft wrap
76 if (this.pos.x < -20) this.pos.x = width + 20;
77 if (this.pos.x > width + 20) this.pos.x = -20;
78 if (this.pos.y < -20) this.pos.y = height + 20;
79 if (this.pos.y > height + 20) this.pos.y = -20;
80 }
81
82 show() {
83 stroke(this.hue, 70, 100, 70);
84 strokeWeight(this.sz);
85 point(this.pos.x, this.pos.y);
86 }
87 }
88
89 function windowResized() {
90 resizeCanvas(windowWidth, windowHeight);
91 background(0);
92 }
93
94 function keyPressed() {
95 if (key === 'c' || key === 'C') background(0);
96 if (key === 'r' || key === 'R') {
97 particles = [];
98 for (let i = 0; i < COUNT; i++) {
99 particles.push(new Particle(random(width), random(height)));
100 }
101 }
102 }