nannon-Reading-02

1A: Effective Complexity--Kyuha Shim's Generative Typography

In the past two years (mostly), I've become very interested in typography, how it moves and how it scales. However, it wasn't until last year that I really encountered Kyu's work as his student in his Computational Design class. Kyu's work in generative typography has greatly effected and also falls in line with broad movements towards parametric design in the visual design world. I love this work because it gives the very language and content of our world character and movement, engages the viewer in completely new ways. In terms of Galanter's article, I would say that this kind of generative typography, that is, typography that changes based on external input(s), still definitely leans towards order (crystal lattice). No matter what, typography is still driven by language/linguistics, and whatever alphabet its shape is taking. That line is extremely concrete. However, I love that it finally is offering a source of variation within the previously static world of type.

Peep it here: http://generativetypography.com/

1B: The problem of uniqueness

"Whether in the context of market value or cultural value, traditional works of art have been treasured as unique and thus rare objects. "

"Digital generative art introduces a completely new problem: rather than offering an endless supply of copies, it provides an endless supply of original and unique artifacts. "

Generative art is by nature repetitive in some way, and I don't believe the same problem of uniqueness that might arise in non-generative art can apply, at least without differentiation. When it comes to generative art, I believe that the more important point/source of uniqueness is the generator (code, etc), not the generated content.

chewie-Reading02


1A) Something that exhibits effective complexity is shaving. With the stroke of a razor, a 2 dimensional area of skin is shaved, removing the hair that's growing there. The goal of shaving is to remove enough of these areas around your face so that there's no hair left. The shapes of these areas have to be arranged so that:

-Every point on your face lies in one of the areas

-You don't shave the same place too many times and give yourself razor burn (these areas will have many areas overlapping them)

Most people do this by shaving an area at the edge of the face, and then removing the hair around it until the face is empty of hair or they start working on a different section. These areas are arranged and ordered so that the edge of each stroke  slightly overlaps the edge of the previous stroke. Longer strokes make for more efficient shaving because it reduces the number of strokes and the area of overlapping shapes.

Shaving is pretty chaotic because it depends on the person's abilities and methods of packing shapes into a area. Everyone who shaves is generally going to remove the hair that they want to remove, the most variation lies in the razor strokes that remove that area of hair.

1B) The Problem of Meaning touches on some interesting ideas in terms of finding purpose and meaning in generative art. The idea behind generative art is to create functioning systems where each component interacts with another in a ordered, calculated way. The reasoning and logic behind the systems operations or products is what is interesting about the art. If these systems are based on systems that already exist, the art becomes the study of that system and the ability to simulate it in a different medium (like simulating the way clouds form in code).

These systems are designed by artists with a product in mind. Despite the fact that a generative work may be based off of an existing natural system, the artist has to understand that system in order to exploit or reproduce it. It's a study of how humans reason over dynamic processes.

chewie-AnimatedLoop

My original goal in making this gif was to familiarize myself with loading animating picture files in p5.js. I started with a simple animation sequence that I processed in photoshop to isolate each element.

 

The hardest part was getting different cells to animate the frames at a different offset interval. The solution I used wasn't very elegant but in the future I think I'll set up some sort of data structure for frames that are being animated.

I used the PennerEaseInOutBack easing function to shift the entire frame of cells down smoothly. I think the smooth sweeping motion of the frame looks nice against the choppier, hand-drawn animation. The way it stretches slightly past the end point also makes the motion look like waves crashing. What I like about the gif is the way that the hand-drawn animations works with the perfect grid layout and smooth sweeping animation. The combination of those two qualities makes it satisfying for me to watch.

If I were to spend more time on it I would get more creative with the way that the animations are triggered. There are probably more interesting patterns and different ways activate the animation sequences of the folding papers.

 

// This is a template for creating a looping animation in p5.js (JavaScript). 
// 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;
 
