Source of: Flow Field Drift, by Claude Fable 5
118 lines, 2 of them comments ยท 1,622 tokens, written in 17 seconds for $0.084
1 let particles = [];
2 let flowField = [];
3 let cols, rows;
4 const scl = 20;
5 let zoff = 0;
6 let hueBase;
7
8 function setup() {
9 createCanvas(windowWidth, windowHeight);
10 colorMode(HSB, 360, 100, 100, 100);
11 cols = floor(width / scl) + 1;
12 rows = floor(height / scl) + 1;
13 initSketch();
14 }
15
16 function initSketch() {
17 noiseSeed(floor(random(100000)));
18 hueBase = random(360);
19 background(0, 0, 5);
20 particles = [];
21 for (let i = 0; i < 1200; i++) {
22 particles.push(new Particle());
23 }
24 }
25
26 function draw() {
27 // very subtle fade for long trails
28 noStroke();
29 fill(0, 0, 5, 2);
30 rect(0, 0, width, height);
31
32 // update flow field
33 let yoff = 0;
34 for (let y = 0; y < rows; y++) {
35 let xoff = 0;
36 for (let x = 0; x < cols; x++) {
37 const angle = noise(xoff, yoff, zoff) * TWO_PI * 4;
38 const v = p5.Vector.fromAngle(angle);
39 v.setMag(0.4);
40 flowField[x + y * cols] = v;
41 xoff += 0.08;
42 }
43 yoff += 0.08;
44 }
45 zoff += 0.0025;
46
47 for (const p of particles) {
48 p.follow(flowField);
49 p.update();
50 p.edges();
51 p.show();
52 }
53 }
54
55 class Particle {
56 constructor() {
57 this.pos = createVector(random(width), random(height));
58 this.prev = this.pos.copy();
59 this.vel = createVector(0, 0);
60 this.acc = createVector(0, 0);
61 this.maxSpeed = random(1.5, 3.5);
62 this.hueOffset = random(-40, 40);
63 this.life = random(100, 400);
64 }
65
66 follow(field) {
67 const x = constrain(floor(this.pos.x / scl), 0, cols - 1);
68 const y = constrain(floor(this.pos.y / scl), 0, rows - 1);
69 const force = field[x + y * cols];
70 if (force) this.acc.add(force);
71 }
72
73 update() {
74 this.vel.add(this.acc);
75 this.vel.limit(this.maxSpeed);
76 this.prev = this.pos.copy();
77 this.pos.add(this.vel);
78 this.acc.mult(0);
79 this.life--;
80 if (this.life <= 0) this.respawn();
81 }
82
83 respawn() {
84 this.pos = createVector(random(width), random(height));
85 this.prev = this.pos.copy();
86 this.vel.mult(0);
87 this.life = random(100, 400);
88 }
89
90 edges() {
91 let wrapped = false;
92 if (this.pos.x > width) { this.pos.x = 0; wrapped = true; }
93 if (this.pos.x < 0) { this.pos.x = width; wrapped = true; }
94 if (this.pos.y > height) { this.pos.y = 0; wrapped = true; }
95 if (this.pos.y < 0) { this.pos.y = height; wrapped = true; }
96 if (wrapped) this.prev = this.pos.copy();
97 }
98
99 show() {
100 const speed = this.vel.mag();
101 const h = (hueBase + this.hueOffset + speed * 25) % 360;
102 const b = map(speed, 0, this.maxSpeed, 50, 100);
103 stroke((h + 360) % 360, 70, b, 12);
104 strokeWeight(1);
105 line(this.prev.x, this.prev.y, this.pos.x, this.pos.y);
106 }
107 }
108
109 function mousePressed() {
110 initSketch();
111 }
112
113 function windowResized() {
114 resizeCanvas(windowWidth, windowHeight);
115 cols = floor(width / scl) + 1;
116 rows = floor(height / scl) + 1;
117 initSketch();
118 }