dinkolas-AnimatedLoop

About

I wrote a function with a bunch of parameters that makes a character, and then called the function several times to populate the GIF. I used DoubleExponentialSigmoid, CircularEaseInOut, AdjustableCenterEllipticWindow, and ExponentialSmoothedStaircase to change the pace of the legs and bodies of some characters. I think that the individual characters look good, but I could certainly improve the over all color selection and composition of the piece if I had more time. Now that the character building function is working, a better application of it might be some sort of random walk-cycle generator, so I might implement that at some point. Maybe this concept doesn't fit the GIF format very well, and would be better if the viewer could see more of the possible walks.

Sketches

Code

I used Golan's Java Processing Template, here's my code:

// This is a template for creating a looping animation in Processing/Java. 
// When you press the 'F' key, this program will export a series of images
// into a "frames" directory located in its sketch folder. 
// These can then be combined into an animated gif. 
// Known to work with Processing 3.3.6
// Prof. Golan Levin, January 2018
 
//===================================================
// Global variables. 
String  myNickname = "dinkolas"; 
int     nFramesInLoop = 120;
int     nElapsedFrames;
boolean bRecording; 
 
//===================================================
void setup() {
  size (640,640); 
  bRecording = true;
  nElapsedFrames = 0;
}
//===================================================
void keyPressed() {
  if ((key == 'f') || (key == 'F')) {
    bRecording = true;
    nElapsedFrames = 0;
  }
}
 
//===================================================
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("frames/" + myNickname + "_frame_" + nf(nElapsedFrames, 4) + ".png");
    nElapsedFrames++; 
    if (nElapsedFrames >= nFramesInLoop) {
      bRecording = false;
    }
  }
}
 
//===================================================
void renderMyDesign (float percent) {
  color c1 = color(59,58,133);
  color c2 = color(137,52,109);
  color c3 = color(199,96,88);
  color c4 = color(255,178,72);
  color c5 = color(255,225,150);
  background(c5);
 
  //1
  color bodyCol = c3;
  color legCol =  c2;
  float originX = 100;
  float originY = 200;
  float xShift = -20;
  float altitude = 100;
  float diameter = 70;
  float bounce = 10;
  float legLength = 50;
  float legWeight = 10;
  float footLength = 10;
  float cycles = 2;
  float footH = 40;
  float footV = 20;
  float phase = 0;
  float legT = function_DoubleExponentialSigmoid(percent,.5);
  float bodyT = function_DoubleExponentialSigmoid(percent,.5);
  drawCharacter(bodyCol, legCol, originX, originY, xShift, altitude, diameter, bounce, legLength, legWeight, footLength, cycles, footH, footV, phase, legT, bodyT);
 
  //2
  bodyCol = c1;
  legCol = c2;
  originX = 320;
  originY = 190;
  xShift = 10;
  altitude = 90;
  diameter = 120;
  bounce = 5;
  legLength = 25;
  legWeight = 15;
  footLength = 10;
  cycles = 1;
  footH = 20;
  footV = 10;
  phase = .5;
  legT = function_ExponentialSmoothedStaircase(percent,.05,4);
  bodyT = legT;
  drawCharacter(bodyCol, legCol, originX, originY, xShift, altitude, diameter, bounce, legLength, legWeight, footLength, cycles, footH, footV, phase, legT, bodyT);
 
  //3
  bodyCol = c4;
  legCol = c3;
  originX = 520;
  originY = 250;
  xShift = 0;
  altitude = 200;
  diameter = 50;
  bounce = 15;
  legLength = 100;
  legWeight = 8;
  footLength = 10;
  cycles = 3;
  footH = 30;
  footV = 60;
  phase = .6;
  legT = 1-percent;
  bodyT = 1-percent;
  drawCharacter(bodyCol, legCol, originX, originY, xShift, altitude, diameter, bounce, legLength, legWeight, footLength, cycles, footH, footV, phase, legT, bodyT);
 
  //4
  for (int i = 0; i < 3; i++){
    bodyCol = c3;
    legCol = c1;
    originX = 50+55*i;
    originY = 350;
    xShift = 0;
    altitude = 40;
    diameter = 40;
    bounce = 3;
    legLength = 20;
    legWeight = 4;
    footLength = 3;
    cycles = 2;
    footH = 10;
    footV = 6;
    phase = .1+.3*i;
    legT = function_AdjustableCenterEllipticWindow (percent, .5);
    bodyT = percent;
    drawCharacter(bodyCol, legCol, originX, originY, xShift, altitude, diameter, bounce, legLength, legWeight, footLength, cycles, footH, footV, phase, legT, bodyT);
  }
 
  //5
  bodyCol = c3;
  legCol = c1;
  originX = 490;
  originY = 430;
  xShift = 10;
  altitude = 90;
  diameter = 50;
  bounce = 15;
  legLength = 60;
  legWeight = 8;
  footLength = 10;
  cycles = 1;
  footH = 80;
  footV = 130;
  phase = .6;
  legT = percent;
  bodyT = percent;
  drawCharacter(bodyCol, legCol, originX, originY, xShift, altitude, diameter, bounce, legLength, legWeight, footLength, cycles, footH, footV, phase, legT, bodyT);
 
  //6
  bodyCol = c3;
  legCol = c4;
  originX = 300;
  originY = 380;
  xShift = -30;
  altitude = 100;
  diameter = 60;
  bounce = 25;
  legLength = 40;
  legWeight = 10;
  footLength = 10;
  cycles = 2;
  footH = 40;
  footV = 40;
  phase = .8;
  legT = 2*function_CircularEaseInOut(percent);
  bodyT = percent;
  drawCharacter(bodyCol, legCol, originX, originY, xShift, altitude, diameter, bounce, legLength, legWeight, footLength, cycles, footH, footV, phase, legT, bodyT);
 
  //7
  bodyCol = c1;
  legCol = c4;
  originX = 400;
  originY = 600;
  xShift = 0;
  altitude = 70;
  diameter = 30;
  bounce = 5;
  legLength = 60;
  legWeight = 10;
  footLength = 5;
  cycles = 1;
  footH = 120;
  footV = 35;
  phase = 0;
  legT = percent;
  bodyT = 1-percent;
  drawCharacter(bodyCol, legCol, originX, originY, xShift, altitude, diameter, bounce, legLength, legWeight, footLength, cycles, footH, footV, phase, legT, bodyT);
 
  //8
  bodyCol = c2;
  legCol = c4;
  originX = 100;
  originY = 580;
  xShift = 20;
  altitude = 130;
  diameter = 70;
  bounce = 40;
  legLength = 70;
  legWeight = 20;
  footLength = 30;
  cycles = 2;
  footH = 30;
  footV = 35;
  phase = .5;
  legT = percent;
  bodyT = 1-percent;
  drawCharacter(bodyCol, legCol, originX, originY, xShift, altitude, diameter, bounce, legLength, legWeight, footLength, cycles, footH, footV, phase, legT, bodyT);
}
 
