Source of: Interactive Flowing Particles, by Qwen3.6 35B A3B
129 lines, 10 of them comments ยท 1,250 tokens, written in 12 seconds for $0.0011
1 // Interactive Flowing Particle System
2 let particles = [];
3 let flowField = [];
4 let cols, rows;
5 let scl = 10;
6 let zOff = 0;
7 let canvasWidth, canvasHeight;
8
9 function setup() {
10 createCanvas(800, 600);
11 canvasWidth = width;
12 canvasHeight = height;
13 cols = canvasWidth / scl;
14 rows = canvasHeight / scl;
15
16 // Create initial particles
17 for (let i = 0; i < 500; i++) {
18 particles.push(new Particle());
19 }
20
21 background(10);
22 }
23
24 function draw() {
25 // Semi-transparent background for trail effect
26 fill(10, 10, 20, 25);
27 noStroke();
28 rect(0, 0, canvasWidth, canvasHeight);
29
30 // Draw flow field
31 let yoff = 0;
32 for (let y = 0; y < rows; y++) {
33 let xoff = 0;
34 for (let x = 0; x < cols; x++) {
35 let index = x + y * cols;
36 let angle = noise(xoff, yoff, zOff) * TWO_PI * 4;
37 flowField[index] = p5.Vector.fromAngle(angle);
38 flowField[index].mult(0.5);
39 xoff += 0.15;
40 }
41 yoff += 0.15;
42 }
43 zOff += 0.003;
44
45 // Mouse attraction
46 let mouse = createVector(mouseX, mouseY);
47
48 // Update and draw particles
49 for (let particle of particles) {
50 let pos = particle.pos;
51 let col = constrain(floor(pos.x / scl), 0, cols - 1);
52 let row = constrain(floor(pos.y / scl), 0, rows - 1);
53 let index = col + row * cols;
54 let force = flowField[index] || p5.Vector.fromAngle(0);
55
56 // Attract particles toward mouse
57 let mouseForce = p5.Vector.sub(mouse, pos);
58 let dist = mouseForce.mag();
59 if (dist < 200) {
60 mouseForce.normalize();
61 mouseForce.mult(0.3 * (1 - dist / 200));
62 force.add(mouseForce);
63 }
64
65 particle.applyForce(force);
66 particle.update();
67 particle.edges();
68 particle.show();
69 }
70 }
71
72 // Particle class
73 class Particle {
74 constructor() {
75 this.pos = createVector(random(width), random(height));
76 this.vel = createVector(random(-1, 1), random(-1, 1));
77 this.acc = createVector(0, 0);
78 this.maxSpeed = 4;
79 this.hueVal = random(360);
80 this.life = 255;
81 }
82
83 applyForce(force) {
84 this.acc.add(force);
85 }
86
87 update() {
88 this.vel.add(this.acc);
89 this.vel.limit(this.maxSpeed);
90 this.pos.add(this.vel);
91 this.acc.mult(0);
92 this.hueVal += 1;
93 }
94
95 edges() {
96 if (this.pos.x > width) this.pos.x = 0;
97 if (this.pos.x < 0) this.pos.x = width;
98 if (this.pos.y > height) this.pos.y = 0;
99 if (this.pos.y < 0) this.pos.y = height;
100 }
101
102 show() {
103 let speed = this.vel.mag();
104 let h = this.hueVal % 360;
105 let s = 80;
106 let b = min(255, 100 + speed * 50);
107
108 noStroke();
109 fill(h, s, b, 180);
110 ellipse(this.pos.x, this.pos.y, 3 + speed * 0.5);
111 }
112 }
113
114 // Add particles on mouse click
115 function mousePressed() {
116 for (let i = 0; i < 30; i++) {
117 particles.push(new Particle());
118 }
119 }
120
121 // Remove excess particles if too many
122 function mouseDragged() {
123 if (particles.length > 1500) {
124 particles.splice(0, 30);
125 }
126 for (let i = 0; i < 3; i++) {
127 particles.push(new Particle());
128 }
129 }