// Other global variables you don't need to touch.
var nElapsedFrames;
var bRecording;
var theCanvas;
var frames = [];
var frameN = 0;
var unit;
var activeFrames = [];
var framesToDeact = [];
var levels = [[[4,4],[4,5],[5,4],[4,3],[3,4]],
 
               [[4,2],[3,2],[3,3],[2,3],[2,4],[2,5],[3,5],[3,6],[4,6],
                [5,6],[5,5],[6,5],[6,4],[6,3],[5,3],[5,2]],
 
               [[4,1],[3,1],[2,1],[2,2],[1,2],[1,3],[1,4],[1,5],[1,6],
                [2,6],[2,7],[3,7],[4,7],[5,7],[6,7],[6,6],[7,6],[7,5],
                [7,4],[7,3],[7,2],[6,2],[6,1],[5,1]],
 
               [[3,0],[2,0],[1,0],[1,1],[0,1],[0,2],[0,5],[0,6],[0,7],
                [1,7],[1,8],[2,8],[5,8],[6,8],[7,8],[7,7],[8,7],[8,6],
                [8,3],[8,2],[8,1],[7,1],[7,0],[6,0]],
 
               [[0,0],[0,3],[0,4],[0,8],[3,8],[4,8],[8,8],[8,5],[8,4],
                [8,0],[5,0],[4,0]]];
 
 
 
var levels1 = [[[4,4],[4,5],[5,4],[5,5]],
 
              [[3,3],[3,4],[3,5],[3,6],[4,6],[5,6],[6,6],[6,5],[6,4],
               [6,3],[5,3],[4,3]],
 
              [[2,2],[2,3],[2,4],[2,5],[2,6],[2,7],[3,7],[4,7],[5,7],
               [6,7],[7,7],[7,6],[7,5],[7,4],[7,3],[7,2],[6,2],[5,2],
               [4,2],[3,2]],
 
              [[1,1],[1,2],[1,3],[1,4],[1,5],[1,6],[1,7],[1,8],[2,8],
               [3,8],[4,8],[5,8],[6,8],[7,8],[8,8],[8,7],[8,6],[8,5],
               [8,4],[8,3],[8,2],[8,1],[7,1],[6,1],[5,1],[4,1],[3,1],
               [2,1]],
 
              [[0,0],[0,1],[0,2],[0,3],[0,4],[0,5],[0,6],[0,7],[0,8],
               [0,9],[1,9],[2,9],[3,9],[4,9],[5,9],[6,9],[7,9],[8,9],
               [9,9],[9,8],[9,7],[9,6],[9,5],[9,4],[9,3],[9,2],[9,1],
               [9,0],[8,0],[7,0],[6,0],[5,0],[4,0],[3,0],[2,0],[1,0]]];
 
 
//===================================================
function setup() {
  theCanvas = createCanvas(1920, 1920);
  bRecording = false;
  nElapsedFrames = 0;
  loadFrames();
  unit = width/9;
}
 
//===================================================
function keyTyped() {
  if (bEnableExport) {
    if ((key === 'f') || (key === 'F')) {
      bRecording = true;
      nElapsedFrames = 0;
      frameN = 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++;
    frameN ++;
 
    if (nElapsedFrames >= nFramesInLoop) {
      bRecording = false;
    }
  }
}
 
//===================================================
function renderMyDesign (percent) {
  if (percent >0.95) activeFrames = [];
  // 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(3);
  var inc = nElapsedFrames%6;
  var offset = height*function_PennerEaseInOutBack (percent);
  for (var row=0; row<9; row++) {
    for (var col=0; col<9; col++) {
      //offset=0;
      image(frames[(nElapsedFrames+row+col)%4],col*unit,row*unit+offset,unit,unit);
      image(frames[(nElapsedFrames+row+col)%4],col*unit,row*unit+offset-height,unit,unit);
    }
  }
  if (inc==0&&nElapsedFrames<30) {
    //start next tier of animation
    animateTier(int(nElapsedFrames/6));
  }
  drawActiveFrames(offset);
  for (var i=0; i<activeFrames.length; i++){//animate the active frames;
    activeFrames[i][2] = (activeFrames[i][2]+1)%frames.length;
    //if (activeFrames[i][2]>frames.length) framesToDeact.push(activeFrames[i]);
  }
 
}
 
// 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 loadFrames() {
  var fileName;
  for (var i=0; i<12; i++) {
    fileName = join(["frames/sqr_",str(i+1),".png"],"");
    frames.push(loadImage(fileName));
  }
  for (i=11; i>-1; i--) {
    fileName = join(["frames/sqr_",str(i+1),".png"],"");
    frames.push(loadImage(fileName));
  }
}
 
