Here's a version for 102+, without the unreliable antialiased points and with a few more comments. Note that the main difference between this and the black and white versions is that the colour hue is set according to the velocities of the pixels and each point.
Code:
int NUM_PARTICLES = 8192;
int NUM_ATTRACTORS = 8;
Particle[] particle;
Attractor[] attractor;
// how much speed to keep for particles, between frames
float damp = 0;
// how attractive the attractors are
float accel = 5000;
float buffer[]; // count particle locations for each pixel
float vxbuffer[]; // count particle x speed for each pixel
float vybuffer[]; // count particle y speed for each pixel
// for scaling the colour values (use mouse to set contrast)
float power = 0.4;
class Particle {
float x,y,vx,vy;
Particle() {
x = random(width);
y = random(height);
vx = random(-accel/2,accel/2);
vy = random(-accel/2,accel/2);
}
void step() {
for (int i = 0; i < attractor.length; i++) {
float d2 = sq(attractor[i].x-x) + sq(attractor[i].y-y);
if (d2 > 0.1) {
vx += accel * (attractor[i].x-x) / d2;
vy += accel * (attractor[i].y-y) / d2;
}
}
x += vx;
y += vy;
deposit(x,y,vx,vy);
vx *= damp;
vy *= damp;
}
}
void deposit(float x, float y, float vx, float vy) {
if (x >= 0 && x < width && y >= 0 && y < height) {
int index = (int)x+((int)y*width);
buffer[index]++;
vxbuffer[index] += vx;
vybuffer[index] += vy;
}
}
class Attractor {
float x,y;
Attractor() {
x = random(width);
y = random(height);
}
Attractor(float x, float y) {
this.x = x;
this.y = y;
}
}
void setup() {
size(600,600,P2D);
colorMode(HSB);
attractor = new Attractor[NUM_ATTRACTORS];
particle = new Particle[NUM_PARTICLES];
scatter();
// cut and pasted a favourite
attractor[0] = new Attractor(398.7855, 260.12);
attractor[1] = new Attractor(430.83215, 433.03412);
attractor[2] = new Attractor(257.5647, 411.41632);
attractor[3] = new Attractor(231.67877, 426.55072);
attractor[4] = new Attractor(127.697815, 317.10776);
attractor[5] = new Attractor(165.1979, 245.52707);
attractor[6] = new Attractor(332.07306, 104.05903);
attractor[7] = new Attractor(465.398, 162.21806);
}
void draw() {
for (int j = 0; j < particle.length; j++) {
particle[j].step();
}
set(frameCount%32,frameCount/32,#d0d0d0);
if (frameCount % 32 == 0) {
background(255);
float maxbuf = 0;
float maxspeed = 0.0;
for (int i = 0; i < buffer.length; i++) {
maxbuf = max(maxbuf,buffer[i]);
maxspeed = max(maxspeed,sqrt(sq(vybuffer[i])+sq(vxbuffer[i])));
}
loadPixels();
for (int i = 0; i < pixels.length; i++) {
if (buffer[i] > 0) {
float value = buffer[i]/maxbuf;
float b = 255.0-(255.0*pow(value,power));
float speed = sqrt(sq(vybuffer[i])+sq(vxbuffer[i]))/maxspeed;
float s = (255.0*pow(speed,power));
float angle = PI+atan2(vybuffer[i],vxbuffer[i]);
float h = 255.0-(angle*255.0/TWO_PI);
pixels[i] = color(h,s,b);
}
}
updatePixels();
}
}
void scatter() {
background(255);
println("attractors:");
for (int i = 0; i < attractor.length; i++) {
attractor[i] = new Attractor();
attractor[i].x = (width*0.25*cos(TWO_PI*(float)i/(float)attractor.length)) + (width/2) + random(-width*0.1,width*0.1);
attractor[i].y = (height*0.25*sin(TWO_PI*(float)i/(float)attractor.length)) + (height/2) + random(-height*0.1,height*0.1);
println("attractor["+i+"] = new Attractor("+attractor[i].x+", "+attractor[i].y+");");
}
println();
for (int i = 0; i < particle.length; i++) {
particle[i] = new Particle();
}
buffer = new float[width*height];
vxbuffer = new float[width*height];
vybuffer = new float[width*height];
}
void keyPressed() {
scatter();
}
void mousePressed() {
power = float(mouseX)/float(width);
}