void drawCharacter(color bodyCol, color legCol, float originX, float originY, float xShift, float altitude, float diameter, float bounce, float legLength, float legWeight, float footLength, float cycles, float footH, float footV, float phase, float legT, float bodyT) {
  //set origin and coordinate system so up is positive
  translate(originX,originY);
  scale(1,-1);
  legT = ((legT%(1/cycles))*cycles+phase)%1;
  bodyT = ((bodyT%(1/cycles/2))*2*cycles+phase)%1;
  stroke(bodyCol);
  strokeWeight(legWeight);
  line(-footH-20,-legWeight/2,footH+20+footLength,-legWeight/2); 
 
  //body positions
  float bodyX = xShift+map(-sin(TWO_PI*bodyT), -1,1, -bounce/5, +bounce/5);
  float bodyY = map(-cos(TWO_PI*bodyT), -1,1, altitude-bounce, altitude+bounce);
 
  //back leg
  //hip=(x1,y1), ankle=(x2,y2), knee = (a,b), toe = (x3,y3)
  translate(0,legWeight/2);
  strokeWeight(legWeight);
  stroke(legCol);
  noFill();
  float x1 = bodyX;
  float y1 = bodyY-diameter/3;
  float x2 = map(-cos(TWO_PI*legT), -1,1,-footH,footH);
  float y2 = max(0,map(sin(TWO_PI*legT), -1, 1, -footV, footV));
  float mult = -.5*sqrt(4*pow(legLength,2)/(pow(x2-x1,2)+pow(y2-y1,2))-1);
  float a = .5*(x1+x2)+mult*(y2-y1);
  float b = .5*(y1+y2)+mult*(x1-x2);
  float x3 = x2 + footLength; 
  float y3 = 0;
  if (2*legLength < sqrt(pow(x2-x1,2)+pow(y2-y1,2))){line(x1,y1,x2,y2);}
  else{line(x1,y1,a,b);line(a,b,x2,y2);}
  if (y2==0) {line(x2,y2,x3,y3);}
  else {
    float angle = atan((y2-y1)/(x2-x1));
    angle = PI/2-angle;
    if (x2<x1) {angle+=PI;}
    translate(x2,y2);
    rotate(-angle);
    line(0,0,-footLength,0);
    rotate(angle);
    translate(-x2,-y2);
  }
  translate(0,-legWeight/2);
 
  //body
  noStroke();
  fill(bodyCol);
  ellipse(bodyX,bodyY,diameter,diameter);
 
  //front leg
  //hip=(x1,y1), ankle=(x2,y2), knee = (a,b), toe = (x3,y3)
  legT = (legT+.5)%1;
  translate(0,legWeight/2);
  strokeWeight(legWeight);
  stroke(legCol);
  x1 = bodyX;
  y1 = bodyY-diameter/3;
  x2 = map(-cos(TWO_PI*legT), -1,1,-footH,footH);
  y2 = max(0,map(sin(TWO_PI*legT), -1, 1, -footV, footV));
  mult = -.5*sqrt(4*pow(legLength,2)/(pow(x2-x1,2)+pow(y2-y1,2))-1);
  a = .5*(x1+x2)+mult*(y2-y1);
  b = .5*(y1+y2)+mult*(x1-x2);
  x3 = x2 + footLength; 
  y3 = 0;
  if (2*legLength < sqrt(pow(x2-x1,2)+pow(y2-y1,2))){line(x1,y1,x2,y2);}
  else{line(x1,y1,a,b);line(a,b,x2,y2);}
  if (y2==0) {line(x2,y2,x3,y3);}
  else {
    float angle = atan((y2-y1)/(x2-x1));
    angle = PI/2-angle;
    if (x2<x1) {angle+=PI;}
    translate(x2,y2);
    rotate(-angle);
    line(0,0,-footLength,0);
    rotate(angle);
    translate(-x2,-y2);
  }
  translate(0,-legWeight/2);
 
  //reset origin
  scale(1,-1);
  translate(-originX,-originY);
}
 
 
 
//===================================================
// Taken from https://github.com/golanlevin/Pattern_Master
float function_DoubleExponentialSigmoid (float x, float a) {
  // functionName = "Double-Exponential Sigmoid";
 
  float min_param_a = 0.0 + EPSILON;
  float max_param_a = 1.0 - EPSILON;
  a = constrain(a, min_param_a, max_param_a); 
  a = 1-a;
 
  float y = 0;
  if (x<=0.5) {
    y = (pow(2.0*x, 1.0/a))/2.0;
  } else {
    y = 1.0 - (pow(2.0*(1.0-x), 1.0/a))/2.0;
  }
  return y;
}
 
float function_ExponentialSmoothedStaircase (float x, float a, int n) {
  //functionName = "Smoothed Exponential Staircase";
  // See http://web.mit.edu/fnl/volume/204/winston.html
 
  float fa = sq (map(a, 0,1, 5,30));
  float y = 0; 
  for (int i=0; i<n; i++){
    y += (1.0/(n-1.0))/ (1.0 + exp(fa*(((i+1.0)/n) - x)));
  }
  y = constrain(y, 0,1); 
  return y;
}
 
float function_AdjustableCenterEllipticWindow (float x, float a){
  //functionName = "Adjustable-Center Elliptic Window";
 
  float min_param_a = 0.0 + EPSILON;
  float max_param_a = 1.0 - EPSILON;
  a = constrain(a, min_param_a, max_param_a);
 
  float y = 0;
 
  if (x<=a){
    y = (1.0/a) * sqrt(sq(a) - sq(x-a));
  } 
  else {
    y = (1.0/(1-a)) * sqrt(sq(1.0-a) - sq(x-a));
  }
  return y;
}
 
float function_CircularEaseInOut (float x) {
  //functionName = "Penner's Circular Ease InOut";
 
  float y = 0; 
  x *= 2.0; 
 
  if (x < 1) {
    y =  -0.5 * (sqrt(1.0 - x*x) - 1.0);
  } else {
    x -= 2.0;
    y =   0.5 * (sqrt(1.0 - x*x) + 1.0);
  }
 
  return y;
}

Spoon-AnimatedLoop

 

 

With this project I set out to make a GIF consisting of flat graphics that illustrated some sort of mechanical interaction. I experimented with a couple of ideas first through sketching before settling on the idea of rectangles that sucked up and expelled circles.

I started with the rectangle and used the adjustable center cosine window function for the expansion and contraction of the rectangle. This gave me a smooth stretching of the rectangle before a quick contraction.

I attempted first to use this same function for the movement of the circles, but I switched to the double elliptic sigmoid, which has a slow beginning and a very fast middle before slowing down in the end. Fiddling with the constants in these two functions allowed me to get the movements to sync up as if the circles were being shot out of the rectangles.

Next I doubled the rows of rectangles and circles and moved the first row to the top half of the image. This created an image that filled the square better (it wasn't as bottom heavy as the previous version), and it also created an image that could be tiled, which provides an indication of spacial unendingness to the animation.

The constant linear movement rectangles and circles was distracting and not particularly enjoyable to watch, so I experimented with a number of different mathematical functions for the movement.

I settled on the double odd polynomial ogee function, which has a very large flat section in its curve that brings the movement of the rectangles to a complete halt. This function gave me the freedom to invert the direction of the circles, which now moved in the opposite direction of the rectangles while both stopped to interact with each other. After settling on the movement, I experimented with a lot of different color schemes for the animation. I decided to go with the black-and-grey color scheme with changing yellow and red circles. The colors evoked a more industrial feel to the image. The changing from grey to white in the rectangles as they expand and contract helps to indicate stretching, like the discoloration of plastics and rubbers when they are stretched. The changing from red to yellow indicates change in state for the circles as they pass through the system, almost like the cooling off and heating up of metal.

I am satisfied with the movement of the image, although I am not as sure about the coloration. I liked the black and red as a graphic composition, but I also like the references that the final color scheme indicate to. I also wonder if my composition might have been stronger with a larger, zoomed in view of a single row. When untiled, the top and bottom rows appear less intriguing than the central one.

 

 

// This is a template for creating a looping animation in Processing/Java. 
// When you press the 'F' key, this program will export a series of images
// into a "frames" directory located in its sketch folder. 
// These can then be combined into an animated gif. 
// Known to work with Processing 3.4
// Prof. Golan Levin, January 2018
 
//===================================================
// Global variables. 
String  myNickname = "Spoon"; 
int     nFramesInLoop = 180;
int     nElapsedFrames;
boolean bRecording; 

//===================================================
void setup() {
  size (640, 640); 
  bRecording = false;
  nElapsedFrames = 0;
}

//===================================================
void keyPressed() {
  if ((key == 'f') || (key == 'F')) {
    bRecording = true;
    nElapsedFrames = 0;
  }
}
 
//===================================================
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("frames/" + myNickname + "_frame_" + nf(nElapsedFrames, 4) + ".png");
    nElapsedFrames++; 
    if (nElapsedFrames >= nFramesInLoop) {
      bRecording = false;
    }
  }
}