function animateTier(x) {
  var tier = levels[x];
  var xyt;
  var start = 0;
  for (var i=0; i<tier.length; i++) {
    xyt = [tier[i][0], tier[i][1], 0];
    activeFrames.push(xyt);
  }
}
 
function removeXYT(xyt, l) {
  for (var i=0; i<l.length; i++) {
    if (xyt[0]==l[i][0] && xyt[1]==l[i][1]) {
      l.pop(i);
      return;
    }
  }
  return;
}
 
function drawActiveFrames(offset) {
  var af;
  for (var i=0; i<activeFrames.length; i++) {
    af = activeFrames[i];
    //offset=0;
    image(frames[af[2]%frames.length], unit*af[1], unit*af[0]+offset, unit, unit);
  }
}
 
//The following function was taken from pattern_master - 
//https://github.com/golanlevin/Pattern_Master/blob/master/pattern_master/F00.pde
 
function function_PennerEaseInOutBack (x) {
 
  var s = 1.70158 * 1.525;
  x /= 0.5;
 
  var y = 0; 
  if (x < 1) {
    y = 1.0/2.0* (x*x*((s+1.0)*x - s));
  } 
  else {
    x -= 2.0;
    y = 1.0/2.0* (x*x*((s+1.0)*x + s) + 2.0);
  } 
  return y;
}

High-Res Gif (1920x1920):

nerual-Reading02

Question 1A.
I really like clouds. It's interesting that it can be easily represented, but they seem very difficult to model. The cloud itself is a collection of gas molecules distinctly different from the molecules around them, and that system interacts with the weather system, to change the shape of the cloud based on wind, temperature, and humidity. It falls more on the total randomness side, but it isn't quite as random as gas molecules alone.

The Problem of Postmodernity intrigues me because I'm personally a greater fan of more traditional art, and I disagree with the notion that the future of art means neglecting or leaving traditional mediums behind. I'm often more impressed with art that uses old techniques in new ways, rather than new techniques in new ways. But I accept that the prospect of a completely new framework to explore literally any aspect of life is exciting,and part of my reason for taking this class is to become more familiar with that.

nerual-AnimatedLoop

"a cursory moment" (get it?)

Perhaps it isn't worthy of a title, but I would like to informally dub it "A Cursory Moment."

The Process:

Some sketches:

 

I was going to follow up the idea of the mouse and pop signs by making the mouse "click" on a place and then bounce off it, like the force of the click sent it flying.  I got as far as making it move in the right physics-like snapping motion with the elastic function, but I didn't get the mouse to rotate, so I decided to play with contrast and repetition to make it look more interesting instead.

This was actually where I was going to stop:

I thought it looked like the James Bond opening, so I wanted to rotate the finger to make it a finger gun. I ended up with the rotated lines of circles lining up on accident and with some tweaking, I got all circles to line up. I thought it looked more interesting that way.

I felt like this work definitely turned out a lot better than expected(although I expected very little at that point). I like the way the repetition and the appearance of the hand go with the elastic movement. It's kind of rhythmic.

It definitely lacks a lot of the mathematical complexity that I would have liked to explore. The black and white was partially an aesthetic choice and mostly a practical one. The elastic movement is rather sloppy. Overall it's very simple. Way back in the beginning I had hoped to made something more along the lines of, well, lines, and I was really disappointed I couldn't manage to incorporate that at all. I think that with more time however, I definitely could have expanded on the concept or at least made the final product smoother.

I later went back and tweaked it a bit more:

Code (easing function from p5.func):

// based on p5.func examples - easing3_animation
// the sound is from the example
// I<3DM rld 


var ease = new p5.Ease();
var speed = 0.02;
var t = 0.;
var doclear;
var x, y, tx, ty, x1, y1;
var osc, rev;
var pause = 0;
var pauseTime = 60;
var fc=0;
var padding = 20;
var ex1,ey1,ex2,ey2,ex3,ey3,ex4,ey4;
var left,right;
var phase;
var startx,starty;
var radius, mr;
var debug = false;

function setup()
{
  frameRate(70);
  createCanvas(640, 640);
  
  radius = 120;
  mr = radius*0.1;
  
  ex1 = left;
  ey1 = height/5;
  ex2 = right;
  ey2 = height/3 + height/6;
  ex3 = left;
  ey3 = ey2 + height/;
  ey4 = 700;
  
  left = width/4;
  right = 3*left;
  phase = 0;
  
  background(255);

  startx = left;
  starty = -radius;
  x = startx;
  y = starty;
  tx = startx;
  ty = starty;
}

