Ticha-Como se llama

Initially, I wanted to create a full-bodied, trippy-looking, fantastical creature whose motions reacted to the user’s facial expressions. I researched different character designs found on Pictoplasma and deviantART, but mostly drew inspiration from the Faun in Guillermo del Toro’s Pan’s Labyrinth. The Faun was a particularly interesting subject for me because not only was its design a well-crafted fusion of plant and animal, but also its movements and gestures strongly complemented its plantlike characteristics. My ‘final’ creature design, with its wooden ram horns, frog body, bushy hair, human hands, and venus flytrap for a tail, was inspired by the Faun and a bizarre legendary creature called the chimera.

Soon I realized that I was getting too entangled in the design and aesthetics that I did not give much thought into how the creature would move, or how it would interact with its environment. When I was unable to formulate an algorithm that would allow it to walk in a convincing manner, I decided to scrap my ram-frog-tree-bush-human-flytrap creature entirely and go with something more simple.

The ‘final‘ final creature design is a slightly cuter, llama-esque creature (because as I was painting it on Tuesday Jun noted that it resembled a llama). Its movements are controlled by the eyebrows, mouth, and head rotation – though I wanted it to be affected by the head tilt as well but ran into technical difficulties. I am more or less satisfied with the result, and found the extra ladybug feature to be an effective way to get the user more engaged with the character. If I had more time, I would make the ladybug fly more convincingly and try to work out the technical issues I had with the head tilting.

// a template for receiving face tracking osc messages from
// Kyle McDonald's FaceOSC https://github.com/kylemcdonald/ofxFaceTracker
//
// 2012 Dan Wilcox danomatika.com
// for the IACD Spring 2012 class at the CMU School of Art
//
// adapted from from Greg Borenstein's 2011 example
// http://www.gregborenstein.com/
// https://gist.github.com/1603230
//
import oscP5.*;
OscP5 oscP5;

// num faces found
int found;

// pose
float poseScale;
PVector posePosition = new PVector();
PVector poseOrientation = new PVector();

// gesture
float mouthHeight;
float mouthWidth;
float eyeLeft;
float eyeRight;
float eyebrowLeft;
float eyebrowRight;
float jaw;
float nostrils;

//fetus
PImage face;
PImage body;
PImage lefteye;
PImage righteye;
PImage leftlid;
PImage rightlid;
PImage upperlip;
PImage llamajaw;
PImage ladybug;
PImage flyinglady;

int x = 0;

boolean moving = false;
boolean flying = true;

Ladybug myLadybug;

void setup() {
  size(600, 525);

  face = loadImage("face.png");
  body = loadImage("body.png");
  lefteye = loadImage("left eye.png");
  righteye = loadImage("right eye.png");
  leftlid = loadImage("left lid.png");
  rightlid = loadImage("right lid.png");
  upperlip = loadImage("mouth top.png");
  llamajaw = loadImage("mouth bottom.png");
  ladybug = loadImage("ladybug.png");
  flyinglady = loadImage("ladybug-flying.png");

  oscP5 = new OscP5(this, 8338);
  oscP5.plug(this, "found", "/found");
  oscP5.plug(this, "poseScale", "/pose/scale");
  oscP5.plug(this, "posePosition", "/pose/position");
  oscP5.plug(this, "poseOrientation", "/pose/orientation");
  oscP5.plug(this, "mouthWidthReceived", "/gesture/mouth/width");
  oscP5.plug(this, "mouthHeightReceived", "/gesture/mouth/height");
  oscP5.plug(this, "eyeLeftReceived", "/gesture/eye/left");
  oscP5.plug(this, "eyeRightReceived", "/gesture/eye/right");
  oscP5.plug(this, "eyebrowLeftReceived", "/gesture/eyebrow/left");
  oscP5.plug(this, "eyebrowRightReceived", "/gesture/eyebrow/right");
  oscP5.plug(this, "jawReceived", "/gesture/jaw");
  oscP5.plug(this, "nostrilsReceived", "/gesture/nostrils");

  myLadybug = new Ladybug();

  noStroke();
  fill(0);
}

void draw() {  
  background(255);

  //I tried to allow the character to tilt its head
  //but due to high memory usage Processing just decided not to work. :/
  //translate(314,514);
  //rotate(poseOrientation.z);

  if (found > 0) {
    //pushMatrix();
    //translate(-314,-514);

    image(lefteye, 0, 0);
    image(righteye, 0, 0);

    float eyeDir = map(poseOrientation.y, -0.16, 0.04, -5, 5);
    ellipse(238+eyeDir, 337, 30, 30);
    ellipse(371+eyeDir, 337, 30, 30);

    /* for whatever reason, the eyebrow tracking is much more 
     appropriate for blink motions.... */
    float blinkleft = map(eyebrowLeft, 7.3, 8.350, -5, 45);
    float blinkright = map(eyebrowRight, 7.3, 8.350, -5, 45);

    image(leftlid, 0, blinkleft);
    image(rightlid, 0, blinkright);

    image(face, 0, 0);
    image(body, 0, 0);

    float mouthopen = map(mouthHeight, 1.0, 7.0, 0, 30);

    moving = mouthopen > 4.1;

    image(llamajaw, 0, mouthopen);

    image(upperlip, 0, 0);

    //popMatrix();
  }

  myLadybug.fly();
  myLadybug.display();
}