//===================================================
void renderMyDesign (float percent) {
  //
  // YOUR ART GOES HERE.
  // This is an example of a function that renders a temporally looping design. 
  // It takes a "percent", between 0 and 1, indicating where we are in the loop. 
  // This example uses two different graphical techniques. 
  // Use or delete whatever you prefer from this example. 
  // Remember to SKETCH FIRST!
 
  //----------------------
  // here, I set the background and some other graphical properties
  background (0, 0, 0);
  noStroke();
  fill (75, 75, 75);
  rect (0, height / 4 - 15, width, 2 * height / 4);
  smooth(); 
  noStroke(); 
 
  //----------------------
  // Here, I assign some handy variables. 
  float cx = 100;
  float cy = 100;
 
 //----------------------
  int quantity = 8;
  float reducedPercent = map(percent < 0.75 ? 0 : percent - 0.75, 0, 0.25, 0, 1);
  float invertedPercent = percent <= .5 ? percent + 0.5 : percent - 0.5;
  float invertedReducedPercent = map(invertedPercent < 0.75 ? 0 : invertedPercent - 0.75, 0, 0.25, 0, 1);
  float multiplier1 = function_DoubleEllipticSigmoid(reducedPercent, 0.8, 0.4);
  float invertedMultiplier1 = function_DoubleEllipticSigmoid(invertedReducedPercent, 0.8, 0.4);
  float multiplier2 = function_AdjustableCenterCosineWindow(percent, 0.8);
  float invertedMultiplier2 = function_AdjustableCenterCosineWindow(invertedPercent, 0.8);
  float positionMultiplier = map(function_DoubleOddPolynomialOgee (percent > 0.15 ? percent - 0.15 : percent + 0.85, 0.5, 0.5, 6),
        0, 1, 0, 0.25);
  for(int i = -quantity; i <= quantity; i++) {
    if(i % 2 == 0) {
      fill(map(percent, 0, 1, 255, 150), map(percent, 0, 1, 198, 0), map(percent, 0, 1, 0, 35));
      ellipse(i * width / quantity + map(positionMultiplier, 0, 1, width, 0), map(multiplier1, 0, 1, 30, -height / 2 + 30), 50, 50);
      fill(map(percent, 0, 1, 150, 255), map(percent, 0, 1, 0, 198), map(percent, 0, 1, 35, 0));
      ellipse(i * width / quantity + map(positionMultiplier, 0, 1, width, 0), map(multiplier1, 0, 1, height / 2 + 30, 30), 50, 50);
      fill(map(percent, 0, 1, 255, 150), map(percent, 0, 1, 198, 0), map(percent, 0, 1, 0, 35));
      ellipse(i * width / quantity + map(positionMultiplier, 0, 1, width, 0), map(multiplier1, 0, 1, height + 30, height / 2 + 30), 50, 50);
    } else {
      fill(map(invertedPercent, 0, 1, 255, 150), map(invertedPercent, 0, 1, 198, 0), map(invertedPercent, 0, 1, 0, 35));
      ellipse(i * width / quantity + map(positionMultiplier, 0, 1, width, 0), map(invertedMultiplier1, 0, 1, 30, -height / 2 + 30), 50, 50);
      fill(map(invertedPercent, 0, 1, 150, 255), map(invertedPercent, 0, 1, 0, 198), map(invertedPercent, 0, 1, 35, 0));
      ellipse(i * width / quantity + map(positionMultiplier, 0, 1, width, 0), map(invertedMultiplier1, 0, 1, height / 2 + 30, 30), 50, 50);
      fill(map(invertedPercent, 0, 1, 255, 150), map(invertedPercent, 0, 1, 198, 0), map(invertedPercent, 0, 1, 0, 35));
      ellipse(i * width / quantity + map(positionMultiplier, 0, 1, width, 0), map(invertedMultiplier1, 0, 1, height + 30, height / 2 + 30), 50, 50);
    }
  }
   
  //----------------------
  for(int i = -quantity; i <= quantity; i++) {
    if(i % 2 == 0) {
      fill(map(multiplier2, 0, 1, 150, 255));
      drawCenteredRectangleFromBottom(i * width / quantity + map(positionMultiplier, 0, 1, 0, width), height, 
                                    map(multiplier2, 0, 1, 75, 50), 100 - map(multiplier2, 0, 1, 50, 0));
      drawCenteredRectangleFromBottom(i * width / quantity + map(positionMultiplier, 0, 1, 0, width), height / 2, 
                                    map(multiplier2, 0, 1, 50, 75), 100 - map(multiplier2, 0, 1, 0, 50));
    } else {
      fill(map(invertedMultiplier2, 0, 1, 150, 255));
      drawCenteredRectangleFromBottom(i * width / quantity + map(positionMultiplier, 0, 1, 0, width), height, 
                                    map(invertedMultiplier2, 0, 1, 75, 50), 100 - map(invertedMultiplier2, 0, 1, 50, 0));
      drawCenteredRectangleFromBottom(i * width / quantity + map(positionMultiplier, 0, 1, 0, width), height / 2, 
                                    map(invertedMultiplier2, 0, 1, 50, 75), 100 - map(invertedMultiplier2, 0, 1, 0, 50));
    }
  }
}
 
//===================================================
// Functions I wrote that are called in renderMyFunction

void drawCenteredRectangleFromBottom(float x, float y, float width, float height) {
  float upperLeftX = x - (width / 2);
  float upperLeftY = y - height;
  rect(upperLeftX, upperLeftY, width, height);
}
 
//===================================================
// Mathematical functions called in renderMyDesign
// Taken from https://github.com/golanlevin/Pattern_Master

float function_AdjustableCenterCosineWindow (float x, float a) {
  // functionName = "Adjustable Center Cosine Window";
  
  float ah = a/2.0; 
  float omah = 1.0 - ah;

  float y = 1.0;
  if (x <= a) {
    y = 0.5 * (1.0 + cos(PI* ((x/a) - 1.0)));
  } 
  else {
    y = 0.5 * (1.0 + cos(PI* (((x-a)/(1.0-a))  )));
  } 
  return y;
}

float function_DoubleEllipticSigmoid (float x, float a, float b){
  // functionName = "Double-Elliptic Sigmoid";

  float y = 0;
  if (x<=a){
    if (a <= 0){
      y = 0;
    } else {
      y = b * (1.0 - (sqrt(sq(a) - sq(x))/a));
    }
  } 
  else {
    if (a >= 1){
      y = 1.0;
    } else {
      y = b + ((1.0-b)/(1.0-a))*sqrt(sq(1.0-a) - sq(x-1.0));
    }
  }
  return y;
}

float function_DoubleOddPolynomialOgee (float x, float a, float b, int n) {
  //functionName = "Double Odd-Polynomial Ogee";

  float min_param_a = 0.0 + EPSILON;
  float max_param_a = 1.0 - EPSILON;
  float min_param_b = 0.0;
  float max_param_b = 1.0;

  a = constrain(a, min_param_a, max_param_a); 
  b = constrain(b, min_param_b, max_param_b); 
  int p = 2*n + 1;
  float y = 0;
  if (x <= a) {
    y = b - b*pow(1-x/a, p);
  } 
  else {
    y = b + (1-b)*pow((x-a)/(1-a), p);
  }
  return y;
}

nannon-AnimatedLoop

end of semester edit:

I went back a couple times on this assignment and finally got it to be a bit closer to what I envisioned.

Final:

A few iterations:

 

-----------------------------------------------------------

I originally wanted to do something totally different (a set of eyes that opened into new eyes), but I eventually thought that it was too complicated and illustrative and decided to invest my time in doing something 3D instead. I saw this cool gif with rotating text in a cylinder in the spirit of Zach Lieberman the other day, and wanted to do something similar. Unfortunately, I decided all of the Wednesday night, and only had Thursday to do it. Having never worked in 3D before, this was really torturous, but still kind of rewarding. I'm disappointed I didn't get the final (as of now) to how I imagined it in my head, but I'm going to keep working on this. Ultimately, I want the text to actually be a coherent sentence, and have different text on the inside of the cylinder and out. A lot of time was spent figuring out Geomerative and cyinder geometry, but I did really learn a lot. I didn't have time to use p5_func, but hopefully will get around to it as I continue to flesh this guy out.

Thank you Golan  for taking the time to explain a ton mathematics.

// This is a template for creating a looping animation in Processing/Java. 
// When you press the 'F' key, this program will export a series of images
// into a "frames" directory located in its sketch folder. 
// These can then be combined into an animated gif. 
// Known to work with Processing 3.3.6
// Prof. Golan Levin, January 2018
import geomerative.*;
 
RFont f;
RShape fire;
RPoint[] points;
RShape[] letterShapes;
//===================================================
// Global variables. 
String  myNickname = "nannon"; 
int     nFramesInLoop = 120;
int     nElapsedFrames;
boolean bRecording; 
 
//===================================================
void setup() {
  size (640, 640, P3D); 
  lights();
  background(0);
  bRecording = false;
  nElapsedFrames = 0;
 
  RG.init(this);
  fire = RG.getText("x", "Optimo.ttf", 72, CENTER);
 
  RG.setPolygonizer(RG.UNIFORMLENGTH);
  RG.setPolygonizerLength(1);
  fill(255);
 
  letterShapes = fire.children;
 
 
  smooth();
}
//===================================================
void keyPressed() {
  if ((key == 'f') || (key == 'F')) {
    bRecording = true;
    nElapsedFrames = 0;
  }
}
 