function draw()
{
  background(0);
  drawTheThing();
  if (debug){
    push();
    background(255);
    stroke(255);
    text(frameCount, 20,20);  
    pop();
  }
  if (frameCount > 30 && frameCount < 100 && (frameCount%4 ==0)){
    var filename = "myZoetrope_" + nf(frameCount,2) + ".png";
    saveCanvas(filename, 'png');
  }
}

function doPhase(){
    switch(phase){
      case 0:
        ty = starty;
        tx = left;
        phase++;
        break;
      case 1:
        ty = ey1;
        tx = left;
        phase++;
        break;
      case 2:
        ty = ey2;
        tx = right;
        phase++;
        break;
      case 3:
        ty = ey3;
        tx = left;
        phase++;
        break;
      case 4:
        ty = ey4;
        tx = right;
        phase=0;
        phase++;
        break;
      default:
        ty += height/6;
        if (tx==left) tx=right;
        else if(tx==right) tx=left;
        else tx = left;
        phase=0;
        redraw();
        break;
    }
}

function drawTheThing(){
  var func = "elasticOut";
  var q = ease[func](t*2.5);

  var diff = frameCount - fc;
  if (diff > pauseTime){
    doPhase();
    t = 0.;
    fc = frameCount;
  }
  else if (phase==0)
    doPhase();

  x1 = map(q, 0., 1., x, tx);
  y1 = map(q, 0., 1., y, ty);

  fill(255);
  stroke(0);
  ellipse(x1, y1, radius,radius);
  ellipse(x1+left, y1, radius,radius);
  ellipse(x1-left, y1, radius,radius);

  drawMouse(width-x1, height-y1, mr);
  drawMouse(width-x1+left, height-y1, mr);
  drawMouse(width-x1-left, height-y1, mr);

  t+=speed;
  if(t>1.) {
    t=1.;
    x = tx;
    y = ty;
  } 
}

function drawMouse(y,x,s) {
  push(); 
  stroke(255);
  noFill();
  strokeWeight(10);
  ellipse(x,y, 9*s,9*s);
  
  stroke(0);
  strokeWeight(5);
  translate(-1.5*s,0,5);
  rect(x,y,4*s,3*s,5);
  rect(x,y-3.5*s,s,3.5*s,5,5,0,0);
  rect(x+s,y-s,s,s,5,5,0,0);
  rect(x+2*s,y-s,s,s,5,5,0,0);
  rect(x+3*s,y-s,s,s,5,5,0,0);
  rect(x-s,y,s,2*s,5);
  pop();
}

}

Spoon-Scope

 

This design takes the bouncing rectangle design from my GIF and applies it to the format of a praxinoscope. The side ratios are slightly different, adjusted only so the rectangle would fill the frame well. The motion in the rectangle is fluid as it uses the same adjustable center cosine window function that is used in the GIF for the rectangle.

See my GIF post for sketches.

praxinoscope-output

 

 

// Template for KidzLabs/4M/Toysmith Animation Praxinoscope
// https://www.amazon.com/4M-3474-Animation-Praxinoscope/dp/B000P02HYC
// https://www.walmart.com/ip/Animation-Praxinoscope-Science-Kits-by-Toysmith-3474/45681503
// Developed for Processing 3.3.6 * http://processing.org
// 23 January 2018 * Golan Levin 
 
// See information about Processing PDF export at: 
// https://processing.org/reference/libraries/pdf/index.html
// PDF generated by Processing can be opened in Adobe Illustrator.
import processing.pdf.*;
boolean bRecordingPDF = false;
 
float inch = 72; 
float diamArtInner = inch * 1.50; 
float diamArtOuter = inch * 4.80; 
float diamCutInner = inch * 1.41; 
float diamCutOuter = inch * 4.875; 
float holeDy = inch * 0.23;
float holeDx = inch * 0.20;
float holeD = inch * 0.1;
 
final int nFrames = 10; 
int myFrameCount = 0;
int exportFrameCount = 0; 
boolean bAnimate = true; 
boolean bExportFrameImages = false;
 