// OSC CALLBACK FUNCTIONS

public void found(int i) {
  println("found: " + i);
  found = i;
}

public void poseScale(float s) {
  println("scale: " + s);
  poseScale = s;
}

public void posePosition(float x, float y) {
  println("pose position\tX: " + x + " Y: " + y );
  posePosition.set(x, y, 0);
}

public void poseOrientation(float x, float y, float z) {
  println("pose orientation\tX: " + x + " Y: " + y + " Z: " + z);
  poseOrientation.set(x, y, z);
}

public void mouthWidthReceived(float w) {
  println("mouth Width: " + w);
  mouthWidth = w;
}

public void mouthHeightReceived(float h) {
  println("mouth height: " + h);
  mouthHeight = h;
}

public void eyeLeftReceived(float f) {
  println("eye left: " + f);
  eyeLeft = f;
}

public void eyeRightReceived(float f) {
  println("eye right: " + f);
  eyeRight = f;
}

public void eyebrowLeftReceived(float f) {
  println("eyebrow left: " + f);
  eyebrowLeft = f;
}

public void eyebrowRightReceived(float f) {
  println("eyebrow right: " + f);
  eyebrowRight = f;
}

public void jawReceived(float f) {
  println("jaw: " + f);
  jaw = f;
}

public void nostrilsReceived(float f) {
  println("nostrils: " + f);
  nostrils = f;
}

// all other OSC messages end up here
void oscEvent(OscMessage m) {
  if (m.isPlugged() == false) {
    println("UNPLUGGED: " + m);
  }
}

class Ladybug {
  float posX;
  float posY;
  float spdX;
  float spdY;

  Ladybug() {
    posX = random(40, 400);
    posY = random(60, 400);
    spdX = random(-2, 2);
    spdY = random(-2, 2);
  }

  void fly() {
    if (!moving && 264 < = posX && posX <= 303 
      && 300 <= posY && posY <= 342) {
      flying = false;
      spdX = 0;
      spdY = 0;
    }

    else {
      if (spdX == 0) {
        //it'll move away from the mouth
        spdX = random(-2, 2);
        spdY = random(-3, 0);
      }

      posX += spdX;
      posY += spdY;
      flying = true;

      if (posX < 40 || posX > 400) {
        spdX *= -1;
      }

      if (posY < 60 || posY > 400) {
        spdY *= -1;
      }
    }
  }

  void display() {

    if (flying) {
      image(flyinglady, posX, posY);
    }

    else {
      image(ladybug, posX, posY);
    }
  }
} 

< \pre>

Deer Hunter

face1

face2

I once participated in a game design conversation, and one of the ideas we developed was to have a predator hiding its size so it can sneak up on its prey. The devouring idea is also inspired by Thatgamecompany’s Flow. I feel by using the mouth as the catalyst for consuming, I can put the player into a more emersive atmosphere, and maybe make them feel either bad or empowered as they turn deer into explosions of blood. I feel I can improve upon the game by adding in sound effects and music, but finding the perfect ones take more time than I anticipated.

I used the position of the head to determine whether the blob should go left or right on screen. Changing the height of my mouth will change the size of the blob, and when that height go past a certain threshold, the blob turns into a monster and devour the deer. It will revert back to a small blob once the player closes his/her mouth again.

MINIMALIST CLOCK

I went for a more minimal approach with this assignment. The hours and minutes are displayed in binary, with only the necessary bits available for view (ones are filled rectangles, zeroes are blank). The triangles in the background progressively divide, with the amount of seconds represented as the number of triangles in a given row or column. I was really enamored with the idea of neatly subdividing objects as a clock, and thought the execution of the minute and hour representations worked well. Unfortunately, the triangles tended to leave extra space between the right and lower sides of the sketch and the last triangles. Other than that, I’m super pleased with the color scheme.

Balls and Legs

clock1 clock2 clock3

“Everyone you know someday will die” – The Flaming Lips

No matter how strong a relationship or how durable a material is, the passage of time will erode it. The blobs and the legs fall down as each minute passes, each trying to bounce or twitch its way back up top, but it will never achieve its old position ever again. We too can struggle against the flow of time, but we cannot win.

I like the organic feel of the blob, but I feel like it could be more irregular to look even weirder. I also wanted the legs to represent something, such as the hours, but since my clock resets every 12 hours, 12 legs is not enough. They also look weird without being attached to a ball so I just attached them randomly to balls.

int square_width = 15;
Ball[] balls;
Leg[] legs;
int past_second = -1;
PImage leg_img;

class Ball{
  int x, y, diameter, stop_point;
  float speed, acceleration;
  int dx, dy, dd;

