Processing Code Remix: The Joy of Modifying Existing Sketches
This is a modified version of a published Processing sketch where particles appear to autonomously sort themselves into color groups. Below, I’ll share the source code and the key insights from my creative process.
Original Processing Code for Modification
This example was created by modifying the code that ひさだん(@hisadan) san had published on Twitter.
//#Processing
— ひさだん (@hisadan) April 20, 2025
int i,s,n=999;
float a[]=new float[n*2],x,y,d,r,e,f;
void setup(){
size(800,800);
for(i=0;i<n;i++){
d=random(250);
r=random(TAU);
a[i]=d*sin(r);
a[i+n]=d*cos(r);}
background(0);
colorMode(HSB);}
void draw(){
fill(0,2);
square(0,0,800);
for(i=0;i<n;i++){
//cont. pic.twitter.com/uFrvtnXZ6p
I was captivated by its organic, autonomous movement, which inspired me to create my own version.
Key Points of Reading Source Code in Creative Coding Way
First, I refactored the minified source code from the original tweet to make it more readable.
/**
* Ref. https://x.com/hisadan/status/1913976946687422638
*/
int i, s, n = 999;
float a[] = new float[n * 2];
float x, y, d, r, e, f;
void setup() {
size(800,800);
colorMode(HSB);
background(0);
// locate the particles randomly
// a[i] as x axis, a[i + n] as y axis
for (i = 0; i < n; i++) {
d = random(250);
r = random(TAU);
a[i] = d * sin(r);
a[i + n] = d * cos(r);
}
}
void draw() {
// translucent for afterimage
fill(0,2);
square(0, 0, 800);
// calculation for particle move
for(i = 0; i < n; i++) {
for(s = 0; s < n; s++) {
x = 0;
y = 0;
d = dist(a[i], a[i + n], a[s], a[s + n]);
e = (a[s] - a[i]) / d / d;
f = (a[s + n] - a[i + n]) / d / d;
if (i != s) {
if (i % 2 == s % 2) {
x += d > 100 ? -e : e;
y += d > 100 ? -f : f;
} else {
x += d > 50 ? e : -e;
y += d > 50 ? f : -f;
}
}
a[i] += x;
a[i + n] += y;
}
// color with distance from center of sketch
stroke(mag(a[i], a[i + n]), 255, 255);
point(a[i] + 400, a[i + n] + 400);
}
}
I've written some comments on source code.
A basic grasp of the logic is enough to get started. You’ll naturally understand the finer details as you begin to experiment and tweak the values.
Key Points for Creative Coding Style Modifications
Reading code all day can be tedious, so let’s dive straight into the 'creative coding' way—start tweaking things first, and let the understanding follow through experimentation.
When making modifications, if you start with simple changes such as changing initial values, you will smoothly understand how the program works. By the time you move on to more difficult modifications such as changing movements, you will be able to see what the key parts of the code are and where the appeal is created.
Try to Change the Number of Particles
int i, s, n = 999;
Try changing the number of particles from 999 to something extreme, like 10,000 or 10, and see how the behavior shifts.
Try to Change the Way to Draw
// translucent for afterimage
fill(0,2);
square(0, 0, 800);
Try opaque or transparent.
// color with distance from center of sketch
stroke(mag(a[i], a[i + n]), 255, 255);
point(a[i] + 400, a[i + n] + 400);
For example.
描画を変えてみる 🙂
— deconbatch (@deconbatch) April 22, 2025
・計算前後の座標間に線をひく
・その線の長さに応じて色を付ける
・線が短いほど太く#processing #creativecoding pic.twitter.com/D3XatxtR8t
Try to Change the Moving of Particles
d = dist(a[i], a[i + n], a[s], a[s + n]);
e = (a[s] - a[i]) / d / d;
f = (a[s + n] - a[i + n]) / d / d;
This is one of the most important points because it is the part that determines the amount of change in position. It's both a rewarding and challenging aspect of the modification.
if (i % 2 == s % 2) {
x += d > 100 ? -e : e;
y += d > 100 ? -f : f;
} else {
x += d > 50 ? e : -e;
y += d > 50 ? f : -f;
}
This is a section where the logic is clear, yet the resulting behavior is wonderfully unpredictable.
So, I guess it must be a key point of this work!
Example modification of this part.
Processing Code of Modified Work
Here is the Processing code of my modified work.
/**
* Processing Example Code: particles seemingly autonomously sort themselves into different color clusters.
* Ref. https://x.com/hisadan/status/1913976946687422638
*
* @author @deconbatch
* @version 0.1
* @license CC0 1.0 https://creativecommons.org/publicdomain/zero/1.0/deed.ja
* Processing 4.3.3
* 2025.05.22
*/
void setup() {
int frmMax = 24 * 10; // 24fps x 10sec
int pNumMax = 1800;
int pClass = 6;
float pIniRad = 80.0;
float pDistMax = 220.0;
float distMag = 12.0;
float baseHue = random(120.0, 300.0);
PVector curP[] = new PVector[pNumMax];
PVector prvP[] = new PVector[pNumMax];
size(720, 720);
colorMode(HSB, 360.0, 100.0, 100.0, 100.0);
for(int pCnt = 0; pCnt < pNumMax; pCnt++){
float r = random(pIniRad);
float t = random(TAU);
curP[pCnt] = new PVector(r * cos(t), r * sin(t));
prvP[pCnt] = new PVector(curP[pCnt].x, curP[pCnt].y);
}
translate(width * 0.5, height * 0.5);
rectMode(CENTER);
blendMode(BLEND);
background(baseHue, 40.0, 10.0, 100.0);
// iterate for the number of animation frames
for (int frm = 0; frm < frmMax; frm++) {
// for afterimage
blendMode(BLEND);
fill(baseHue, 40.0, 10.0, 20.0);
noStroke();
rect(0.0, 0.0, width, height);
// calc position of points
for (int mCnt = 0; mCnt < pNumMax; mCnt++) {
for (int oCnt = 0; oCnt < pNumMax; oCnt++) {
if (mCnt != oCnt) {
float pDist = 1.0 + PVector.dist(prvP[mCnt], prvP[oCnt]);
PVector pDiv = PVector.div(PVector.mult(PVector.sub(prvP[mCnt], prvP[oCnt]), distMag), pow(pDist, 2));
if (mCnt % pClass == oCnt % pClass) {
curP[mCnt].add(pDiv.mult((pDistMax - pDist) / pDistMax));
} else {
if (pDist > pDistMax) {
curP[mCnt].sub(pDiv);
} else {
curP[mCnt].add(pDiv);
}
}
}
}
}
// calc min/max distance
float drMin = 99.0;
float drMax = -drMin;
for (int mCnt = 0; mCnt < pNumMax; mCnt++) {
float dR = PVector.dist(curP[mCnt], prvP[mCnt]) / pDistMax;
drMax = max(drMax, dR);
drMin = min(drMin, dR);
}
// draw lines
blendMode(SCREEN);
noFill();
for (int mCnt = 0; mCnt < pNumMax; mCnt++) {
float distRatio = map(PVector.dist(curP[mCnt], prvP[mCnt]) / pDistMax, drMin, drMax, 0.0, 1.0);
float hueRatio = (mCnt % pClass) * 1.0 / pClass;
float satRatio = floor(0.5 + mCnt / (pNumMax * 0.3)) / 3.0;
float briRatio = floor(0.5 + mCnt / (pNumMax * 0.2)) / 5.0;
stroke((baseHue + 90.0 + hueRatio * 120.0 + distRatio * 90.0) % 360.0,
10.0 + satRatio * (1.0 - distRatio) * 80.0,
20.0 + briRatio * 30.0 + distRatio * 30.0,
100.0
);
strokeWeight(constrain(6.0 * (1.0 - distRatio), 1.0, 6.0));
line(prvP[mCnt].x, prvP[mCnt].y, curP[mCnt].x, curP[mCnt].y);
}
// carry over
cur2prv(curP, prvP);
// make image files for animation frames
saveFrame("frames/" + String.format("%04d", frm) + ".png");
}
exit();
}
/**
* deep copy from c to p
*/
void cur2prv(PVector[] c, PVector[] p){
for(int pCnt = 0; pCnt < p.length; pCnt++){
p[pCnt].x = c[pCnt].x;
p[pCnt].y = c[pCnt].y;
}
}
Points of Modification
I decided to manage the particles with PVector.
curP[pCnt] = new PVector(r * cos(t), r * sin(t));
I separated arrays for position calculations and for the results of previous calculations, so that the results of one particle's calculations do not affect the position calculations of other particles.
PVector curP[] = new PVector[pNumMax]; // current
PVector prvP[] = new PVector[pNumMax]; // previous
I implemented a change in the particle's position calculation formula, using the condition '(mCnt % pClass == oCnt % pClass)'.
float pDist = 1.0 + PVector.dist(prvP[mCnt], prvP[oCnt]);
PVector pDiv = PVector.div(PVector.mult(PVector.sub(prvP[mCnt], prvP[oCnt]), distMag), pow(pDist, 2));
if (mCnt % pClass == oCnt % pClass) {
curP[mCnt].add(pDiv.mult((pDistMax - pDist) / pDistMax));
} else {
if (pDist > pDistMax) {
curP[mCnt].sub(pDiv);
} else {
curP[mCnt].add(pDiv);
}
}
Because of the long computation time required, I did not make real-time animation with draw() function. Instead, I've decided to use a method that writes out a still image for each frame to generate a movie.
Additional Ideas for Modification
Even with the code as it is, you can get various results by changing the initial values of the variables.
int pNumMax = 1800;
int pClass = 6;
float pIniRad = 80.0;
float pDistMax = 220.0;
float distMag = 12.0;
It would be interesting to get interesting results if these variables were not fixed values, but rather values that change as the frame progresses.
Conclusion
My version ended up feeling more like a simulation than a traditional art piece.
It looks as if the particles by color move and gather autonomously, but in reality it is better to say that the color is changed by the group of particles that gather.
float hueRatio = (mCnt % pClass) * 1.0 / pClass;
To tell the truth, I have no idea why both the original code and the modified code work like this.
This shows that you don't need a deep technical mastery to start creating. By experimenting with existing code, you can craft something unique—and that’s the true essence of creative coding.
Without the underlying code this time, this work would not have been created.
Thank you, ひさだん(@hisadan) san for publishing this interesting example code.