//-------------------------------------------------------
void setup() {
  size(792, 612); // 11x8.5" at 72DPI
  frameRate(15);
  smooth();
} 
 
//-------------------------------------------------------
void draw() {
  background(240); 
  if (bRecordingPDF) {
    beginRecord(PDF, "praxinoscope-output.pdf");
  }
 
  // Do all the drawing. 
  pushMatrix(); 
  translate(width/2, height/2);
  drawCutLines(); 
  drawGuides(); 
  drawAllFrames();
  popMatrix();
 
  if (bExportFrameImages) {
    // If activated, export .PNG frames 
    if (exportFrameCount &lt; nFrames) { String filename = "frame_" + nf((exportFrameCount%nFrames), 3) + ".png"; saveFrame("frames/" + filename); println("Saved: " + filename); exportFrameCount++; if (exportFrameCount &gt;= nFrames) {
        bExportFrameImages = false;
        exportFrameCount = 0;
      }
    }
  }
 
  if (bRecordingPDF) {
    endRecord();
    bRecordingPDF = false;
  }
}
 
 
//-------------------------------------------------------
void keyPressed() {
  switch (key) {
  case ' ': 
    // Press spacebar to pause/unpause the animation. 
    bAnimate = !bAnimate;
    break;
 
  case 'p': 
  case 'P':
    // Press 'p' to export a PDF for the Praxinoscope.
    bRecordingPDF = true; 
    break;
 
  case 'f': 
  case 'F': 
    // Press 'f' to export .png Frames (to make an animated .GIF)
    myFrameCount = 0; 
    exportFrameCount = 0; 
    bExportFrameImages = true;
    bAnimate = true; 
    break;
  }
}
 
//-------------------------------------------------------
void drawCutLines() {
  fill(0); 
  textAlign(CENTER, BOTTOM); 
  text("Praxinoscope Template", 0, 0-diamCutOuter/2-6); 
 
  stroke(0); 
  strokeWeight(1.0);
 
  noFill(); 
  if (!bRecordingPDF) {
    fill(255); 
  }
  ellipse(0, 0, diamCutOuter, diamCutOuter);
 
  noFill(); 
  if (!bRecordingPDF) {
    fill(240); 
  }
  ellipse(0, 0, diamCutInner, diamCutInner);
 
  noFill(); 
  ellipse(diamCutOuter/2 - holeDx, 0-holeDy, holeD, holeD); 
 
  line (diamCutInner/2, 0, diamCutOuter/2, 0);
}
 
//-------------------------------------------------------
void drawGuides() {
  // This function draws the guidelines. 
  // Don't draw these when we're exporting the PDF. 
  if (!bRecordingPDF) {
 
    noFill(); 
    stroke(128); 
    strokeWeight(0.2); 
    ellipse(0, 0, diamArtInner, diamArtInner); 
    ellipse(0, 0, diamArtOuter, diamArtOuter);
 
    for (int i=0; i&lt;nFrames; i++) {
      float angle = map(i, 0, nFrames, 0, TWO_PI); 
      float pxi = diamArtInner/2 * cos(angle);
      float pyi = diamArtInner/2 * sin(angle);
      float pxo = diamArtOuter/2 * cos(angle);
      float pyo = diamArtOuter/2 * sin(angle);
      stroke(128); 
      strokeWeight(0.2);
      line (pxi, pyi, pxo, pyo);
    }
 
    // Draw the red wedge outline, highlighting the main view.
    int redWedge = 7; // assuming nFrames = 10
    for (int i=redWedge; i&lt;=(redWedge+1); i++) {
      float angle = map(i, 0, nFrames, 0, TWO_PI); 
      float pxi = diamArtInner/2 * cos(angle);
      float pyi = diamArtInner/2 * sin(angle);
      float pxo = diamArtOuter/2 * cos(angle);
      float pyo = diamArtOuter/2 * sin(angle);
      stroke(255, 0, 0); 
      strokeWeight(2.0);
      line (pxi, pyi, pxo, pyo);
    }
    noFill(); 
    stroke(255, 0, 0); 
    strokeWeight(2.0);
    float startAngle = redWedge*TWO_PI/nFrames;
    float endAngle = (redWedge+1)*TWO_PI/nFrames;
    arc(0, 0, diamArtInner, diamArtInner, startAngle, endAngle); 
    arc(0, 0, diamArtOuter, diamArtOuter, startAngle, endAngle); 
 
 
    for (int i=0; i&lt;nFrames; i++) {
      float angle = map(i, 0, nFrames, 0, TWO_PI); 
 
      pushMatrix();
      rotate(angle); 
      float originY = ((diamArtOuter + diamArtInner)/2)/2;
      translate(0, 0-originY); 
 
      noFill(); 
      stroke(128); 
      strokeWeight(0.2);
      line (-inch/2, 0, inch/2, 0); 
      line (0, -inch/2, 0, inch/2); 
 
      popMatrix();
    }
  }
}
 