  Ball(int a, int b, int d)
  {
    x = a;
    y = b;
    diameter = d;
    speed = 0.0;
    acceleration = 0.0;
    stop_point = (int)random(-30, 30);
    dx = (int)random(-3,3);
    dy = (int)random(-3,3);

    // delta diameter
    dd = (int)random(5,15);
    if(random(-1,1) < 0.0)
      dd *= -1.0;
  }

  /**
   * drops the ball
   */
  void drop()
  {
    acceleration = 0.9;
  }

  void draw_ball()
  {
    // make the ball fall
    if(y < height - 40 + stop_point || (speed < 0.0))
    {
      y += speed;
      speed += acceleration;
    }
    else if(abs(speed) > 3.5)
      speed *= -0.4;

    float percent = (millis() % 1000.0)/1000.0;
    if(percent > 0.5)
      percent = 1.0 - percent;

    stroke(200, 50 + diameter);
    fill(20, 50 + diameter);
    ellipse(x+percent*dx, y+percent*dy, diameter+percent*dd, diameter+percent*dd);
  }
}

class Leg{
  float direction;
  float theta, leg_scale, dtheta;
  Ball ball;

  Leg(Ball ba)
  {
    ball = ba;
    leg_scale = random(0.8, 1.2);
    theta = random(-0.5, 0.5);
     
    direction = 1.0;
    if(random(-1,1) < 0.0)
      direction *= -1;
  }

  void draw_leg()
  {
    pushMatrix();
    
    float percent = (millis() % 1000.0)/1000.0;
    if(percent > 0.5)
      percent = 1.0 - percent;

    translate(ball.x, ball.y);

    if(ball.x > width/2)
      scale(-leg_scale,leg_scale);
    else
      scale(leg_scale,leg_scale);

    if(0.0 < ball.speed && ball.speed < 0.1)
      percent = 1.0 + 0.03*percent;

    float on_ground = 0.0;
    float speed = 1.0;
    if(ball.acceleration != 0.0)
    {
      on_ground = 0.006*ball.y;
      speed = ball.speed;
    }

    rotate(theta+percent*0.09*(direction*speed)+on_ground);

    image(leg_img, 0, 0);

    popMatrix();
  }
}

void setup() {
  size(600, 600);
  randomSeed(15251);
  initialize_balls();
  leg_img = loadImage("leg.png");
  initialize_legs();
  set_to_current_time();
  
}

/**
 * initializes the clock to current time
 */
void set_to_current_time()
{
  int minute_passed = 60*hour() + minute();
  for(int i = 0; i < minute_passed; i++)
  {
    int r = (int)random(0,720);
    Ball random_ball = balls[r];
    
    // select a ball that hasnt already fallen
    while(random_ball.speed != 0.0)
    {
      r = (int)random(0,720);
      random_ball = balls[r];
    }

    random_ball.drop();
  }
}

/**
 * allocate the ball objects
 */
void initialize_balls(){
  balls = new Ball[720];
  int i = 0;
  while(i < balls.length)
  {
    int x = 0;
    int y = 20;
    while((x < width/2 && y > 2*x/3 + random(-10,10)) || 
          (x > width/2 && y > 2*(width - x)/3 + random(-10,10))
          )
    {
      x = (int)random(20, width-20);
      y = (int)random(5, height/3);
    }

    int s = (int)random(20, 50);

    balls[i] = new Ball(x, y, s);
    i++;
  }
}

/**
 * allocate the leg objects
 */
void initialize_legs()
{
  legs = new Leg[20];
  for(int i = 0; i < legs.length; i++)
  {
    Ball b = balls[(int)random(0,720)];
    while(!(b.x < 200 || b.x > 300))
      b = balls[(int)random(0,720)];

    legs[i] = new Leg(b);
  }

}

/**
 * draw the gradient for the background
 */
void draw_background(){

  for(int i = 0; i < height; i++)
  {
    stroke(100-i/7, 50-i/12, 70-i/10);
    line(0, i, width, i);
  }
}

void draw() {
  draw_background();

  // resets the clock every 12 hours
  if(hour() % 12 == 0 && minute() % 60 == 0 && millis() % 1000.0 < 100.0)
  {
    initialize_balls();
    initialize_legs();
  }

  // selects random ball to drop every minute
  if(second() == 0 && past_second != second())
  {
    int r = (int)random(0,720);
    Ball random_ball = balls[r];
    
    // select a ball that hasnt already fallen
    while(random_ball.speed != 0.0)
    {
      r = (int)random(0,720);
      random_ball = balls[r];
    }
    random_ball.drop();
  }
  past_second = second();

  // draw the balls
  for(int i = 0; i < balls.length; i++)
  {
    Ball b = balls[i];
    b.draw_ball();
  }

  // draw the legs
  for(int i = 0; i < legs.length; i++)
  {
    Leg l = legs[i];
    l.draw_leg();
  }
}

Angwy Face