//===================================================
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("frames/" + myNickname + "_frame_" + nf(nElapsedFrames, 4) + ".png");
    nElapsedFrames++; 
    if (nElapsedFrames &gt;= nFramesInLoop) {
      bRecording = false;
    }
  }
}
 
//===================================================
void renderMyDesign (float percent) {
  //
  background(0);
  translate(320, 320, 200);
  rotateX(PI);
  rotateY(map(percent,0,1,0,PI/2));
  rotateZ(sin(PI));
  stroke(255);
  noFill(); 
 
 
  int r= 100;
  int nSlices = 50;
  int zA = -20;
  int zB = 20;
 
 
 
 
 
  rotateY(PI/2);
  rotateX(PI/2);
  noFill();
 
  // draw a cylinder
  noFill(); 
  stroke(255); 
  beginShape(TRIANGLE_STRIP);
  for (int i=0; i&lt;=nSlices; i++) {
    float theta = map(i, 0, nSlices, 0, 2*PI);
    float pX = r*cos(theta);
    float pY = r*sin(theta);
    vertex(pX, pY, zA);
    vertex(pX, pY, zB);
  }
  endShape();
 
  // print("x1:"+points[0].x+" "+"y:"+ points[0].y+"    ");
  // print("x2:"+points[points.length-1].x+" "+"y:"+ points[points.length-1].y+"    ");
 
 
 
   //beginShape();
   //for (int i=0; i&lt;points.length; i++) {
   //  float newX = map(points[i].x, -187, 189, 0, 0.6*(2*PI));
   //  float newZ = map(points[i].y, -24, 0, 0, 0.6*(2*PI));
   //  vertex(r*cos(newX), points[i].y, abs(r*cos(newZ)));
 
   //  endShape();
   //}
 
 
 
  noFill(); 
  stroke(255); 
  //int nLetters = 16; 
  //for (int i=0; i&lt;nLetters; i++) {
 
 
 
 
    //int nPointsInThisContour = 16;
    for (int k=0; k&lt;10; k++) {
      pushMatrix();
      float letterTheta = k* radians(45.0);
 
 
      float letterX = r * cos(letterTheta);
      float letterY = r * sin(letterTheta); 
 
 
      //float eased = function_DoubleExponentialSigmoid(percent, 0.7);
      //float newletterY = map(eased, 0,1,0,PI);
 
      translate(letterX, letterY);
      rotateX( radians(90)); 
      rotateY(letterTheta+ radians(90));
 
      fill(255);
      stroke(255);
      beginShape();
      RPoint[] points = letterShapes[0].getPoints();
      for (int j=0; j&lt;points.length; j++) {
 
        //print(points[j].x);
        //float ht = map(j, 0, nPointsInThisContour, 0, TWO_PI); 
        float hpx = points[j].x; 
        float hpy =  points[j].y; 
        float hpz =0;
 
 
        vertex(hpx, hpy, hpz);
 
        //ellipse(hpx,hpy,10,10);
      }
      endShape();
      popMatrix();
    }
 
}
float function_DoubleExponentialSigmoid (float x, float a) {
  // functionName = "Double-Exponential Sigmoid";
 
  float min_param_a = 0.0 + EPSILON;
  float max_param_a = 1.0 - EPSILON;
  a = constrain(a, min_param_a, max_param_a); 
  a = 1-a;
 
  float y = 0;
  if (x&lt;=0.5) {
    y = (pow(2.0*x, 1.0/a))/2.0;
  } else {
    y = 1.0 - (pow(2.0*(1.0-x), 1.0/a))/2.0;
  }
  return y;
}

rigatoni-Reading2

Question 1A
This is perhaps an obvious example of a generative work exhibiting effective complexity, but Markus Pearsson's Minecraft in my opinion sits in a very pleasing balance between total order and total randomness. The terrain generating algorithm and mob spawning behavior has just enough order to it so as to create a gameplay experience that the player feels is fair and can adapt to, but there are so many examples of simply bizarre and wonderful points of interest that are generated within the order of these terrain chunks that makes every player's experience unique and memorable.

The footage above is from an "amplified" world generated in Minecraft. The noise used to generate this chunk makes much more of an extreme use of LOD and falloff as opposed to its "normal" counterpart.

Question 1B
I chose to address the Problem of Creativity over the other problems that Galanter puts forth because I find myself constantly weighing if I have become too technical in my work. I found this reading productive because it helped me understand what creativity even is, and I think I will be carrying the terms p-creativity and h-creativity with me as I further analyze my practices as an artist.
I am not convinced by the argument that in order for a complex system to be creative it must also be adaptive. I believe the creator of generative art is still strongly affiliated with the audience's experience. Going back to the example I used in 1A, the dev team behind Minecraft listens to its playerbase and regularly releases updates that best serves what the players want to see in the game. While Minecraft the software may not be an evolving system that adapts to each player, the creators adapt to the community as a whole in the way they continue to grow their game. In this way I believe a non-adaptive system may still be creative.

sapeck-Reading02

  1. I appreciate the beauty and effective complexity of a sunset. Every sunset is affected by the shapes, sizes, and positions of the cloud, the position of the sun in relation to the earth, and where you are on earth. A sunset is much closer to total randomness than total order--every factor changes sporadically.
  2. I have struggled with that Galanter labels as The Problem of Meaning. Nearly all of my computer-based art is created with an image of the final product in my head. I find it much more difficult to find emotion in something that I have created than it is to create something "forced." Creating something aesthetically pleasing seems easy when the conceptual aspect is mostly ignored.

casher-AnimatedLoop

https://editor.p5js.org/cassiescheirer/sketches/SknMGG_O7

I don't know why but my computer was having an incredibly hard time exporting my frames -- some were being skipped, and everything was patchy. I had Char help me export and the GIF still ended up being way too slow. Click the link above it or here (https://editor.p5js.org/cassiescheirer/sketches/SknMGG_O7) to see it in the p5.js editor. Sorry about this.

Anyway, I am mostly happy with this project. I wish I had had more time to make it more fancy, and a better laptop so that I wouldn't have gotten so frustrated at the lag, but overall I am proud. It took a little while to come up with the idea, but once I knew what I wanted to do, I had to make it happen. Again I had Char talk through the syntax I didn't know, but a lot of it I figured out myself this time. For the easing function, I knew that I was probably going to need one to simulate the "bouncing" of the ball, so I looked for one that matched that motion the most. I went through a couple of them before concluding that the doubleExponentialSigmoid, which Golan had already used in the template, was actually the best one. Having that as a guide was helpful.

I'm proud of myself for having a set idea in mind and then putting in the effort to execute it, even though it was hard. I also really like the simplicity of my GIF. At one point I was about to add some flashing circles in the background, but Char told me that the minimalist-ness of it was what made it so nice. However, I think it is only that way because I hadn't allotted enough time to make it super grand. If I had more time and had known more of the p5.js syntax, I definitely would have made it more complicated.  As you can see in my sketch (bottom left), I started an idea that branched off of the flipping square -- a skillet flipping a pancake. I stopped myself there though, as I knew I probably didn't know enough to do that. I hope I can come back to this project when I have time and make the pancake idea.

 

//===================================================
// User-modifiable global variables. 
var myNickname = "Cassie";
var nFramesInLoop = 120;
var bEnableExport = true;
 
// Other global variables you don't need to touch.
var nElapsedFrames;
var bRecording;
var theCanvas;
 
var canvaswidth = 645;
var canvasheight = 645;
 
var flipper = false;
 
//===================================================
function setup() {
  theCanvas = createCanvas(canvaswidth, canvasheight);
  bRecording = false;
  nElapsedFrames = 0;
  rectMode(CENTER);
}
 
//===================================================
function keyTyped() {
  if (bEnableExport) {
    if ((key === 'f') || (key === 'F')) {
      bRecording = true;
      nElapsedFrames = 0;
    }
  }
}
 