//-------------------------------------------------------
void drawAllFrames() {
  for (int i=0; i&lt;nFrames; i++) {
    float angle = map(i, 0, nFrames, 0, TWO_PI); 
    float originY = ((diamArtOuter + diamArtInner)/2)/2;
 
    pushMatrix();
    rotate(angle); 
    translate(0, 0-originY); 
    scale(0.8, 0.8); // feel free to ditch this 
 
    int whichFrame = i; 
    if (bAnimate) {
      whichFrame = (i+myFrameCount)%nFrames;
    }
    drawArtFrame (whichFrame); 
    // drawArtFrameAlternate (whichFrame); 
 
    popMatrix();
  }
  myFrameCount++;
}
 
 
//-------------------------------------------------------
void drawArtFrame (int whichFrame) { 
  // Draw the artwork for a generic frame of the Praxinoscope, 
  // given the framenumber (whichFrame) out of nFrames.
  // NOTE #1: The "origin" for the frame is in the center of the wedge.
  // NOTE #2: Remember that everything will appear upside-down!
 
  fill(0); 
  noStroke(); 
 
  float multiplier2 = function_AdjustableCenterCosineWindow(
                                              map(whichFrame, 
                                              0, nFrames, 0, 1), 
                                              0.3);
  drawCenteredRectangleFromBottom(0, 30, 
                                  map(multiplier2, 0, 1, 35, 65),
                                  map(multiplier2, 0, 1, 100, 50));
  noFill();
  stroke(255);
  strokeWeight(5);
}
 
//-------------------------------------------------------
 
void drawCenteredRectangleFromBottom(float x, float y, 
                            float width, float height) {
  float upperLeftX = x - (width / 2);
  float upperLeftY = height - y;
  rect(upperLeftX, upperLeftY, width, -height);
}
 
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 &lt;= 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;
}

Spoon-Reading02

1A. Strandbeests, the gigantic sculptures created by Theo Jansen that use wind power to walk up and down the beaches of the Netherlands are an example of effective complexity. They exhibit qualities of both randomness and order. The mechanism for the legs was developed through an evolution simulation (randomness). The geometry of the legs is rather simple, but when assembled together, the legs create an intricate walking thing. There is a formal order to the mechanics of the creatures, which lends itself to the graceful movements of the sculptures. In this case, the randomness led to an order, mimicking our own development as a species.  Some of the Strandbeests even feature abilities to respond to their environments -- they are able to turn away from water or anchor themselves in the case of too much wind.

 

1B. The problem of uniqueness: As mentioned in the essay, part of the value of art for a long time has been its one-of-a-kind nature. While I understand that rarity has value, I think we often place too much emphasis on it. Valuable art is art that you can develop a connection with. It doesn't seem particularly important whether everybody else also has access to the same art. In fact, I think art has the potential to be even more powerful when it can be mass produced. If a piece of art can be mass produced, whether each piece is unique as is the case for some pieces of generative art, more people can access it, and, therefore, more people have the opportunity to derive meaning from it. If enough people derive meaning from a piece, that piece might have the power to effect society on a macro scale.

lass-AnimatedLoop

For my looping gif, I was really interested in doing a pinching/pulling motion, similar to cell division. At first I was very unsure of how to do this, since I had seen it before but didn't know a technical term. Connie Ye told me to look into metaballs, and I learned the process for making them from this coding challenge by Dan Shiffman. My initial plan involved several components, but after I had gotten the metaballs to work initially, many people urged me to keep it simple instead. I ended up only creating the first main component from the sketches below.   