I have always been afraid of upsetting people and invoking their wrath. I always try to leave an encounter without making another person angry, as I am especially afraid of being yelled at. This fear has been eating at me recently as I try to navigate a new place with new people and new different personalities. I chose to make a face, modeled on the PLZ face memes because of their wide range of over the top expressions, that confronted this fear by always being angry. The point of interacting with the face is to make it angrier. I wanted to make the angry face cuter and cuter the more aggressive it tried to be, negating its negativity. The angriest features correspond to the happiest features on the viewer, leaving the viewer smiling while the face is raging at them. I used right and left eyebrow heights and the mouth height OSC properties as I felt these features give the widest range of happy/angry facial triggers. This too was a choice to negate the face’s negativity. I like the way this turned out; it gives me a little more confidence in my fuddlings with Processing.

Empty Space Clock

My clock visually juxtaposes clean circles against a heavy noise background with tumultuous shades of purple and green haze. Hours change the noise fuzziness, minutes change the color, and seconds are indicated by circles traveling across the width of the frame two per second. I landed on two per second after fiddling with different speeds, sizes, colors, and shapes and finding the spacing, transparency and speed of the second-circles to be the most visually pleasing. The color and noise shifts occur subtly, focusing more attention on the current moment rather than the grand scheme of time. This notion has become increasingly more important to me over the last few weeks, and it drove me to make the clock more of an empty, cosmological space beyond understanding.

 

10:02:52 (am and pm)
 photo FinalClockScreen1002_zpsf6f88f8f.jpg

2:50:12 (am and pm)
 photo FinalClockScreen250_zps9119edf5.jpg

/*Rachel Moeller
EMS2 Clock
Hours change the noise level.
Minutes change the color.
Seconds draw stars.
 */
float increment = 0.006;
 
 
void setup()
{
  size(600, 200);
}
 
