class Attractor
{
Vec pos = new Vec(0,0,0); // current point position of attractor NOT A VECTOR
Vec dir; // movement vector for attractor
int atrType; // national or foreigner attractor type
int xdirection = 2; // Left or Right
int ydirection = 2; // Up or Down
int numPart = 0; // number of particles engaged to static, incremented in particle class
int snapCounter=0; // counts time while particle outside of moving aoe for snapping to particle and changing vector, incremented in particle class
int snapCount=0; // number of snaps a static attractor has caused, helps determine moving atr deflection
int totalSnapCount=0; // total number of snaps moving atr has experienced, helps determine when a pairing leaves the sketch and is "reborn"
int rebornCounter=0; // counts how long a pair has moved off screen before resetting it to start conditions
boolean particle2moving = true; // swaps attraction between particle and moving attractor
boolean moving; // true = moving, false = static
float aoeAtr; // max distance for child particle to move in before moving particle changes vector
float vel; // velocity of movement
float [] staticSnapCount = new float[NUM_STATIC]; // each moving atr stores an array associated to number of static atr
color c; // attractor color
color c1, c2; // lerping colors
Attractor(float X, float Y, Vec startDir, int moveType, float _vel)
{
pos.x = X;
pos.y = Y;
dir = startDir;
atrType = moveType;
vel=_vel;
switch(atrType)
{
case 0: // national
aoeAtr=350; // distance after which moving atr (adult) follows particle (child)
c = color(255,0,0); // red
moving=true;
c1= color(200,0,0); // dark red
c2= color(255,255,25); //yellow
break;
case 1: // foreign
aoeAtr=160; // distance after which moving atr (adult) follows particle (child)
c = color(0,255,0); // green
moving=true;
c1= color(160,0,160); // dark purple
c2= color(0,255,255); //aquamarine
break;
case 2: // static
aoeAtr=0; // static, no such aoe variable
c = color(0);
moving=false;
break;
}
for(int j=0; j<staticSnapCount.length;j++) // give staticAttracted array null values
{
staticSnapCount[j] = 0;
}
}
///////////////////////////// draw attractor (desired color, moving or not)
void drawAtr()
{
if(visualSwitch==false)
{
stroke(c);
line(pos.x-4, pos.y, pos.x+4, pos.y);
line(pos.x, pos.y-4, pos.x, pos.y+4);
}
if(moving==false)
{
noFill();
stroke(0,numPart*20);
rectMode(CENTER);
rect(pos.x, pos.y,8+numPart*5,8+numPart*5);
}
if(moving==true && visualSwitch==false)
{
if(atrType==0)
{
noFill();
stroke(c,66);
ellipse(pos.x, pos.y, aoeAtr, aoeAtr);
}
else if(atrType==1)
{
noFill();
stroke(c,66);
ellipse(pos.x, pos.y, aoeAtr, aoeAtr);
}
}
}
///////////////////////////// moving attractor on vector, bounce off edges, and deflect off statics
void stepAtr()
{
for(int i=0;i<staticAtr.length;i++)
{
float d1=pos.distance(staticAtr[i].pos);
if(d1<aoeAtr/2 && staticSnapCount[i]>0)
{
dir.normalize(); // normalize movement vector, just a direction now
float ny= (pos.y-staticAtr[i].pos.y)/d1; // normalize vector to static atr
float nx= (pos.x-staticAtr[i].pos.x)/d1; // ^^
float a1=atan2(nx,ny); // calculate angle to static atr
float a2=atan2(dir.x, dir.y); // calculate angle of movement
//println(a2);
float newA=a1-a2; // difference between two angles
if(newA>=0)
{
a2 += radians(staticSnapCount[i]*1);
}
else if(newA<0)
{
a2 -= radians(staticSnapCount[i]*1);
}
dir.rotateVec(a2);
}
}
pos.x += dir.x*vel;
pos.y += dir.y*vel;
if (pos.x > width || pos.x < 0)
{
if(totalSnapCount<3) dir.x *= -1;
else rebornCounter++;
}
if (pos.y > height || pos.y < 0)
{
if(totalSnapCount<3) dir.y *= -1;
else rebornCounter++;
}
}
///////////////////////////// when snap occurs and moving atr follows particle
void attractAtr(Particle pW)
{
float d2=pos.distance(pW.pos); // always calculate associated child particle distance
float tempX = pW.pos.x;
float tempY = pW.pos.y;
pos.x += 1.8 * (tempX-pos.x) / d2; // smaller divider number stronger force of attraction
pos.y += 1.8 * (tempY-pos.y) / d2;
if(visualSwitch==false)
{
stroke(255,255,0);
line(pos.x, pos.y, pW.pos.x, pW.pos.y);
}
}
void drawSnap(int i)
{
for(int k=0; k<10; k++)
{
stroke(0, (k+1)*10);
strokeWeight(totalSnapCount+1);
fill(255,66);
ellipse(pos.x, pos.y, aoeAtr/2-(k*(aoeAtr/20)), aoeAtr/2-(k*(aoeAtr/20)));
strokeWeight(1);
}
stroke(255);
strokeWeight(2);
line(pos.x, pos.y, p[i].pos.x, p[i].pos.y);
strokeWeight(1);
}
///////////////////////////// increments values for which static atr have caused snaps
void updateStaticSnap(int [] update, float amount)
{
for(int i=0; i<update.length; i++)
{
if(update[i]>=0) staticSnapCount[update[i]] += amount;
}
//println(staticSnapCount);
}
}
class Vislines
{
float x1,y1,x2,y2; // start and end coords
color c; // stroke color
int a; // alpha value
Vislines(float X1, float Y1, float X2, float Y2, int A, color C)
{
x1=X1;
y1=Y1;
x2=X2;
y2=Y2;
c=C;
a=A;
}
void drawLine()
{
stroke(c,a);
line(x1,y1,x2,y2);
}
}
class Particle
{
//Vislines [] particleLines = new Vislines[500]; // array of moving attractors (adult)
Vec partMoveDir = new Vec(0,0,0); // sets wandering vector when part/atr snap
float w,h; // width and height
float t=0; // translate smoothing
color lineColor = color(0); // line color
int lineAlpha = 66; // line alpha
color c3= color (130,255,25); //green
color c4= color (0,255,255); //blue
boolean runOnce = true; // sets values at moment of particle to movingatr swap
boolean forgetting =false; // whether particle is looking for static atr or not
float xdirection = 1; // Left or Right
float ydirection = 1; // Top to Bottom
float accel=1; // acceleration value
float xP, yP; // start position
float aoeAtr=200; // max distance to engage with attractor
int maxStaticCount=3; // max # of static atr a particle will be attracted too
int staticCounter=0; // # of static attractors within particles aoe
int [] staticAttracted = new int[maxStaticCount]; // keeps track of which static atr particle is attracted too
float partMoveX=0; // determines wandering vector
float partMoveY=0; // ^^
int partStart; // determines which screen edge particle starts on
float ty,tx;
float angle = 0.0;
float speed = .33;
Vec pos = new Vec(0,0,0); // current point position of particle NOT A VECTOR
Vec v = new Vec(0,0,0); // current vector
Particle(float X, float Y, float W, float H, int _partStart) //constructor
{
pos.x=X;
pos.y=Y;
w=W;
h=H;
partStart=_partStart;
/*for(int i=0; i<particleLines.length;i++) // give particleLine array null values
{
particleLines[i]=new Vislines(0,0,0,0,lineAlpha,lineColor);
}*/
}
//////////////////////////////////////
void step(Attractor [] a, Attractor b) // when particle is following moving and engaging statics
{
float d1 = 0; // distance from static to particle
float d2 = 0; //distance from associated moving to particle
float ax = 0;
float ay = 0;
for(int j=0; j<staticAttracted.length;j++) // give staticAttracted array null values
{
staticAttracted[j] = -1;
}
////////////////////// movement due to static attractor////////////////////////////
for (int i=0;i<a.length;i++)
{
if(staticCounter<3) // only check for static attractors if particle is engaged with less than 2
{
d1=pos.distance(a[i].pos);
if (d1 < aoeAtr/2 && a[i].numPart < 6 ) //if distance is within child particle aoe and less than X particles are engaged at that attractor
{
a[i].numPart++;
if(d1 > (aoeAtr/2)*.75) // if distance is 75% of aoe radius or more, slows down particle movement at first
{
pos.x +=(a[i].pos.x-pos.x) * d1/2000;
pos.y +=(a[i].pos.y-pos.y) * d1/2000;
}
else
{
pos.x += (a[i].pos.x-pos.x) * d1/(1000-a[i].numPart*50); // smaller divider number stronger force of attraction
pos.y += (a[i].pos.y-pos.y) * d1/(1000-a[i].numPart*50); // as particles accumulate, force stronger
}
if (visualSwitch==true)
{
lineColor=color(0);
lineAlpha=33;
stroke(lineColor, lineAlpha);
line(a[i].pos.x, a[i].pos.y, pos.x, pos.y); // line to static
//prepLineArray(a[i].pos.x, a[i].pos.y);
}
else if (visualSwitch==false)
{
stroke(0);
line(pos.x,pos.y, a[i].pos.x, a[i].pos.y);
}
if(b.snapCounter > SNAP_TIME-1) staticAttracted[staticCounter] = i; // loads all engaged static atr's array position just before snap
staticCounter++;
}
}
}
////////////////////// movement due to moving attractor////////////////////////////
d2=pos.distance(b.pos); // always calculate associated parent attractor after all statics checked
if (d2 < b.aoeAtr/2) // condition 1x - if distance smaller than parent aoe
{
if (d2 < aoeAtr/2) // condition 1A - if d2 smaller than particle aoe
{
pos.x += (b.pos.x-pos.x) * d2/1000; // smaller divider number stronger force of attraction
pos.y += (b.pos.y-pos.y) * d2/1000;
if(staticCounter==0) // if only engaged by moving attractor and no statics, sinusoidal movement
{
curveTrans(100/d2);
}
if (visualSwitch==true)
{
lineColor=lerpColor(b.c1,b.c2,d2/(aoeAtr/2));
lineAlpha=80;
stroke(lineColor, lineAlpha);
line(b.pos.x, b.pos.y, pos.x, pos.y);
//prepLineArray(b.pos.x, b.pos.y);
}
else if (visualSwitch==false)
{
stroke(b.c);
line(pos.x,pos.y, b.pos.x, b.pos.y);
}
}
else if(d2 > aoeAtr/2) // condition 1B - if d2 larger than particle aoe (only possible with national pairs)
{
if (visualSwitch==true)
{
lineColor=lerpColor(b.c2,c3,d2/(b.aoeAtr/2));
lineAlpha=80;
stroke(lineColor, lineAlpha);
line(b.pos.x, b.pos.y, pos.x, pos.y);
//prepLineArray(b.pos.x, b.pos.y);
}
else if (visualSwitch==false)
{
stroke(0,130,255);
//line(pos.x,pos.y, b.pos.x, b.pos.y);
}
}
}
else if(d2 > b.aoeAtr/2) // condition 2x - if d2 outside of parent aoe
{
if(b.particle2moving==true) b.snapCounter++; // only increment when particle following moving
if (d2 < aoeAtr/2) // condition 2A - if d2 smaller than child aoe (only possible with foreigner pairs)
{
pos.x += (b.pos.x-pos.x) * d2/1000; // smaller divider number stronger force of attraction
pos.y += (b.pos.y-pos.y) * d2/1000;
if(staticCounter==0 && d2<aoeAtr/2 && forgetting==false) // if only engaged by moving attractor (parent), move somewhat freely
{
curveTrans(100/d2);
}
if (visualSwitch==true)
{
lineColor=lerpColor(b.c2,c3,d2/(b.aoeAtr/2));
lineAlpha=80;
stroke(lineColor, lineAlpha);
line(b.pos.x, b.pos.y, pos.x, pos.y);
//prepLineArray(b.pos.x, b.pos.y);
}
else if (visualSwitch==false)
{
stroke(0,130,255);
line(pos.x,pos.y, b.pos.x, b.pos.y);
}
}
else if(d2 > aoeAtr/2) // condition 2B - if d2 larger than particle aoe (only possible with national pairs)
{
if (visualSwitch==true)
{
lineColor=lerpColor(c3,c3,d2/(b.aoeAtr/2));
lineAlpha=80;
stroke(lineColor, lineAlpha);
line(b.pos.x, b.pos.y, pos.x, pos.y);
//prepLineArray(b.pos.x, b.pos.y);
}
else if (visualSwitch==false)
{
stroke(0,130,255);
line(pos.x,pos.y, b.pos.x, b.pos.y);
}
}
}
if (visualSwitch==true)
{
//for(int i=0; i<particleLines.length;i++) particleLines[i].drawLine(); // draw array of lines
}
staticCounter=0;
}
/////////////////////////////////////
void setWanderVec() // wandering vector
{
while(partMoveX < .35 && partMoveX > -.3) partMoveX=random(-1,1); // ensures wandering vector is not too "slow"
while(partMoveY < .35 && partMoveY > -.3) partMoveY=random(-1,1); // ^^
partMoveDir = new Vec(partMoveX,partMoveY,0.0); // gives wandering particle new direction vector at moment of snapping
}
//////////////////////////////////////
void wandering(Attractor c) // when particle wandering
{
float d3=pos.distance(c.pos); // always calculate associated particle/moving atr distance
pos.x += partMoveDir.x*xdirection;
pos.y += partMoveDir.y*ydirection;
if (pos.x > width || pos.x < 0) {
xdirection *= -1;
}
if (pos.y > height || pos.y < 0) {
ydirection *= -1;
}
if(d3>aoeAtr/2 && visualSwitch==true)
{
stroke(255);
strokeWeight(1);
line(c.pos.x, c.pos.y, pos.x, pos.y);
}
if(d3<aoeAtr/2) // if moving atr back within aoe
{
if (visualSwitch==true)
{
lineColor=lerpColor(c3,c.c2,d3/(aoeAtr/2));
lineAlpha=80;
line(c.pos.x, c.pos.y, pos.x, pos.y);
//prepLineArray(c.pos.x, c.pos.y);
}
else if (visualSwitch==false)
{
stroke(255,255,0);
line(pos.x,pos.y, c.pos.x, c.pos.y);
}
}
if(d3<aoeAtr/6) // if within 1/3 of particle aoe radius reset relation
{
c.dir.x=partMoveDir.x; // sets moving atr vector to particle vector
c.dir.y=partMoveDir.y;
c.vel=random(1.5,2.0);
partMoveX=0;
partMoveY=0;
forgetting=true; // sets particle to not look for statics
}
if (visualSwitch==true)
{
//for(int i=0; i<particleLines.length;i++) particleLines[i].drawLine(); // draw array of lines
}
}
/* /////////////////////////////////////
void prepLineArray(float x2, float y2)
{
for(int i=1;i<particleLines.length;i++)
{
float fadeNumber=particleLines.length*.33; //% of line array fades out
if(i < int(fadeNumber))
{
float alphaChange = 100/fadeNumber; //amount to fade per increment
float actualAlpha = i*alphaChange; // oldest line, i=1, faded the most to newest line, i=fadeNumber, the least
particleLines[i].a = int(actualAlpha);
}
particleLines[i-1]=particleLines[i];
}
particleLines[particleLines.length-1]= new Vislines(pos.x, pos.y, x2, y2, lineAlpha, lineColor);
}*/
//////////////////////////////////////
void curveTrans(float radius)
{
angle+=speed;
float sinval=sin(angle);
float ty = sinval * radius;
pos.y+=ty;
float cosval=cos(angle);
float tx = cosval * radius;
pos.x+=tx;
}
//////////////////////////////////////
void drawParticle()
{
strokeWeight(1);
stroke(0,0,0,33);
noFill();
ellipse(pos.x, pos.y,aoeAtr,aoeAtr);
noStroke();
fill(0);
ellipse(pos.x, pos.y,w,h);
}
}
//import processing.pdf.*;
int NUM_PAIRS=25; // number of (child) particle and (adult) moving attractors pairs
int NUM_STATIC=30; // number of static attractors
int SNAP_TIME=80; // # of frames until part/moving atr snap occurs
int FORGET_TIME=60; // # of frames particle "forgets" static atr
int REBIRTH_TIME=60; // # frames until part/moving atr pair reset to start
boolean visualSwitch = true ; // swaps between behavior and resultant visualizations
float startLx, startLy; // left edge gate
float startBx, startBy; // bottom edge gate
float startRx, startRy; // right edge gate
float startTx, startTy; // top edge gate
Particle [] p = new Particle[NUM_PAIRS]; // array of particles (child)
Attractor [] movingAtr = new Attractor[NUM_PAIRS]; // array of moving attractors (adult)
Attractor [] staticAtr = new Attractor[NUM_STATIC]; // array of static attractors (non-adult)
Attractor [] nullAtr = new Attractor[0]; // array of null statics for particle in "forgetting" phase
Vec nullDir=new Vec(0,0,0); // a no movement vector for static attractors
Vec moveDir[]= new Vec[NUM_PAIRS]; // initial movement vector for moving attractors
int forgettingCounter=0; // used for how long particle "forgets" to look for static atr
int counter=0;
void setup()
{
size(800,800);
background(255);
smooth();
//beginRecord(PDF, "everything.pdf");
setGateways(); // sets random start locations on each edge of screen
setParticles(0, p.length); // creates particles in Particle class arreay
setMoveAttractors(0, movingAtr.length); // creates movingattractors in Attractor class array
setStaticAttractors(); // creates static attractos in Attractor class array
}
void draw()
{
if(visualSwitch == false) background(255);
else if(visualSwitch == true)
{
counter = counter+1;
int redrawer = counter%15; // every 4 frames
if (redrawer<=0)
{
fill(255,12);
noStroke();
rect(0,0,width,height);
}
}
for(int i=0; i<NUM_PAIRS; i++)
{
if(visualSwitch == false) p[i].drawParticle();
if(movingAtr[i].particle2moving==true) // if particle following moving atr
{
movingAtr[i].stepAtr();
p[i].step(staticAtr, movingAtr[i]);
}
else if(movingAtr[i].particle2moving==false) // if moving atr following particle
{
if(p[i].forgetting==false) // if particle is wandering
{
if(p[i].runOnce==true) // set initial wander vector only once
{
p[i].setWanderVec();
p[i].runOnce=false; // wont run again until reset
}
movingAtr[i].snapCounter=0; // reset
movingAtr[i].attractAtr(p[i]); // moving atr to particle
p[i].wandering(movingAtr[i]); // sets forgetting boolean
}
else if(p[i].forgetting==true) // if particle disregarding static attractors (forgetting)
{
movingAtr[i].stepAtr(); // moving atr normal
p[i].step(nullAtr, movingAtr[i]); // all statics null to particle
forgettingCounter++;
if(forgettingCounter>FORGET_TIME) // number of frames in forgetting phase until particle/moving atr reset to initial condition
{
movingAtr[i].particle2moving=true; // particle following moving atr
p[i].runOnce=true; // reset for when next snap occurs
p[i].forgetting=false; // particle is looking for static atr
forgettingCounter=0; // reset counter for next time
}
}
}
if(movingAtr[i].snapCounter > SNAP_TIME) // SNAP occurs after a X amount of time
{
if(visualSwitch==true) // snapping visual
{
movingAtr[i].drawSnap(i);
}
movingAtr[i].particle2moving=false; // moving atr follows particle now
movingAtr[i].totalSnapCount++;
for(int j=0; j<movingAtr.length; j++) // tests against all other moving atr
{
float d1 = movingAtr[i].pos.distance(movingAtr[j].pos);
if(d1<movingAtr[i].aoeAtr/2) // if within moving atr aoe
{
if(j!=i && movingAtr[i].atrType==movingAtr[j].atrType) // update other same type of moving atr by fraction
{
movingAtr[j].updateStaticSnap(p[i].staticAttracted,.25); // updates moving atr's static atr deflection array
if(visualSwitch==true)
{
fill(255,90);
stroke(0,90);
ellipse(movingAtr[j].pos.x, movingAtr[j].pos.y,15,15);
}
}
else if(j==i) // update current moving atr fully
{
movingAtr[j].updateStaticSnap(p[i].staticAttracted,1.0); // updates moving atr's static atr deflection array
}
}
}
}
if(movingAtr[i].rebornCounter>REBIRTH_TIME) //resets part/moving atr pair
{
setParticles(i, i+1);
setMoveAttractors(i, i+1);
}
}
if(visualSwitch == false) drawAtr();
for(int i=0; i<staticAtr.length; i++) staticAtr[i].numPart=0;
}
void setGateways() // start locations
{
startLx=0; // left edge
startLy=random(height*.1, height-(height*.1));
startBx=random(width*.1, width-(width*.1)); // bottom edge
startBy=height;
startRx=width; // right edge
startRy=random(height*.1, height-(height*.1));
startTx=random(width*.1, width-(width*.1)); // top edge
startTy=0;
}
void setParticles(int forStart, int forUntil)
{
for(int i=0; i<p.length; i++)
{
float partStart=random(4);
if(partStart<1) partStart=0;
if(partStart>=1 && partStart<2) partStart=1;
if(partStart>=2 && partStart<3) partStart=2;
if(partStart>=3 && partStart<4) partStart=3;
switch(int(partStart))
{
case 0:
p[i]= new Particle(startLx,startLy,6,6,0);
break;
case 1:
p[i]= new Particle(startBx,startBy,6,6,1);
break;
case 2:
p[i]= new Particle(startRx,startRy,6,6,2);
break;
case 3:
p[i]= new Particle(startTx,startTy,6,6,3);
break;
}
}
}
/*void keyPressed()
{
if (key == 'q')
{
endRecord();
exit();
}
}*/
void setMoveAttractors(int forStart, int forUntil)
{
float atrType=0; // 0 = national, 1 = foreigner, moving attractor, randomly decided in setup
/////// MOVING
for (int i=0; i<movingAtr.length; i++)
{
atrType=random(0,1); // randomly generate either foreign or national moving attractors
if(atrType<=.5) atrType=0;
else atrType=1;
switch(p[i].partStart)
{
case 0:
moveDir[i] = new Vec(random(.5,2.0),random(-1.5,1.5),0.0); // random starting vector of movement;
break;
case 1:
moveDir[i] = new Vec(random(-1.5,1.5),random(-2.0,-.5),0.0); // random starting vector of movement;
break;
case 2:
moveDir[i] = new Vec(random(-2.0,-.5),random(-1.5,1.5),0.0); // random starting vector of movement;
break;
case 3:
moveDir[i] = new Vec(random(-1.5,1.5),random(.5,2.0),0.0); // random starting vector of movement;
break;
}
movingAtr[i]=new Attractor(p[i].pos.x, p[i].pos.y, moveDir[i], int(atrType), random(1.0,2.0));
}
}
void setStaticAttractors()
{
///////// STATIC
for (int i=0; i<staticAtr.length; i++)
{
staticAtr[i]=new Attractor(random(25,width-25), random(25,height-25), nullDir,2,0);
for(int j=0; j<i; j++) // tests to make sure no statics are drawn within X distance of any other
{
if(staticAtr[i].pos.distance(staticAtr[j].pos) < 50) i--;
}
}
}
void drawAtr()
{
for (int i=0; i<movingAtr.length; i++) movingAtr[i].drawAtr();
for (int i=0; i<staticAtr.length; i++) staticAtr[i].drawAtr();
}
void mouseClicked() // true shows resultant visualization, false behavior visualization, swaps on mouseclick
{
if(visualSwitch==true) visualSwitch=false;
else if (visualSwitch==false) visualSwitch=true;
setup();
}
class Vec
{
float x,y,z;
Vec(float X, float Y, float Z)
{
x=X;
y=Y;
z=Z;
}
void add(Vec v)
{
x+=v.x;
y+=v.y;
z+=v.z;
}
void subtract(Vec v)
{
x-=v.x;
y-=v.y;
z-=v.z;
}
// multiply function, divide by using number below 1
void multiply(float m)
{
x*=m;
y*=m;
z*=m;
}
//distance (to this vector) from calling vector)
float distance(Vec v)
{
float d= sqrt(pow(v.x-x,2) + pow(v.y-y,2) + pow(v.z-z,2));
return d;
}
//calculate the magnitude (length) of the vector and return the magnitude of the vector
float magnitude()
{
return (float) sqrt(pow(x,2) + pow(y,2) + pow(z,2) );
}
void rotateVec(float ang)
{
x=cos(ang);
y=sin(ang);
}
void normalize()
{
x = x/(float) sqrt(pow(x,2) + pow(y,2) + pow(z,2) );
y = y/(float) sqrt(pow(x,2) + pow(y,2) + pow(z,2) );
}
//set function values through manual input //change vector parameters without using another function
void set (float xx, float yy, float zz)
{
x=xx;
y=yy;
z=zz;
}
//set function values with vector values //change vector parameters without using another function
void set (Vec v)
{
x=v.x;
y=v.y;
z=v.z;
}
}
OpenProcessing is an online community platform devoted to sharing and discussing Processing sketches in a collaborative, open-source environment.
Download Processing
Terms of Service
To contact, send an email to:

See the feedback forum and vote!
Follow OpenProcessing on Twitter.
All sketches are licensed under Creative Commons Attribution-Share Alike 3.0.
Syntax highlighting and Processing brush under LGPL 3.
All the source code is licensed under Creative Commons GNU GPL.
Comments engine by Scriptsmill Comments Script.