In the end, I'm glad I went with the simple route. It allowed me to have more flexibility with colors, and it resulted in something much cleaner and not so chaotic. In the future I might try to create something more complex with the code that I wrote, since I planned it out to allow for several "groupings" of metaballs. I think it might be very laggy.

Thank you Golan, Connie, and Lauren for helping me out with this project!

debug view: 

BallGroup metaballs;
int numFrames = 30;
int currentFrame = 0;
int colorChoice = 0;
color[] ballColors, backgroundColors;
 
void setup() {
 size(640, 640);
 noStroke();
 
 ballColors = new color[] {
  color(250, 192, 133), color(169, 67, 117), color(50, 70, 171)
 };
 backgroundColors = new color[] {
  color(200, 94, 141), color(250, 200, 113), color(169, 200, 117)
 };
 
 metaballs = new BallGroup();
 metaballs.addBall(new Metaball(width / 2, height - 40, width / 2, -100, 80, 80));
 metaballs.addBall(new Metaball(width / 2, -100, width / 2, -100, 80, 0));
 metaballs.addBall(new Metaball(width / 2 - 50, height - 50, width / 2 - 50, height - 50, 130, 130));
 metaballs.addBall(new Metaball(width / 2 + 50, height - 50, width / 2 + 50, height - 50, 130, 130));
 metaballs.addBall(new Metaball(width / 2, height * 2, width / 2, height - 40, 0, 80));
 
 metaballs.setColors(ballColors[0], ballColors[1]);
}
 
void draw() {
 currentFrame = (currentFrame + 1) % 30;
 if (currentFrame == 0) {
  colorChoice = (colorChoice + 1) % 3;
  metaballs.setColors(ballColors[colorChoice], ballColors[(colorChoice + 1) % 3]);
 }
 float movement = easing(1.0 * currentFrame / numFrames);
 metaballs.fadeCol = (lerpColor(ballColors[(colorChoice + 1) % 3], ballColors[(colorChoice + 2) % 3], movement));
 background(lerpColor(ballColors[(colorChoice + 0) % 3], ballColors[(colorChoice + 1) % 3], movement));
 //background(255, 200, 200); 
 metaballs.update(movement);
 metaballs.show();
}
 
float easing(float x) {
 //this is where i would put an easing function if i had one 
 return x;
}
 
class Metaball {
 PVector position;
 PVector beginning;
 PVector destination;
 float radius;
 float radius1;
 float radius2;
 Metaball(float x1, float y1, float x2, float y2, float r1, float r2) {
  position = new PVector(x1, y1);
  beginning = new PVector(x1, y1);
  destination = new PVector(x2, y2);
  radius = 100;
  radius1 = r1;
  radius2 = r2;
 }
 void show() { //shows the centers (for debugging) 
  noFill();
  stroke(0);
  strokeWeight(1);
  //ellipse(position.x, position.y, radius, radius);
 }
 void update(float movement) {
  position = position.lerp(beginning, destination, movement);
  radius = lerp(radius1, radius2, movement);
 }
}
 
class BallGroup {
 ArrayList < Metaball > arr;
 color startCol;
 color endCol;
 color col;
 color fadeCol;
 
 BallGroup() {
  arr = new ArrayList < Metaball > ();
  col = color(0, 0, 0);
  startCol = color(0, 0, 0);
  endCol = color(0, 0, 0);
 }
 void setColor(color c) {
  col = c;
 }
 void setColors(color c1, color c2) {
  startCol = c1;
  endCol = c2;
 }
 void addBall(Metaball mb) {
  arr.add(mb);
 }
 void show() {//metaball code from Dan Shiffman: https://www.youtube.com/watch?v=ccYLb7cLB1I
  loadPixels();
  for (int x = 0; x < width; x++) {
   for (int y = 0; y < height; y++) {
    float sum = 0;
    for (Metaball mb: arr) {
     float dst = dist(x, y, mb.position.x, mb.position.y);
     sum += 100 * mb.radius / dst;
    }
    if (sum > 200) {
     //adds a border
     //if (sum < 220)
     //  pixels[x + y * width] = col; 
     //else
     pixels[x + y * width] = lerpColor(col, fadeCol, y * 1.0 / height);
    }
   }
  }
  updatePixels();
  for (Metaball mb: arr)
   mb.show();
 }
 void update(float movement) {
  for (Metaball mb: arr)
   mb.update(movement);
  col = lerpColor(startCol, endCol, movement);
 }
}