//===================================================
function draw() {
 
  // Compute a percentage (0...1) representing where we are in the loop.
  var percentCompleteFraction = 0;
  if (bRecording) {
    percentCompleteFraction = float(nElapsedFrames) / float(nFramesInLoop);
  } else {
    percentCompleteFraction = float(frameCount % nFramesInLoop) / float(nFramesInLoop);
  }
 
  // Render the design, based on that percentage. 
  // This function renderMyDesign() is the one for you to change. 
  renderMyDesign (percentCompleteFraction);
 
  // If we're recording the output, save the frame to a file. 
  // Note that the output images may be 2x large if you have a Retina mac. 
  // You can compile these frames into an animated GIF using a tool like: 
  if (bRecording &amp;&amp; bEnableExport) {
    var frameOutputFilename = myNickname + "_frame_" + nf(nElapsedFrames, 4) + ".png";
    print("Saving output image: " + frameOutputFilename);
    saveCanvas(theCanvas, frameOutputFilename, 'png');
    nElapsedFrames++;
 
    if (nElapsedFrames &gt;= nFramesInLoop) {
      bRecording = false;
    }
  }
}
 
//===================================================
function renderMyDesign (percent) {
  //
  // THIS IS WHERE YOUR ART GOES. 
  // This is an example of a function that renders a temporally looping design. 
  // It takes a "percent", between 0 and 1, indicating where we are in the loop. 
  // Use, modify, or delete whatever you prefer from this example. 
  // This example uses several different graphical techniques. 
  // Remember to SKETCH FIRST!
 
  //----------------------
  // here, I set the background and some other graphical properties
  var x = abs(cos(percent*TWO_PI))
  var y = doubleExponentialSigmoid(x, 0.25);
  var squish =  map(y, 0, 1, 10, 30);
 
  background(0);
  rectMode(CENTER);
  strokeWeight(2);
  stroke(255);
 
  // flipping base
  push();
  translate(width / 2 + 20*sin(TWO_PI*percent), height-250);
  rotate(sin(TWO_PI*percent));
  var flipx = map(-cos(frameCount/9.5), -1, 1, 0, 200);
 
  if (frameCount % 120 &gt;= 60) {
		fill(200, 100, 200);
  }
  else {
    fill(200,200,100);
  }
  rect(0, 20, flipx, 200);
  pop();
 
  translate(width / 2, height / 2);
 
  // ball shadow
  push();
  var shadowbounce = map(y, 0, 1, 100, 40);
  fill(0);
  noStroke();
  rotate(-abs(cos(TWO_PI*percent)));
  ellipse(0, shadowbounce, cos(frameCount/20)*20, cos(frameCount/20)*15);
  pop();
 
  // ball
  push();
  fill(255);
	var ballbounce = map(-y,0,1,100,360);
  distance = height - ballbounce;
  ellipse(0, ballbounce, 30, squish);
  pop();
 
// Symmetric double-element sigmoid function ('_a' is the slope)
// See https://github.com/IDMNYU/p5.js-func/blob/master/lib/p5.func.js
// From: https://idmnyu.github.io/p5.js-func/
//===================================================
function doubleExponentialSigmoid (_x, _a){
  if(!_a) _a = 0.75; // default
 
  var min_param_a = 0.0 + Number.EPSILON;
  var max_param_a = 1.0 - Number.EPSILON;
  _a = constrain(_a, min_param_a, max_param_a);
  _a = 1-_a;
 
  var _y = 0;
  if (_x&lt;=0.5){
    _y = (pow(2.0*_x, 1.0/_a))/2.0;
  }
  else {
    _y = 1.0 - (pow(2.0*(1.0-_x), 1.0/_a))/2.0;
  }
  return(_y);
	}
}

yalbert-AnimatedLoop

Although this pretty much turned out as an elaborate loading bar, I enjoyed making this piece. I'm partial to animation and using an easing technique was a good way to hone that skill. One part of this that I struggled with was color balance. It took me a while to experiment with black on white, white on black, and other colors until I finally settled on this combination. An element that I didn't fully resolve was the relationship between the spiral and the canvas. Making it too small made it feel like it was floating in space, but when it was too big it fit oddly in it.

// This is a template for creating a looping animation in p5.js (JavaScript). 
// When you press the 'F' key, this program will export a series of images into
// your default Downloads folder. These can then be made into an animated gif. 
// This code is known to work with p5.js version 0.6.0
// Prof. Golan Levin, 28 January 2018
 
// INSTRUCTIONS FOR EXPORTING FRAMES (from which to make a GIF): 
// 1. Run a local server, using instructions from here:
//    https://github.com/processing/p5.js/wiki/Local-server
// 2. Set the bEnableExport variable to true.
// 3. Set the myNickname variable to your name.
// 4. Run the program from Chrome, press 'f'. 
//    Look in your 'Downloads' folder for the generated frames.
// 5. Note: Retina screens may export frames at twice the resolution.
 
 
//===================================================
// User-modifiable global variables. 
var myNickname = "nickname";
var nFramesInLoop = 120;
var bEnableExport = true;
var dots = [];
var radius = 1200;
var dotSize = 45;
var worms = [];
var numWorms = 5
var colors = [[255, 0, 0], [0, 255, 0], [0, 0, 255]]
var curColor = [255, 0, 0]
var curColorInd = 0;
 
// Other global variables you don't need to touch.
var nElapsedFrames;
var bRecording;
var theCanvas;
 
//===================================================
function setup() {
  theCanvas = createCanvas(640, 640);
  bRecording = false;
  nElapsedFrames = 0;
  angleMode(DEGREES)
 
  for(j = 0; j &lt;= numWorms; j++){ var r = lerp(243, 0, (j)/(numWorms)); var g = lerp(206, 0, (j)/(numWorms)); var b = lerp(80, 0, (j)/(numWorms)); var color = [r, g, b] var worm = new Worm(color) worm.setup() worms.push(worm) } } //=================================================== function keyTyped() { if (bEnableExport) { if ((key === 'f') || (key === 'F')) { bRecording = true; nElapsedFrames = 0; } } } //=================================================== function draw() { // Compute a percentage (0...1) representing where we are in the loop. var percentCompleteFraction = 0; if (bRecording) { percentCompleteFraction = float(nElapsedFrames) / float(nFramesInLoop); } else { percentCompleteFraction = float(frameCount % nFramesInLoop) / float(nFramesInLoop); } // Render the design, based on that percentage. // This function renderMyDesign() is the one for you to change. renderMyDesign(percentCompleteFraction); // If we're recording the output, save the frame to a file. // Note that the output images may be 2x large if you have a Retina mac. // You can compile these frames into an animated GIF using a tool like: if (bRecording &amp;&amp; bEnableExport) { var frameOutputFilename = myNickname + "_frame_" + nf(nElapsedFrames, 4) + ".png"; print("Saving output image: " + frameOutputFilename); saveCanvas(theCanvas, frameOutputFilename, 'png'); nElapsedFrames++; if (nElapsedFrames &gt;= nFramesInLoop) {
      bRecording = false;
    }
  }
}
 
//===================================================
function renderMyDesign (percent) {
  //
  // THIS IS WHERE YOUR ART GOES. 
  // This is an example of a function that renders a temporally looping design. 
  // It takes a "percent", between 0 and 1, indicating where we are in the loop. 
  // Use, modify, or delete whatever you prefer from this example. 
  // This example uses several different graphical techniques. 
  // Remember to SKETCH FIRST!
 
  //----------------------
 
 
 
 
  // here, I set the background and some other graphical properties
  background(0);
  smooth();
  stroke(0, 0, 0);
  strokeWeight(2);
 
 
  // if(percent == 0.75){
  //   curColorInd +=1;
  //   curColorInd = curColorInd % colors.length;
  //   curColor = colors[curColorInd];
  // }
 
  print(percent)
 
  for(j = 0; j &lt; worms.length; j++){
    var r = lerp(curColor[0], 0, (j)/(numWorms));
    var g = lerp(curColor[1], 0, (j)/(numWorms));
    var b = lerp(curColor[2], 0, (j)/(numWorms));
    var color = [r, g, b];
 
    var worm = worms[j];
    worm.col = color
    worm.update((percent + .04 *j)%1, j);
  }
 
 
}
 
function Worm(c){
  this.dots = []
  this.col = c
  this.setup = function(){
    for(i = 0; i&lt; 60; i++){
      dots.push(new Dot(i * .003))
    }
  }
  this.update = function(percent, sizeOff){
    for(i = 0; i &lt; dots.length; i++){
      d = dots[i];
      d.calculatePos(percent);
      d.draw(this.col, sizeOff);
    }
  }
}
 
