yuvian-AnimatedLoop

For my animated loop, I kept the concept simple and straightforward and focused more on the expressiveness of the face. In my loop, I've made a crying face. The eyebrows raise and fall whereas the eyes open and squeeze shut. The lips stretch and thin out as well. The cheeks flush and pale. And finally, a tear drop falls from the left eye.

I think the design is successful as the colors work well together and the gif loops seamlessly. However, I wish I had had more time to create a more complex looping animation. In other words, I succeeded with the simplistic vision I had in mind but wished I had more time to create something more creative and complex.

function renderMyDesign (percent) {
 
  background(40, 54, 111);
  smooth();
 
  // face
  fill(236, 241, 217);
  ellipse(width/2, height/2, 520, 530);
 
  push();
  rectMode(CORNER);
  rect(0,0,width, 300);
  pop();
 
  // move everything down a bit
  translate(0, 120) 
 
  // cheeks
  if (percent >= 0 && percent <= 0.5) {
    var cheekColor = map(percent, 0, 1, 150, 40);
  }
  else if (percent > 0.5 && percent <= 1.0) {
    var cheekColor = map(percent, 0, 1, 40, 150);
  }
 
  fill(251, cheekColor, cheekColor, 80);
  ellipse(width/2 - 180, 230, 120, 60)
  ellipse(width/2 + 180, 230, 120, 60)
 
 
  // falling tear
  var ease = doubleExponentialSigmoid(percent); 
  ease = (ease + 0.3) % 1.0;
 
  var r = 15;
  var x = width/2 - 150;
  var topY = 220;
  var botY = height + 30;
  var yPosition = map(percent, 0, 3, topY, botY);
  var y = map(ease, 0, 1, topY, botY); 
 
  var triHeight = 12; 
 
  // tear shape
  fill(173, 206, 214);
  noStroke();
  triangle(x - r/2, y, x + r/2, y, x, y - r/2 - triHeight);
  ellipse(x, y, r - 1, r); 
 
  // eyes
  fill(10, 56, 164);
    stroke(10, 56, 164);
 
  var eyeRad = 70
  var eyeHeight = 30
 
  if (percent >= 0 && percent <= 0.5) {
    eyeRad = map(percent, 0, 1, 60, 80);
    eyeHeight = map(percent, 0, 1,30,10);
  }
  else if (percent > 0.5 && percent <= 1.0) {
    eyeRad = map(percent, 0, 1, 80, 60);
    eyeHeight = map(percent, 0, 1,10,30);
  }
 
  // left eye
  push();
  translate(-150,-130);
  beginShape();
  vertex(width/2 - eyeRad, height/2);
  bezier(width/2 - eyeRad, height/2, width/2, height/2 - eyeHeight, width/2, height/2 - eyeHeight, width/2 + eyeRad, height/2);
  vertex(width/2 + eyeRad, height/2);
  bezier(width/2 + eyeRad, height/2, width/2, height/2 + eyeHeight, width/2, height/2 + eyeHeight, width/2 - eyeRad, height/2);
  vertex(width/2 - eyeRad, height/2);
  endShape();
  pop();
 
  // right eye
  push();
  translate(150,-130);
  beginShape();
  vertex(width/2 - eyeRad, height/2);
  bezier(width/2 - eyeRad, height/2, width/2, height/2 - eyeHeight, width/2, height/2 - eyeHeight, width/2 + eyeRad, height/2);
  vertex(width/2 + eyeRad, height/2);
  bezier(width/2 + eyeRad, height/2, width/2, height/2 + eyeHeight, width/2, height/2 + eyeHeight, width/2 - eyeRad, height/2);
  vertex(width/2 - eyeRad, height/2);
  endShape();
  pop();
 
  // mouth from https://www.openprocessing.org/sketch/464492
 
  push();
 
 
  fill(184, 36, 58);
  noStroke();
  translate(width/2,350);
 
  // var yScale = map(percent, 0, 1, 1.5, 1)
  // scale(2,yScale)
 
  if (percent >= 0 && percent <= 0.5) {
    var yScale = map(percent, 0, 1, 1.3, 1)
    scale(2,yScale)
  }
  else if (percent > 0.5 && percent <= 1.0) {
    var yScale = map(percent, 0, 1, 1, 1.3)
    scale(2,yScale)
  }
 
  beginShape();
  vertex(-36,0);
  bezierVertex(-36,0,-30,-4,-26,-7);
  bezierVertex(-18,-14,-15,-19,-10,-19);
  bezierVertex(-8,-20,-4,-20,0,-15);
  vertex(0,-15);
  bezierVertex(4,-20,8,-20,10,-19);
  bezierVertex(15,-19,18,-14,26,-7);
  bezierVertex(30,-4,34,-1,36,0);
  vertex(36,0);
  bezierVertex(35,2,23,19,0,20);
  bezierVertex(-18,19,-23,13,-27,10);
  bezierVertex(-30,7,-35, 2,-36,0);
  endShape();
 
  pop();
 
  // nose
  fill(192, 205, 148);
  noStroke();
  rectMode(CENTER);
  rect(width/2, height/2 - 45, 11, 45, 5);
 
  //eyebrows
  fill(167, 12, 73);
 
  if (percent >= 0 && percent <= 0.5) {
    var eyebrowY = map(percent, 0, 1, height/2 - 200, height/2 - 240);
  }
  else if (percent > 0.5 && percent <= 1.0) {
    var eyebrowY = map(percent, 0, 1, height/2 - 240, height/2 - 200);
  }
 
  push();
  rectMode(CENTER);
  rect(width/2 - 150, eyebrowY, 130, 15, 10);
  pop();
 
  push();
  rect(width/2 + 150, eyebrowY, 130, 15, 10);
  pop();
 
  // hair
  translate(0,-120);
  rectMode(CORNER);
  fill(0);
  rect(0, 0, 100, height);
  rect(width-75, 0, 100, height);
  rect(width-100, 0, 8, height);
 
 
  beginShape();
  vertex(0,0);
  vertex(0, 100);
  bezierVertex(0,100, 400, 250, 600, 30);
  vertex(600,40);
  vertex(600,0);
  vertex(0,0);
  endShape();
 
}
 
// 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 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);
}