paukparl-AnimatedLoop

thoughts:
I wanted to make a series of simple animations with circles as a main theme. But ended up only making two. I wanted to try the blur effect too, but spent too much time figuring out how to export frames from p5 without randomly skipping any frames. But I just gave up. I think slowing down the frame rate might be a solution, although I haven't tried it. Apart from that, I had fun.

gif:

 

 

...
void renderMyDesign (float percent) {
  float x1 = cos(percent*6*PI);
  float x2 = -cos(percent*6*PI);
  float z1 = sin(percent*6*PI);
  float z2 = -sin(percent*6*PI);
  noStroke();
  background(0, 0, 200);
 
 
  translate(width/2, height/2);
  rotate(2*PI*percent);
  if (percent%(1/3f) &lt; 1/6f) {
    fill(255);
    ellipse(
      map(x1, -1, 1, -160, 160), 
      0, 
      200+ map(z1, -1, 1, -80, 80), 
      200+ map(z1, -1, 1, -80, 80));
    fill(0);
    ellipse(
      map(x2, -1, 1, -160, 160), 
      0, 
      200+ map(z2, -1, 1, -80, 80), 
      200+ map(z2, -1, 1, -80, 80));
  } else {
    fill(0);
    ellipse(
      map(x2, -1, 1, -160, 160), 
      0, 
      200+ map(z2, -1, 1, -80, 80), 
      200+ map(z2, -1, 1, -80, 80));
    fill(255);
    ellipse(
      map(x1, -1, 1, -160, 160), 
      0, 
      200+ map(z1, -1, 1, -80, 80), 
      200+ map(z1, -1, 1, -80, 80));
  }
}
...
var frame = 0;
var phase1 = 30;
var phase2 = 50;
var phase3 = 150; // start falling
var phase4 = 160;
var phase5 = 190;
 
var SIZE = 120;
 
var r = 255;
var g = 255;
var b = 255;
 
var cnv;
 
function setup() {
  cnv = createCanvas(640/2, 640/2);
  noStroke();
}
 
function draw() {
  background(225, 0, 100);
 
 
  if (frame &lt;= phase2) { translate(0, map(pennerEaseOutCubic(frame/phase2), 0, 1, -60/2, 520/2)); } // horizontal oscillation if (frame &gt; phase1 &amp;&amp; frame &lt; phase4) { var osc = map(sin((frame-phase2)/PI*2), -1, 1, -50/2, 50/2); var oscScale = reverseSineEase(map(frame, phase1, phase4, 0, 2*PI)); translate(osc*oscScale, 0); // ellipse(width/2, 0, reverseSineEase(map(frame, phase1, phase4, SIZE, 400))); } if (frame &gt; phase2 &amp;&amp; frame &lt;= phase3) { translate(0, map(pennerEaseInOutQuad((frame-phase2)/(phase3-phase2)), 0, 1, 520/2, 200/2)); SIZE += 1.5/2; } if (frame &gt; phase3) {
    translate(0, map(pennerEaseInCubic((frame-phase3)/(phase5-phase3)), 0, 1, 200/2, 840/2)); 
  }
 
  if (frame &gt; phase2 + 20 &amp;&amp; frame &lt; phase4) { r -= 3.5; g -= 3.5; b -= 3.5; } fill(r, g, b); ellipse(width/2, 0, SIZE); frame++; if (frame&gt;phase5) {
    frame=0;
    SIZE = 120;
    r=255;
    g=255;
    b=255;
  }
}
 
function pennerEaseOutCubic(t) {
  t = t-1.0;
  return (t*t*t + 1);
}
 
function pennerEaseInCubic(t) {
  return t*t*t;
}
 
function reverseSineEase(t) {
  return ((1-cos(t))/2)*((1-cos(t))/2);
}
 
function pennerEaseInOutQuad (t) {
  if ((t/=1/2) &lt; 1) return 1/2*t*t;
  return -1/2 * ((--t)*(t-2) - 1);
}

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 &gt;= 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 &lt; 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 &lt; 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&lt;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 &lt; 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&lt;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&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;
}
 
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&lt;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&lt;=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 &lt; 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;
}