void draw()
{
   
  loadPixels();
 
  //time variables
  int hour=hour();
  int minute=minute();
  int second=second();
   
  //change by the hour
  increment=map(sin(hour),-1,1,0.006,.01);
 
  int p=0;
  //array of star positions
  int o=10;
  int r1=(int)random(o*-1,o);
  int[] starPositions= new int[120];
  //populate array
  for (int arrayCount=0;arrayCount

FaceOSC Bot

While I was brainstorming ideas for my FaceOSC character, my first ideas were three dimensional creatures like giraffes or other animals. These are ideas that I would actually like to animate in the future, but I have yet to fully understand how to create complex 3D shapes in Processing.

Anyways, I decided to shift gears to a deconstructed face, so I could work with the component parts: the eyes, eyebrows, nose, and mouth. And I think the webcam icon on my laptop subconsciously inspired me, because I didn’t realize how similar my initial eyeball sketches were to the design of a webcam. And that turned out to be just fine because it adds to the idea of how FaceOSC interacts with people.

A great deal of my time was spent figuring out how to animate the propellers. I decided to go with a 3-frame GIF and just import it as an image. But the more I think of it, my program could have had a lot more flexibility and interactivity if I created a 3D sphere and somehow stuck a propeller and pupil on it. Anywho, I am still quite proud of the propeller GIF though.

My second challenge was figuring out the design for the mouth (I scrapped the nose). I couldn’t really imagine a shape that fit with the theme of circular robots, so I went with red laser beams shooting through robo-dimples. I’m not sure if that idea comes across with other people, though. Unfortunately, my mouth portion is a bit glitchy as the endpoints don’t properly line up with the robo-dimples when I move my mouth in certain areas.

Finally, the properties I used from FaceOSC are the x-position and y-position of the face, the face scale, the eyebrow height (the eyebrows move the eyeballs, I thought it would be more fun that way), the mouth width, and the mouth height. And even though the variables I used are limited, the sinusoidal movements kind of make it more enjoyable to look at.

Here are some sketches I did first, followed by my code:
sketchface

facesketch2

//Kyle McDonald's FaceOSC https://github.com/kylemcdonald/ofxFaceTracker
import oscP5.*;
OscP5 oscP5;

Animation animation1, animation2, animation3, animation4;

int found;

float poseScale;
PVector posePosition = new PVector();
PVector poseOrientation = new PVector();

float mouthHeight;
float mouthWidth;
float eyebrowLeft;
float eyebrowRight;

float theta1 = 0;
float theta2 = 0;
float xposLEye;
float yposLEye;
float xposREye;
float yposREye;
float xposLDimple;
float yposLDimple;
float xposRDimple;
float yposRDimple;
float a1;
float b1;
float c1;
float a2;
float b2;
float c2;

void setup() {
  size(500, 550);
  frameRate(30);
  
  oscP5 = new OscP5(this, 8338);
  oscP5.plug(this, "found", "/found");
  oscP5.plug(this, "poseScale", "/pose/scale");
  oscP5.plug(this, "posePosition", "/pose/position");
  oscP5.plug(this, "poseOrientation", "/pose/orientation");
  oscP5.plug(this, "mouthWidthReceived", "/gesture/mouth/width");
  oscP5.plug(this, "mouthHeightReceived", "/gesture/mouth/height");
  oscP5.plug(this, "eyebrowLeftReceived", "/gesture/eyebrow/left");
  oscP5.plug(this, "eyebrowRightReceived", "/gesture/eyebrow/right");
  
  animation1 = new Animation("roboeye", 3);
  animation2 = new Animation("roboeye", 3);
  animation3 = new Animation("roboballL", 3);
  animation4 = new Animation("roboballR", 3);
}

void draw() {
  background(255);
  backdrop(255,210);
  
  float a1 = map(sin(theta1),-1,1,-10,10);
  float b1 = map(cos(theta1),-1,1,-10,10);
  float c1 = map(sin(theta1),-1,1,-0.1,0.1);
  float a2 = map(sin(theta2),-1,1,-15,15);
  float b2 = map(cos(theta2),-1,1,-8,8);
  float c2 = map(sin(theta2),-1,1,-0.1,0.1);
  theta1+=0.15;
  theta2+=0.08;
  
  xposLEye = -width/2;
  yposLEye = eyebrowLeft * -11;
  xposREye = 0;
  yposREye = eyebrowRight * -10;
  xposLDimple = -mouthWidth * 7-75;
  yposLDimple = height/3-50;
  xposRDimple = mouthWidth * 7-75;
  yposRDimple = height/3-50;
  
  if(found>0) {
    translate(posePosition.x-50, posePosition.y-50);
    
    pushMatrix();
    translate(a1,b1);
    rotate(c1);
    animation1.display(xposLEye,yposLEye);
    popMatrix();
    
    pushMatrix();
    scale(0.85);
    translate(b2,a2);
    rotate(c2);
    animation1.display(xposREye,yposREye);
    popMatrix();
    
    pushMatrix();
    translate(b1,a1);
    rotate(c1);
    laserArc(xposLDimple+160,yposLDimple+50,mouthWidth*11.5,mouthHeight*30,0,PI);
    laserArc(xposLDimple+160,yposLDimple+50,mouthWidth*11.5,mouthHeight*2,-PI,0);
    animation3.display(xposLDimple,yposLDimple);
    animation4.display(xposRDimple,yposRDimple);
    popMatrix();
  }
}

// OSC CALLBACK FUNCTIONS

public void found(int i) {
  println("found: " + i);
  found = i;
}

public void poseScale(float s) {
  println("scale: " + s);
  poseScale = s;
}

public void posePosition(float x, float y) {
  println("pose position\tX: " + x + " Y: " + y );
  posePosition.set(x, y, 0);
}

public void poseOrientation(float x, float y, float z) {
  println("pose orientation\tX: " + x + " Y: " + y + " Z: " + z);
  poseOrientation.set(x, y, z);
}

public void mouthWidthReceived(float w) {
  println("mouth Width: " + w);
  mouthWidth = w;
}

public void mouthHeightReceived(float h) {
  println("mouth height: " + h);
  mouthHeight = h;
}

public void eyebrowLeftReceived(float f) {
  println("eyebrow left: " + f);
  eyebrowLeft = f;
}

public void eyebrowRightReceived(float f) {
  println("eyebrow right: " + f);
  eyebrowRight = f;
}

// all other OSC messages end up here
void oscEvent(OscMessage m) {
  
  /* print the address pattern and the typetag of the received OscMessage */
  println("#received an osc message");
  println("Complete message: "+m);
  println(" addrpattern: "+m.addrPattern());
  println(" typetag: "+m.typetag());
  println(" arguments: "+m.arguments()[0].toString());
  
  if(m.isPlugged() == false) {
    println("UNPLUGGED: " + m);
  }
}

void laserArc(float x0, float y0, float wid, float hgt, float start, float stop){
  noFill();
  
  strokeWeight(15);
  stroke(240,0,0,75);
  arc(x0,y0,wid,hgt,start,stop);
  
  strokeWeight(12);
  stroke(240,0,0,75);
  arc(x0,y0,wid,hgt,start,stop);
  
  strokeWeight(5);
  stroke(255,100);
  arc(x0,y0,wid,hgt,start,stop);
  
  strokeWeight(3);
  arc(x0,y0,wid,hgt,start,stop);
}

void backdrop(color c1, color c2){
  noFill();
  for(int i=0;i< =height-50;i++){
      float inter=map(i,0,height-50,0,1);
      color c=lerpColor(c1,c2,inter);
      stroke(c);
      line(0,i+100,width,i+100);
  }
}

// Class for animating a sequence from Processing.org

class Animation {
  PImage[] images;
  int imageCount;
  int frame;
  
  Animation(String imagePrefix, int count) {
    imageCount = count;
    images = new PImage[imageCount];

    for (int i = 0; i < imageCount; i++) {
      // Use nf() to number format 'i' into four digits
      String filename = imagePrefix + i + ".gif";
      images[i] = loadImage(filename);
    }
  }

  void display(float xpos, float ypos) {
    frame = (frame+1) % imageCount;
    image(images[frame], xpos, ypos);
  }
}

Kristina -GIF

I still can’t seem to make this into a GIF… but at least it’s not working at a normal speed.

int nFramesInLoop=20;
int nElapsedFrames;
void setup() {
size(300, 300);
background(20);
}
void draw() {

fill(0, 0, 60, 20);
rect(0, 0, 500, 200);
for (int skyh = 0;skyh<=150;skyh++) { for (int skyv =0;skyv<=150;skyv++) { pushMatrix(); float randstars = random(-20, 20); float randstars2 = random (-20, 20); translate(1+30*skyh+randstars, 1+randstars2 +16*skyv); fill(0); rect(0, 0, 200, 300); fill(255); ellipse(0, 0, 1, 1); popMatrix(); } } //Adding the buildings of the city fill(110); noStroke(); rect(0, 100, 30, 100); //adding lit windows to this particualr building for (int b2=0; b2 < 3; b2++) { for (int b3 = 0; b3<=8;b3++) { pushMatrix(); fill(200, 220, 255); translate(3+10*b2, 110+10*b3); rect(0, 0, 4, 5); popMatrix(); //continuing to add buildings fill(95); rect(120, 120, 150, 80); fill(50); rect(30, 70, 120, 130); fill(255, 200, 0); //adding lights to this particular building also for (int i=0;i<12;i++) { for (int j=0;j<=12;j++) { pushMatrix(); fill(255, 200, 0); translate(32+10*i, 75+10*j); rect(0, 0, 5, 5); popMatrix(); } fill(100); rect(280, 50, 110, 150); fill(70); rect(170, 150, 130, 50); } //adding a reflection in the water for the yellow building fill(0); rect(0, 200, 300, 200); for (int k=0;k<=8;k++) { for (int l = 0; l<=12; l++) { float rand = random(-10, 10); float rand2 = random(-10, 10); pushMatrix(); fill(255, 200, 0, 220); translate(28+rand+15*k, 210+rand2+15*l); //rect(0, 0, 5, 7); //fill(200,100,0,100); rect(0, 0, 18, 3); popMatrix(); } } for (int reflectb2=0;reflectb2<=4;reflectb2++) { for (int reflectb3 = 0; reflectb3<12; reflectb3++) { float randb2 = random(-10, 10); float randb3 = random(-10, 10); pushMatrix(); fill(200, 220, 255, 220); translate(randb2+15*reflectb2, 210+randb3+15*reflectb3); rect(0, 0, 18, 3); popMatrix(); } } } } } [/embed] Here is my code... I need to find a way to randomly generate buildings and their accompanying lights withuot having to do it individually every time! My code finally got so slow I only ever rendered two buildings...

Erosion GIF

Idea Sketches

GIF Idea Sketches

After messing around in Processing for a while, I went back to the drawing board and came up with 3 ideas for a gif.

One was really macabre—a stickman getting pulled apart. Decided against that one.

The second idea was to do something cyclic with the idea of erosion—inspired by pearls which form around a grain of sand. In this animation, a white circle would get worn down by black or grey particles around it until it was only one white particle, then it would grow back to its original size, layer by layer.

My third idea was to animate ripples (at some level of fidelity), just to play with color and create something aesthetically pleasing and calming to look at.

Inspiration

By Reza Ali Also by Reza Ali by dvdp

Pearls/Erosion GIF (2nd Concept)

erosionGIF

Self-Critique of GIF

My favorite part of creating this GIF was researching GIF art in general. I was impressed and inspired by the works of artists like Reza Ali and dvdp. However, actually creating the types of effects in their GIFs, and creating them at high quality, proved much more difficult than I was expecting. I don’t think my GIF lives up to what I had in my head when I was sketching; the pace of the growth and shrinking of the pearl feels off, as does the motion of the “sand”. I learned a ton from the project; exploring GIFs as a medium, and OOP and particle systems in Processing . That said, I think that the concept I had in mind is one that I will have to come back to in order to execute as well as I would like.

Code

Main

// Global variables 
// for GIF recording template
int     nFramesInLoop = 120;
int     nElapsedFrames;
boolean bRecording; 

// for sketch
ParticleSystem sand;
Pearl pearl;
color backgroundColor = color(0);

void setup() {
  size(600,600);
  pearl = new Pearl(width/2, height/2);
  sand = new ParticleSystem(new PVector(width/2, 50));
  // for recording
  bRecording = false;
  nElapsedFrames = 0; 
  noSmooth() ;
}

// use keypress to trigger recording
void keyPressed() {
  bRecording = true;
  nElapsedFrames = 0; 
  //noLoop();
}

//for recording
void draw() {
  // Compute a percentage (0...1) representing where we are in the loop.
  float percentCompleteFraction = 0; 
  if (bRecording) {
    percentCompleteFraction = (float) nElapsedFrames / (float)nFramesInLoop;
  } else {
    percentCompleteFraction = (float) (frameCount % nFramesInLoop) / (float)nFramesInLoop;
  }

  // Render the design, based on that percentage. 
  renderMyDesign(percentCompleteFraction);

  // If we're recording the output, save the frame to a file. 
  if (bRecording) {
    saveFrame("output/myname-loop-" + nf(nElapsedFrames,4) + ".png");
    nElapsedFrames++; 
    if (nElapsedFrames >= (nFramesInLoop+1)) {
      bRecording = false;
    }
  }
  println("recording " + bRecording);
}

void renderMyDesign(float percent) {
  background(backgroundColor);
  sand.addParticles();
  pearl.run();
  sand.run();
}

Pearl Class

// Pearl class - basically a shrinking and growing circle
class Pearl {
  int x;
  int y;
  float minRadius;
  float maxRadius;
  float currentRadius;
  float growthFactor;
  color pearlColor;
  float epsilon;
  ArrayList borderParticles;

  Pearl(int instanceX, int instanceY) {
    x = instanceX;
    y = instanceY;
    minRadius = 1; // could easily be a parameter
    maxRadius = 170;
    currentRadius = minRadius + 1;
    pearlColor = (255);
    growthFactor = 1.33;
    epsilon = 1;
  }

  int getCenterX() {
    return x + (int)currentRadius;
  }

  int getCenterY() {
    return y + (int)currentRadius;
  }

  void run() {
    update();
    display();
  }

  void update() {
    float diffToMax = abs(currentRadius - maxRadius);
    float diffToMin = abs(currentRadius - minRadius);
    if (diffToMax < epsilon || diffToMin < epsilon) {
      growthFactor*=-1;
    } 
    currentRadius = currentRadius + growthFactor;
  }

  void display() {
    noStroke();
    fill(pearlColor);
    ellipse(x, y, currentRadius*2, currentRadius*2);
  }
}

Particle Class

// A simple Particle class based of class by Daniel Shiffman
// Source: http://processing.org/examples/simpleparticlesystem.html
class Particle {
  PVector location;
  PVector velocity;
  PVector acceleration;
  float lifespan;
  float pSize;
  color pColor;

  Particle(PVector thisLocation) {
    acceleration = new PVector(random(-1, 1), random(-1, 1));
    velocity = new PVector(random(-3, 3),random(-3, 3));
    location = thisLocation.get();
    lifespan = 10000.0;
    pSize = 2;
    pColor = color(30);
  }

  // calculate new values and redraw particle
  void run() {
    update();
    display();
  }

  // Method to update location
  void update() {
    bounceOffWalls();
    avoidPearl();

    acceleration.x *= 0.9;
    acceleration.y *= 0.9;
    velocity.add(acceleration);
    location.add(velocity);
    lifespan -= 1.0;
  }

  // Method to make particles bounce off walls
  void bounceOffWalls() {
    // bounce off the walls
    if (location.x < = 0 || location.x >= width) {
      toggleXVelocity();
    } else if (location.y < = 0 || location.y >= height){
      toggleYVelocity();
    }
  }

  void reverseVelocityDirection() {
    toggleXVelocity();
    toggleYVelocity();
  }

  void toggleXVelocity() {
    velocity.x = velocity.x * -1;
  }

  void toggleYVelocity() {
    velocity.x = velocity.x * -1;
  }

  // Method to avoid pearl
  void avoidPearl() {
    float sumRadii = pSize + pearl.currentRadius;
    float sandPearlDistance = dist(getCenterX(), pearl.getCenterX(), getCenterY(), pearl.getCenterY());
    if (sandPearlDistance == sumRadii) {
      pColor = backgroundColor;
    } 
    else if (sandPearlDistance < sumRadii) {
      reverseVelocityDirection();
      // if growth factor is positive, then particles that hit the pearl
      // should disappear
      if (pearl.growthFactor > 0) {
        lifespan = 0.0;      
      }
      // if the pearl is shrinking, the particles that hit should turn white
      // and have a very short lifespan
      else {
        erodePearl();        
      }
    }
  }

  void erodePearl() {
    if (millis()%2 == 0) {
        pSize = 0.8;
        pColor = color(255);
        lifespan = 10;
        velocity.div(3);
    }
  }

  // Method to display
  void display() {
    // lifespan determines opacity of stroke and fill
    stroke(pColor, lifespan); 
    fill(pColor, lifespan);
    ellipse(location.x, location.y, pSize, pSize);
  }

  // Is the particle still useful?
  boolean isDead() {
    if (lifespan < 0.0) {
      return true;
    } else {
      return false;
    }
  }

  // copy-pasted from pearl but they should probably both be subclasses
  float getCenterX() {
    return location.x + pSize;
  }

  float getCenterY() {
    return location.y + pSize;
  }
}

Particle System Class

// A class to describe a group of Particles by Daniel Shiffman
// Source: http://processing.org/examples/simpleparticlesystem.html
// An ArrayList is used to manage the list of Particles 

class ParticleSystem {
  ArrayList particles;
  PVector origin;

  ParticleSystem(PVector location) {
    origin = location.get();
    particles = new ArrayList();
  }

  void addParticles() {
    int numParticles = (int)random(30, 50);
    for (int i = 0; i < = numParticles; i++) {
      addParticle();
    }
  }

  PVector getRandomLocation() {
    return new PVector(random(0,width), random(0,height));
  }

  void addParticle() {
    PVector randomLocation = getRandomLocation();
    particles.add(new Particle(randomLocation));
  }

  void run() {
    for (int i = particles.size()-1; i >= 0; i--) {
      Particle p = particles.get(i);
      p.run();
      if (p.isDead()) {
        particles.remove(i);
      }
    }
  }
}

Computational Color Palettes (Wallpaper)

After experimenting with making patterns using Penrose L-Systems and not being pleased with the results, I used this assignment to explore computational color palettes in depth. My wallpaper sketch generates a grid of colored dots based on one seed color. The pattern and type of palette was inspired by a unique design that I found online.

Wallpaper Inspiration

Initially I just tried to replicate that design, using it as a starting point to define rules about color relationships.

./wp-content/uploads/sites/2/2013/09/julia-dots-wallpaper-green.pdf

julia-dots-wallpaper-green

After getting my color generators to work as well as possible with the green seed color, I tried with others. Here are the results:

./wp-content/uploads/sites/2/2013/09/julia-dots-wallpaper-orange.pdf
julia-dots-wallpaper-orange

./wp-content/uploads/sites/2/2013/09/julia-dots-wallpaper-purple.pdf
julia-dots-wallpaper-purple

./wp-content/uploads/sites/2/2013/09/julia-dots-wallpaper-yellow.pdf
julia-dots-wallpaper-yellow

Code

import processing.pdf.*;

int numCircles;
int radius;
int outerPadding;
int diameter;
int internalPadding;
color seedColor;
color backgroundColor;
int numPages;

void setup() {
  size(700, 700, PDF, "julia-dots-wallpaper-purple.pdf");
  colorMode(HSB, 360, 100, 100);
  numCircles = 6;
  numPages = 4;
  outerPadding = width/10;
  int internalWidth = width - (2*outerPadding);
  diameter = internalWidth/numCircles;
  internalPadding = width/60;
  
  color green = color(140, 65, 75);
  color orange = color(8, 80, 92);
  color purple = color(268, 52, 79);
  color yellow = color(34, 98, 28);
  seedColor =  purple;
  
  backgroundColor = color(30, 3, 100);
  // noLoop(); // turn off to record to pdf
}

void mousePressed() {
  loop();  // Holding down the mouse activates looping
}

void mouseReleased() {
  noLoop();  // Releasing the mouse stops looping draw()
}

void draw() {
  background(backgroundColor);
  noStroke();
  int rows = numCircles;
  int cols = numCircles;
  
  for (int row = 0; row < = rows; row++) {
    for (int col = 0; col <= cols; col++) {
      drawCircle(row, col);
    }
  }
  
  PGraphicsPDF pdf = (PGraphicsPDF) g;  // Get the renderer
  pdf.nextPage();  // Tell it to go to the next page
  // When finished drawing, quit and save the file
  if (frameCount == numPages) {
    exit();
  }
}

void drawCircle(int row, int col) {
  color circleColor = seedColor;
  float randomNum = random(0, 100);
  if (randomNum >= 0 && randomNum < = 80) {
     circleColor = similarColor(seedColor);
  } else if (randomNum > 80 && randomNum < = 95) {
     circleColor = counterClockwiseAdjacentColor(seedColor);
  } else if (randomNum > 95 && randomNum < 100) { 
     circleColor = clockwiseAdjacentColor(seedColor);
  }
  
  fill(circleColor);
  int x = outerPadding + (diameter * col);
  int y = outerPadding + (diameter * row);
  ellipse(x, y, diameter-internalPadding, diameter-internalPadding);
}

color similarColor(color seedColor) {
   // SIMILAR COLORS, SLIGHT VARIATIONS ON SEED
   // circle color is seed color, but should vary a little in brightness
   float brightDelta = ((int)random(-4, 4))*10.0;
   float hueDelta = random(-25,10);
   float satDelta = -15;
   if (hueDelta > 0) {
     satDelta = 10;
   } else {
     brightDelta += 20;
   }
   return modifyColor(seedColor, hueDelta, satDelta, brightDelta);
}

color counterClockwiseAdjacentColor(color seedColor) {
   // counterclockwise next door color and variations
   float hueDelta = ((int)random(3, 7)) * 10;
   float satDelta = -5;
   float brightDelta = 10;
   if (hueDelta > 65) {
     satDelta -= 30;
     // brightDelta += 35;
   }
   else if (hueDelta > 50) {
     satDelta +=10;
   }
   return modifyColor(seedColor, hueDelta, satDelta, brightDelta);
}

color clockwiseAdjacentColor(color seedColor) {
   // clockwise next to seed color
    float hueDelta = random(8, 11) * -10;
    float satDelta = -15;
    float brightDelta = 23;
    if (hueDelta < -90) {
      satDelta = 25;
    }
    return modifyColor(seedColor, hueDelta, satDelta, brightDelta); 
}

color modifyColor(color startColor, float hueDelta, float saturationDelta, float brightnessDelta) {
  float hue = hue(startColor);
  float newHue = hue + hueDelta;
  
  float sat = saturation(startColor);
  float newSat = sat + saturationDelta;
  
  float brightness = brightness(startColor);
  float newBrightness = brightness + brightnessDelta;
  
  return color(newHue, newSat, newBrightness);
}