function Dot(off){
  this.offset = off
  this.x = 0
  this.y = 0
  this.radius = map(this.offset, 0, 1, 0, radius)
  this.angle = 0
  this.eased = 0
 
  this.calculatePos = function(percent){
    this.eased = quadraticInOut((percent + this.offset)%1);
    this.eased = (this.eased - 0.25)%1.0; // shifted by a half-loop, for fun
    this.angle = map(this.eased, 0, 1, 0, 360); 
 
    this.x = cos(this.angle)*this.radius + width/2;
    this.y = sin(this.angle)*this.radius + height/2;
  }
 
  this.draw = function(color, sizeOff){
    fill(color);
    noStroke();
    ellipse(this.x, this.y, dotSize + sizeOff*2, dotSize + sizeOff*2);
  }
}
 
 
// symmetric double-element sigmoid function (a is slope)
// See https://github.com/IDMNYU/p5.js-func/blob/master/lib/p5.func.js
// From: https://idmnyu.github.io/p5.js-func/
//===================================================
 
function gompertz(_x, _a){
    if(!_a) _a = 0.25; // default
    var min_param_a = 0.0 + Number.EPSILON;
    _a = max(_a, min_param_a);
 
    var b = -8.0;
    var c = 0 - _a*16.0;
    var _y = exp( b * exp(c * _x));
 
    var maxVal = exp(b * exp(c));
    var minVal = exp(b);
    _y = map(_y, minVal, maxVal, 0, 1);
 
    return(_y);
}
 
function quadraticInOut(_x) {
    if(_x &lt; 0.5)
    {
      return(8 * _x * _x * _x * _x);
    }
    else
    {
      var _v = (_x - 1);
      return(-8 * _v * _v * _v * _v + 1);
    }
}
function sineOut(_x) {
    return(sin(_x * HALF_PI));
  }
 
function cubicIn(_x) {
    return(_x * _x * _x);
}

rigatoni-AnimatedLoop

This project felt like jumping into the deep end as far as rendering goes. I was inspired by Dan Shiffman's 4D Tesseract challenge, although I wanted to make the gif my own. Until now I hadn't really given any thought to there being dimensions beyond the ones we can comfortably perceive and how they work, and thinking about higher dimensions led me down a rabbit hole of awesome math and logic content by channels like Numberphile and 3B1B. Dan Shiffman's challenge in particular was in Java and was a lot more conducive to 3d with its P3D renderer, but I think I found a suitable workaround by drawing quads instead of individual points using WEBGL. I was also treating the easing function (doubleExpSig) as an after thought, but once I actually used it to control what I see as distance along the 4th dimension I was surprised by what a huge role it played in the aesthetic of the loop. I can't imagine pulling off a smooth and natural motion without it. That being said however, the gif doesn't convincingly feel "4D" and I was to revisit it once I have more time.

I didn't end up sketching very much for this assignment, but here's a few things I did that helped me keep track of what I was doing

I realized pretty early into the project that hardcoding in each of the 16 4d vertices was time-consuming and I often drew the quads in the wrong order. I decided to make use of modulus and int division to set the points in the right order.

This is me marking the points needed to make up each face. This is maybe a quarter of what I ended up needing and I think I could have spent more time on this planning phase.

// This is a template for creating a looping animation in p5.js (JavaScript). 
// When you press the 'F' key, this program will export a series of images into
// your default Downloads folder. These can then be made into an animated gif. 
// This code is known to work with p5.js version 0.6.0
// Prof. Golan Levin, 28 January 2018
 
// INSTRUCTIONS FOR EXPORTING FRAMES (from which to make a GIF): 
// 1. Run a local server, using instructions from here:
//    https://github.com/processing/p5.js/wiki/Local-server
// 2. Set the bEnableExport variable to true.
// 3. Set the myNickname variable to your name.
// 4. Run the program from Chrome, press 'f'. 
//    Look in your 'Downloads' folder for the generated frames.
// 5. Note: Retina screens may export frames at twice the resolution.
 
 
//===================================================
// User-modifiable global variables. 
var myNickname = "rigatoni";
var nFramesInLoop = 60;
var bEnableExport = true;
 
// Other global variables you don't need to touch.
var nElapsedFrames;
var bRecording;
var theCanvas;
 
//===================================================
function setup() {
  theCanvas = createCanvas(720, 720, WEBGL);
  bRecording = false;
  nElapsedFrames = 0;
}
 
//===================================================
function keyTyped() {
  if (bEnableExport) {
    if ((key === 'f') || (key === 'F')) {
      bRecording = true;
      nElapsedFrames = 0;
    }
  }
}
 
//===================================================
function draw() {
 
  // Compute a percentage (0...1) representing where we are in the loop.
  var percentCompleteFraction = 0;
  if (bRecording) {
    percentCompleteFraction = float(nElapsedFrames) / float(nFramesInLoop);
  } else {
    percentCompleteFraction = float(frameCount % nFramesInLoop) / float(nFramesInLoop);
  }
 
  // Render the design, based on that percentage. 
  // This function renderMyDesign() is the one for you to change. 
  renderMyDesign (percentCompleteFraction);
 
  // If we're recording the output, save the frame to a file. 
  // Note that the output images may be 2x large if you have a Retina mac. 
  // You can compile these frames into an animated GIF using a tool like: 
  if (bRecording && bEnableExport) {
    var frameOutputFilename = myNickname + "_frame_" + nf(nElapsedFrames, 4) + ".png";
    print("Saving output image: " + frameOutputFilename);
    saveCanvas(theCanvas, frameOutputFilename, 'png');
    nElapsedFrames++;
 
    if (nElapsedFrames >= nFramesInLoop) {
      bRecording = false;
    }
  }
}
 
//===================================================
function renderMyDesign (percent) {
  background(180);
  var cube = new Hypercube(500, percent)
  rotateY(percent*PI)
  cube.Draw()
}
 
function Hypercube(size, margin) {
	this.points = []
  margin -= 1
  margin = doubleExponentialSigmoid(margin)
	for(var i=0; i<16; i++) {
    var j = i
		var w = floor(j/8)*margin
    j=j%8 
    var stereo = 1/(2-w)
    var z = floor(j/4)*stereo-(0.5*stereo)
    j=j%4
    var y = floor(j/2)*stereo-(0.5*stereo)
    j=j%2
    var x = floor(j/1)*stereo-(0.5*stereo)
  	this.points[i] = new P4(x*size, y*size, z*size, 0)
  }
  this.Draw = function() {
    fill(225,15)
		var front = new Face(this.points[4], this.points[5], 
                         this.points[6], this.points[7])
		var back = new Face(this.points[0], this.points[1], 
                         this.points[2], this.points[3])
		var left = new Face(this.points[0], this.points[2], 
                         this.points[4], this.points[6]) 
    var right = new Face(this.points[1], this.points[3], 
                     this.points[5], this.points[7]) 
		var top = new Face(this.points[2], this.points[3], 
                         this.points[6], this.points[7]) 
		var bottom = new Face(this.points[0], this.points[1], 
                         this.points[4], this.points[5]) 
		var sFront = new Face(this.points[12], this.points[13], 
                         this.points[14], this.points[15])
		var sBack = new Face(this.points[8], this.points[9], 
                         this.points[10], this.points[11])
		var sLeft = new Face(this.points[8], this.points[10], 
                         this.points[12], this.points[14]) 
    var sRight = new Face(this.points[9], this.points[11], 
                     this.points[13], this.points[15]) 
		var sTop = new Face(this.points[10], this.points[11], 
                         this.points[14], this.points[15]) 
		var sBottom = new Face(this.points[8], this.points[9], 
                         this.points[12], this.points[13]) 
 
    var pfront = new Face(this.points[4], this.points[5], 
                         this.points[12], this.points[13])
		var pback = new Face(this.points[0], this.points[1], 
                         this.points[8], this.points[9])
		var pleft = new Face(this.points[0], this.points[2], 
                         this.points[8], this.points[10]) 
    var pright = new Face(this.points[1], this.points[3], 
                     this.points[9], this.points[11]) 
		var ptop = new Face(this.points[2], this.points[3], 
                         this.points[10], this.points[11]) 
		var pbottom = new Face(this.points[0], this.points[4], 
                         this.points[8], this.points[12]) 
		var psFront = new Face(this.points[1], this.points[5], 
                         this.points[9], this.points[13])
		var psBack = new Face(this.points[5], this.points[7], 
                         this.points[13], this.points[15])
		var psLeft = new Face(this.points[3], this.points[7], 
                         this.points[11], this.points[15]) 
    var psRight = new Face(this.points[2], this.points[6], 
                     this.points[10], this.points[14]) 
		var psTop = new Face(this.points[6], this.points[7], 
                         this.points[14], this.points[15]) 
		var psBottom = new Face(this.points[4], this.points[6], 
                         this.points[12], this.points[14]) 
 
  	front.Draw()
    back.Draw()
    left.Draw()
    right.Draw()
    sFront.Draw()
    sBack.Draw()
    sLeft.Draw()
    sRight.Draw()
  	pfront.Draw()
    pback.Draw()
    pleft.Draw()
    pright.Draw()
    psFront.Draw()
    psBack.Draw()
    psLeft.Draw()
    psRight.Draw() 
  }
}
 
