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):