Flares
I am not usually excited about the sketches I make in Processing: I mean, I do like them, but I always think they could have come out better, somehow.
This one, instead, I really like, and I’m quite happy about. It is the outcome of playing with an amazing particle systems library for Processing, called toxiclibs.
The whole idea is particularly simple: take a huge number of points, and connect them with springs with a short rest length, so to form a circular shape. Moreover, add an attraction towards the center, and start the animation with the particles far from the center. They’ll start bouncing under elastic forces and the attraction to the center until the circular shape becomes relatively small. The interesting choice here, from the visual perspective, was not to “represent” (or draw) the particles, but rather the springs themselves. One of the important feature of the animation, in this case, is to be able to slowly clean the screen from what has been drawn on it, otherwise you’ll end up with a huge white blob (not that pleasant, is it?). This is accomplished by the function fadescr, which goes pixel by pixel and slowly turns it to the value 0 in all of its colour channels, so basically fading the screen to black: all the >> and << binary operations are there to speed up things, which is crucial in these cases.
Why not using the celebrated "rectangle over the screen with low alpha"? Because it produces "ghosts", or graphic artifacts. See here for a nice description.
I really like the type of textures the animation develops, it gives me the idea of smooth silk, smoke or… flares. 😉
import toxi.physics2d.behaviors.*;
import toxi.geom.*;
VerletPhysics2D physics;
int N=2000;
VerletParticle2D[] particles=new VerletParticle2D[N];
VerletParticle2D center;
float r=200;
void setup(){
size(600,600);
background(0);
frameRate(30);
physics=new VerletPhysics2D();
center=new VerletParticle2D(new Vec2D(width/2,height/2));
center.lock();
AttractionBehavior behavior = new AttractionBehavior(center, 800, 0.01);
physics.addBehavior(behavior);
beginShape();
noFill();
stroke(255,5);
for (int i=0;i<particles.length;i++){
particles[i]=new VerletParticle2D(new Vec2D(width/2+(r+random(10,30))*cos(radians(i*360/N)),height/2+(r+random(10,30))*sin(radians(i*360/N))));
vertex(particles[i].x,particles[i].y);
}
endShape();
for (int i=0;i<particles.length;i++){
physics.addParticle(particles[i]);
};
for (int i=1;i<particles.length;i++){
VerletSpring2D spring=new VerletSpring2D(particles[i],particles[i-1],10,0.01);
physics.addSpring(spring);
};
VerletSpring2D spring=new VerletSpring2D(particles[0],particles[N-1],20,0.01);
physics.addSpring(spring);
};
void draw(){
physics.update();
beginShape();
noFill();
stroke(255,10);
for (int i=0;i<particles.length;i++){
vertex(particles[i].x,particles[i].y);
}
endShape();
if (frameCount % 3 == 0)
{
fadescr(0,0,0);
};
};
void fadescr(int r, int g, int b) {
int red, green, blue;
loadPixels();
for (int i = 0; i < pixels.length; i++) {
red = (pixels[i] >> 16) & 0x000000ff;
green = (pixels[i] >> 8) & 0x000000ff;
blue = pixels[i] & 0x000000ff;
pixels[i] = (((red+((r-red)>>8)) << 16) | ((green+((g-green)>>8)) << 8) | (blue+((b-blue)>>8)));
}
updatePixels();
}
Here’s a render of the animation
The audio has been generated in SuperCollider, using the following code
~b=Bus.audio(s,2);
File.openDialog("",{|path|
~path=path;
~buff=Buffer.read(s,path);
})
20.do({
~x=~x.add(Buffer.read(s,~path,rrand(0,~buff.numFrames)));
})
(
SynthDef(\playbuff,{arg out=0,in=0,r=1,p=0;
var sig=PlayBuf.ar(1,in,r,loop:1);
var env=EnvGen.kr(Env([0,1,1,0],[0.01,Rand(0.1,1),0.01]),gate:1,doneAction:2);
Out.ar(out,Pan2.ar(sig*env*3,pos:p));
}).add;
SynthDef(\synth,{arg out=0;
var sig=VarSaw.ar(90,0,0.5*LFTri.kr(0.1).range(1,1.2),mul:0.1)+VarSaw.ar(90.5,0,0.5*LFTri.kr(0.2).range(1,1.2),mul:0.1)+VarSaw.ar(90.8,0,0.5*LFTri.kr(0.3).range(1,1.2),mul:0.1);
sig=HPF.ar(sig,200);
sig=FreeVerb.ar(sig);
Out.ar(out,sig!2)
}).add;
SynthDef(\pad,{arg out=0,f=0;
var sig=Array.fill(3,{|n| SinOsc.ar(f*(n+1),0,0.05/(n+1))}).sum;
var env=EnvGen.kr(Env([0,1,1,0],[10,5,10]),gate:1,doneAction:2);
sig=LPF.ar(sig,3000);
Out.ar(~b,sig*env!2)
}).add;
SynthDef(\del,{arg out=0;
var sig=CombC.ar(In.ar(~b,2),0.4,TRand.kr(0.05,0.3,Impulse.kr(0.1)),7);
Out.ar(out,sig);
}).add;
)
y=Synth(\del);
x=Synth(\synth);
t=fork{
inf.do{
Synth(\pad,[\f:[48,52,53,55,57].choose.midicps]);
23.wait;}
};
q=fork{
inf.do{
Synth(\playbuff,[\in:~x[rrand(0,20).asInteger],\r:[-1,1].choose,\p:rrand(-0.5,0.5),\out:[0,~b].wchoose([0.92,0.08])]);
rrand(0.5,3).wait}
}
t.stop;
q.stop;
x.free;
y.free;
s.quit;
comment
Please Leave a Reply
TrackBack URL :
This is one of the best examples of generative art and using the physics lib. Thank you for that! I have been using this as a tool to get back into processing and also play with things like FFT and Amplitude from audio.
One thing I am having a hard time with is how to export this as video using frames. Any suggestions on that? What I export doesn’t seem to match what I am seeing on the screen. I even tried to create a buffer to copy the pixels into then save that, but no luck.
Any help would be greatly appreciated!
Cheers,
Donovan