function Face(p1, p2, p3, p4) {
  var distance = 200
  this.p1 = p1
  this.p2 = p2
  this.p3 = p3
  this.p4 = p4
 
  this.Draw = function() {
    beginShape()
    	vertex(this.p1.x,this.p1.y,this.p1.z)
    	vertex(this.p2.x,this.p2.y,this.p2.z)
    	vertex(this.p4.x,this.p4.y,this.p4.z)
    	vertex(this.p3.x,this.p3.y,this.p3.z)
    endShape(CLOSE)
  }
 
  this.Print = function() {
  	 this.p1.Print()
     this.p2.Print()
     this.p3.Print()
     this.p4.Print()
  }
}
 
function P4(x,y,z,w) {
	this.x = x
  this.y = y
  this.z = z
  this.w = w
  this.Print = function() {
		print(this.x, this.y, this.z, this.w) 
  }
  this.ScalarMult = function(multiplier) {
		this.x*=multiplier
    this.y*=multiplier
    this.z*=multiplier
    this.w*=multiplier
  }
}
// Symmetric double-element sigmoid function ('_a' is the slope)
// See https://github.com/IDMNYU/p5.js-func/blob/master/lib/p5.func.js
// From: https://idmnyu.github.io/p5.js-func/
//===================================================
function doubleExponentialSigmoid (_x, _a){
  if(!_a) _a = 0.75; // default
 
  var min_param_a = 0.0 + Number.EPSILON;
  var max_param_a = 1.0 - Number.EPSILON;
  _a = constrain(_a, min_param_a, max_param_a);
  _a = 1-_a;
 
  var _y = 0;
  if (_x<=0.5){
    _y = (pow(2.0*_x, 1.0/_a))/2.0;
  }
  else {
    _y = 1.0 - (pow(2.0*(1.0-_x), 1.0/_a))/2.0;
  }
  return(_y);
}

chaine-AnimatedLoop

When I was first brainstorming ideas for my looping gif, I wanted to involve something "gobstopper-esque" just because of its simple but striking colors. I also somehow wanted to convey bursts of "flavor" and change into my gif, so I already knew from the start that I wanted to use the mapping function a lot on the colors. Looking back, I thought my sketches were very basic and almost too simple. I ended up making it more complicated both to make my gif more interesting and also to test myself. I started off with the given double exponential sigmoid function to drag my gobstopper down from the top to the center of the canvas, but I decided that starting straight from the center looked better because the circle wasn't chopped up in the beginning. Then I used the bounce in, bounce out, and bounce in and out functions to make my gobstoppers pop, and I thought that this function suited how they tasted. And finally, I used the exponential for the circle directly in the center to overtake the canvas as the frames went on. I think I did alright with the color palettes and color changes, but I would have liked to incorporate more functions and more elements to this gif. I also think the way the white circle grows larger is awkward and would like to make it maybe so that it "bounces" like its other circular counterparts and finds a more natural way of clearing the canvas.

// This is a template for creating a looping animation in p5.js (JavaScript). 
// When you press the 'F' key, this program will export a series of images into
// your default Downloads folder. These can then be made into an animated gif. 
// This code is known to work with p5.js version 0.6.0
// Prof. Golan Levin, 28 January 2018
 
// INSTRUCTIONS FOR EXPORTING FRAMES (from which to make a GIF): 
// 1. Run a local server, using instructions from here:
//    https://github.com/processing/p5.js/wiki/Local-server
// 2. Set the bEnableExport variable to true.
// 3. Set the myNickname variable to your name.
// 4. Run the program from Chrome, press 'f'. 
//    Look in your 'Downloads' folder for the generated frames.
// 5. Note: Retina screens may export frames at twice the resolution.
 
 
//===================================================
// User-modifiable global variables. 
var myNickname = "chaine";
var nFramesInLoop = 240;
var bEnableExport = true;
 
// Other global variables you don't need to touch.
var nElapsedFrames;
var bRecording;
var theCanvas;
 
//===================================================
function setup() {
  theCanvas = createCanvas(640, 640);
  bRecording = false;
  nElapsedFrames = 0;
}
 
//===================================================
function keyTyped() {
  if (bEnableExport) {
    if ((key === 'f') || (key === 'F')) {
      bRecording = true;
      nElapsedFrames = 0;
    }
  }
}
 
//===================================================
function draw() {
 
  // Compute a percentage (0...1) representing where we are in the loop.
  var percentCompleteFraction = 0;
  if (bRecording) {
    percentCompleteFraction = float(nElapsedFrames) / float(nFramesInLoop);
  } else {
    percentCompleteFraction = float(frameCount % nFramesInLoop) / float(nFramesInLoop);
  }
 
  // Render the design, based on that percentage. 
  // This function renderMyDesign() is the one for you to change. 
  renderMyDesign (percentCompleteFraction);
 
  // If we're recording the output, save the frame to a file. 
  // Note that the output images may be 2x large if you have a Retina mac. 
  // You can compile these frames into an animated GIF using a tool like: 
  if (bRecording &amp;&amp; bEnableExport) {
    var frameOutputFilename = myNickname + "_frame_" + nf(nElapsedFrames, 4) + ".png";
    print("Saving output image: " + frameOutputFilename);
    saveCanvas(theCanvas, frameOutputFilename, 'png');
    nElapsedFrames++;
 
    if (nElapsedFrames &gt;= nFramesInLoop) {
      bRecording = false;
    }
  }
}
 
