made with proce55ing


// TRIPLETS
//
// this system works with each possible triplet of particles, attracting them
// to the average position of the trio. a diffusion filter slowly fades the 
// display after each iteration. click/drag to interact
//
// Seb Chevrel | <A HREF="http://www.seb.cc">www.seb.cc</A>
// ———————————————————————————————————————————————————————————————
final int WIDTH=400;
final int HEIGHT=400;
final int num=6;
Point[] Pts=new Point[num];

void setup() {
  size(WIDTH,HEIGHT);
  noBackground();
  stroke(255,255,255);
  strokeWidth(2);
  fill(0,0,0);
  rect(0,0,WIDTH,HEIGHT);

  for(int i=0;i<num;i++) {
    Pts[i]=new Point(random(WIDTH),random(HEIGHT));
  }

}

void loop() {
  int i,j,k;
  float cx,cy;

  // for each triplet (there are 3^(n-2)+1 triplets)
  for(i=0;i<(num-2);i++) {
    for(j=i+1;j<(num-1);j++) {
      for(k=j+1;k<num;k++) {
        // average pos of the triplet
        cx=(Pts[i].x+Pts[j].x+Pts[k].x)/3;
        cy=(Pts[i].y+Pts[j].y+Pts[k].y)/3;
        // attract pts to the center
        Pts[i].attract(cx,cy);
        Pts[j].attract(cx,cy);
        Pts[k].attract(cx,cy);

      }
    }
  }
  
  blur();

  for(i=0;i<num;i++) {
    Pts[i].update();
    if (i==0 && mousePressed) { Pts[0].x=mouseX; Pts[0].y=mouseY; Pts[0].vx=0; Pts[0].vy=0; }
    Pts[i].render();
  }
}

void blur() {
  int index,sum,left,right,top,bottom;
  for(int j=0;j<WIDTH;j++) {
    for(int i=0;i<HEIGHT;i++) {
      index=i*WIDTH+j;

      // Wraparound offsets
      if(j>0) left=-1; else left=WIDTH-1;
      if(j==(WIDTH-1)) right=-WIDTH+1; else right=1;
      if(i>0) top=-WIDTH; else top=(HEIGHT-1)*WIDTH;
      if(i==(HEIGHT-1)) bottom=-(HEIGHT-1)*WIDTH; else bottom=WIDTH;

      // Calculate the sum of n neighbors
      sum=pixels[left+index] & 255; // left middle
      //sum+=pixels[left+top+index] & 255; // left top
      //sum+=pixels[left+bottom+index] & 255; // left bottom
      sum+=pixels[right+index] & 255; // right middle
      //sum+=pixels[right+top+index] & 255; // right top
      //sum+=pixels[right+bottom+index] & 255; // right bottom
      sum+=(pixels[index] & 255); // middle middle
      sum+=pixels[index+top] & 255; // middle top
      sum+=pixels[index+bottom] & 255; // middle bottom
      sum=(sum/5);
      pixels[index]=(sum<<16)+(sum<<8)+sum;
    }
  }

}

class Point {
  public float x,y;
  public float rx,ry;
  public float vx,vy;
  public int col;
Point(float cx,float cy) { x=rx=cx; y=ry=cy; vx=vy=0; col=0xFFFFFF;}
  void update() {
    rx=x;
    ry=y;
    x+=vx;
    y+=vy;
    if (x>WIDTH) x=rx=x % WIDTH;
    if (x<0) x=rx=WIDTH+(x % WIDTH);
    if (y>HEIGHT) y=ry=y % HEIGHT;
    if (y<0) y=ry=HEIGHT+(y % HEIGHT);
  }

  void render() {
    //stroke(col);
    line(rx,ry,x,y);
  }

  void attract(float cx,float cy) {
    float dx,dy,d2;
    dx=cx-x; dy=cy-y; d2=dx*dx+dy*dy;
    if(d2>20) {
      vx=(vx + dx*40/d2 )*.98;
      vy=(vy + dy*40/d2 )*.98;
    }
  }


};