//===================================================
function renderMyDesign (percent) {
  //
  // THIS IS WHERE YOUR ART GOES. 
  // This is an example of a function that renders a temporally looping design. 
  // It takes a "percent", between 0 and 1, indicating where we are in the loop. 
  // Use, modify, or delete whatever you prefer from this example. 
  // This example uses several different graphical techniques. 
  // Remember to SKETCH FIRST!
 
  //----------------------
  // here, I set the background and some other graphical properties
  background(255);
  smooth();
  stroke(0, 0, 0);
  strokeWeight(2);
 
  //----------------------
  // Here, I assign some handy variables. 
  var cx = 320;
  var cy = 320;
  noStroke();
  var arcSize = 20;
  var topY = 0 - arcSize - 2;
  var botY = height + 2;
 
    push();
 
    //I ended up not using this because it looked better starting centered
    //var eased = doubleExponentialSigmoid ((percent), 0.7); 
    //eased = (eased)%1.0; 
    //var yPosition2 = map(eased, 0, 1, topY, botY/2); 
 
    var eased2 = exponential (percent*1.2);
    var circleGrow = map(eased2, 0, 2, 0, 600);
    //ellipse (width/2, yPosition2, 20, 20);
 
    var eased3 = bounceInOut (percent * 0.5);
    var circle2Grow = map(eased3, 0, 1, 0, 2000);
 
    var eased4 = bounceInOut (percent * 0.5);
    var circle3Grow = map(eased4, 0, 1, 0, 1500);
 
    var eased5 = bounceInOut (percent * 0.5);
    var circle4Grow = map(eased5, 0, 1, 0, 1000);
 
    var easedLast = exponential (percent);
    var easedGrow = map(easedLast, 0, 1, 0, 1000);
 
    //black arc
    var test = map(percent, 0, 1, 0, 2*PI);
    //neon green arc
    var test1 = map(percent, 0, 1, 0, (3 * PI)/2);
    //light green arc
    var test2 = map(percent, 0, 1, 0, PI);
    //pink arc
    var test3 = map(percent, 0, 1, 0, PI/2);
 
 
    var arcCol1 = map(percent, 0.2, 1, 255, 204);
    var arcCol11 = map(percent, 0.2, 1, 204, 0);
    var arcCol111 = map(percent, 0.2, 1, 229, 102);
 
    //arc 1 light pink 255 204 229 -&gt; 204 0 102
    fill (arcCol1,arcCol11,arcCol111);
    arc(width/2, height/2, 1100*percent,1100*percent,0,test);
 
    var arcCol2 = map(percent, 0.2, 1, 204, 76);
    var arcCol22 = map(percent, 0.2, 1, 255, 153);
    var arcCol222 = map(percent, 0.2, 1, 229, 0);
 
    //arc 2 light green 204 255 229 -&gt; 76 153 0
    fill (arcCol2, arcCol22, arcCol222);
    arc(width/2, height/2, 1100*percent,1100*percent,0,test1);
 
 
    var arcCol3 = map(percent, 0.2, 1, 200, 0);
    var arcCol33 = map(percent, 0.2, 1, 255, 102);
    var arcCol333 = map(percent, 0.2, 1, 200, 102);
 
    //arc 3 200 255 200 -&gt; 0 102 102
    fill (arcCol3, arcCol33, arcCol333);
    arc(width/2, height/2, 1100*percent,1100*percent,0,test2);
 
    var arcCol4 = map(percent, 0.2, 1, 204, 76);
    var arcCol44 = map(percent, 0.2, 1, 229, 0);
    var arcCol444 = map(percent, 0.2, 1, 255, 153);
 
    //arc 4 light blue 204 229 255 -&gt; 76 0 153
    fill (arcCol4, arcCol44, arcCol444);
    arc(width/2, height/2, 1100*percent,1100*percent,0,test3);
 
 
    var circleCol1 = map(eased3, 0, 0.5, 102, 0);
    var circleCol11 = map(eased3, 0, 0.5, 178, 0);
    var circleCol111 = map(eased3, 0, 0.5, 255, 0);
 
    //center circle 1 pink 255 102 178
    fill (circleCol111,circleCol1,circleCol11); 
    ellipse (width/2, height/2, circle2Grow, circle2Grow); 
 
    var circleCol2 = map(eased4, 0, 0.75, 255, 0);
    var circleCol22 = map(eased4, 0, 0.75, 102, 0);
    var circleCol222 = map(eased4, 0, 0.75, 255, 0);
 
    //center circle 2 lighter pink 255 102 255
    fill (circleCol2, circleCol22, circleCol222); 
    ellipse (width/2, height/2, circle3Grow, circle3Grow); 
 
    var circleCol3 = map(eased5, 0, 1, 255, 0);
    var circleCol33 = map(eased5, 0, 1, 204, 0);
    var circleCol333 = map(eased5, 0, 1, 255, 0);
 
    //center circle 2 lightest pink 255 204 255
    fill (circleCol3, circleCol33, circleCol333); 
    ellipse (width/2, height/2, circle4Grow, circle4Grow); 
 
 
    var circleCol0 = map(easedLast, 0.1, 0.15, 200, 255);
    var circleCol00 = map(easedLast, 0.1, 0.15, 200, 255);
    var circleCol000 = map(easedLast, 0.1, 0.15, 200, 255);
 
    //center circle 2 white
    fill (circleCol0, circleCol00, circleCol000); 
    ellipse (width/2, height/2, easedGrow, easedGrow); 
 
 
    pop();
 
}
 
 
// Symmetric double-element sigmoid function ('_a' is the slope)
// See https://github.com/IDMNYU/p5.js-func/blob/master/lib/p5.func.js
// From: https://idmnyu.github.io/p5.js-func/
//===================================================
function doubleExponentialSigmoid (_x, _a){
  if(!_a) _a = 0.75; // default
 
  var min_param_a = 0.0 + Number.EPSILON;
  var max_param_a = 1.0 - Number.EPSILON;
  _a = constrain(_a, min_param_a, max_param_a);
  _a = 1-_a;
 
  var _y = 0;
  if (_x&lt;=0.5){
    _y = (pow(2.0*_x, 1.0/_a))/2.0;
  }
  else {
    _y = 1.0 - (pow(2.0*(1.0-_x), 1.0/_a))/2.0;
  }
  return(_y);
}
 
function exponential (_x){
  return((_x == 0.0) ? _x : pow(2, 10 * (_x - 1)));
}
 
function bounceIn (_x){
  return(1 - this.bounceOut(1 - _x));
}
 
function bounceOut (_x){
    if(_x &lt; 4/11.0)
    {
      return((121 * _x * _x)/16.0);
    }
    else if(_x &lt; 8/11.0)
    {
      return((363/40.0 * _x * _x) - (99/10.0 * _x) + 17/5.0);
    }
    else if(_x &lt; 9/10.0)
    {
      return((4356/361.0 * _x * _x) - (35442/1805.0 * _x) + 16061/1805.0);
    }
    else
    {
      return((54/5.0 * _x * _x) - (513/25.0 * _x) + 268/25.0);
    }
}
 
function bounceInOut (_x){
      if(_x &lt; 0.5)
    {
      return(0.5 * this.bounceIn(_x*2));
    }
    else
    {
      return(0.5 * this.bounceOut(_x * 2 - 1) + 0.5);
    }
}
 
function exponentialEmphasis (_x, _a){
    if(!_a) _a = 0.25; // default
 
    var min_param_a = 0.0 + Number.EPSILON;
    var max_param_a = 1.0 - Number.EPSILON;
    _a = constrain(_a, min_param_a, max_param_a);
 
    if (_a &lt; 0.5) {
      // emphasis
      _a = 2*(_a);
      var _y = pow(_x, _a);
      return(_y);
    }
    else {
      // de-emphasis
      _a = 2*(_a-0.5);
      var _y = pow(_x, 1.0/(1-_a));
      return(_y);
    }
}

airsun-Reading02

Question 1A. Be sure to have read the first 20 pages of "Generative Art Theory" by Philip Galanter (p.146-166). In your own words, and in just a few sentences, discuss an example of something you like which exhibits effective complexity. (It doesn't have to be something made by people.) Where does your selection sit between total order (e.g. crystal lattice) and total randomness (e.g. gas molecules, white noise, static)? Include an image which illustrates your selected thing.

My understanding for a project being effectively complex is: 1. having a combination of both ordered and disordered systems 2. the whole concept do not only work in one direction linearly but in a random direction and cannot be easily predicted. Therefore, my first thought is to find works that incorporate living objects (because they are less likely to be controlled). For example, a coded ecosystem where things like animals and human beings can act like the unpredictable randomness. However, on my way of finding this kind of project, I encountered "inFORM". "inFORM" is a Dynamic Shape Display that can render 3D content physically, so users can interact with digital information in a tangible way. On one hand, this project generates precisely and predictably when displaying three-dimensional math functions: e.g. plotting hyperbolic paraboloids with the combination of cubes. However, on the other hand, this project also incorporates psychological and physiological elements to increase its complexity. First of all, it reacts to physical human actions. For example, it can replicate actions to interact with a red ball. Moreover, it motivates people to do in ways that hint them to do certain actions. For instance, the cubes will form a changing wave to make the participant's phone constantly moving when someone is calling. In this sense, because the work corresponds with these sort of actions, it incorporates a large extent of randomness when our actions became random and unpredictable. In this sense, I found "inFORM" to be an example exhibiting effective complexity. Moreover, it sits closer to the "total order" end.

Question 1B. Quickly skim the remaining 10 pages of Galanter's article, in which he outlines nine different problems with generative art (The Problem of Authorship; The Problem of Intent; The Problem of Uniqueness; The Problem of Authenticity; The Problem of Dynamics; The Problem of Postmodernity; The Problem of Locality, Code, and Malleability; The Problem of Creativity; The Problem of Meaning). Select one of these problems for which you yourself feel some sort of internal conflict or personal stake. Discuss your internal conflict. Which side of the argument do you come down on?

The Relationship between Works of Art and the Audience - in Response to the Change of Aura
Having read Walter Ben "The Work of Art in the Age of Mechanical Reproduction", I remember figuring out how the development of technology has evoked reproduction of artworks with the medium such as films and photographs. These inventions had rendered and manipulated the aura of artwork and its relationship with the audience. With mass production, not only the reproduced artwork can never fully present the original work with its unique aura, the original work also loses a sense of authenticity and authority by being depreciated. However, is it the same for generative art? For generative art, what is being mass-produced are unique objects instead of copies. Therefore, the idea of aura and the relationship between the audience and the artwork remains ambiguous to me. What generative art brings us is so different from the "traditional" painting and sculptures. Not only the context changed (our understanding and familiarity with technology and our acceptance of what counts as art ), the way we interact with the artwork also changed. For example, we cannot touch a painting in the Le Louvre (or sometimes even taking a photo), however, for generative art, there are so many that welcomes the user to interact, by either pushing a key on your keyboard to generate work or to do some real actions.