Spoon-Clock

Instead of creating a clock that displayed time in a useful manner, I decided to explore the experience of time. The resulting product is a giant eye that looks at the audience all day. It wakes up at 6AM every morning, blinks on a random cycle (the blinking motion is powered by an adjustable-center elliptic window function), and it falls asleep by 11PM every night. To reflect the light changing throughout the day, the creature's skin gets darker at night and lighter during the day. In addition while the creature sleeps, a monster wakes up to haunt you in the night. Rather than a green eye, the monster has a bright yellow eye. Rather than being awake from 6 to 11, the monster is awake from 1AM to 4AM.

I am happy with the way this piece turned out, as there are a few details I paid close attention to that came out nicely: the motion of the blinking, the colors, the fact that the creature slowly wakes up and falls asleep. There are a few details, though, that I am unhappy with or torn about. The pupil of the monster's eye could have been more feline rather than owl-like, but I couldn't quite make up my mind which to go with. Also, when the creature and the monster blink, the rounded stripes are not a constant width the whole way through. I was unable to figure out the geometry necessary to get the curves to line up perfectly, and this bugs me.

var STRIPE_WIDTH = 20;
var frameCounter;
var dayTime;
var midnight;
var lightness;
var human;
var monster;
 
 
function setup() {
	createCanvas(740, 740); 
	frameCounter = 0.0;
}
 
function draw() {
	checkTime();
	createStripes(color(255 * lightness, 100 * lightness, 100 * lightness));
	if(human) {
		createEye(width / 2, height / 2 + 10, 300, 160, color(0), 
							color(255 * lightness, 100 * lightness, 100 * lightness), 
							color(5, 150, 90));
	} else if(monster) {
		createMonsterEye(width / 2, height / 2 + 10, 300, 160, color(0), 
							color(255 * lightness, 100 * lightness, 100 * lightness), 
							color(255, 198, 0));
	}
}
 
function createStripes(c) {
	strokeWeight(STRIPE_WIDTH);
	for(var i = STRIPE_WIDTH / 2; i < height; i += STRIPE_WIDTH) {
		if((i - 10) % 40 == 0) stroke(0);
		else stroke(c);
		line(0, i, width, i);
	}
}
 
function createEye(x, y , eyeWidth, eyeHeight, stripe1, stripe2, iris) {
	noStroke();
	fill(255);
	ellipse(x, y, eyeWidth, eyeHeight);
	fill(iris);
	ellipse(x, y, eyeHeight, eyeHeight);
	fill(0);
	ellipse(x, y, eyeHeight / 2, eyeHeight / 2);
 
	noFill();
	var blinkNum = blink();
	for(var i = max((height - y) / 20, y / 20) - 1; i >= 0; i--) {
		i % 2 == 1 ? stroke(stripe2) : stroke(stripe1);
		bezier(x - eyeWidth / 2 - 6 * i + 5, y - 10 - i * 20 - 10, 
					 (x - eyeWidth / 2) + blinkNum * (eyeWidth / 7.5) + 5, 
					 y - 10 - blinkNum * dayTime * eyeHeight / 1.6 - i * 20 - 10,  
					 (x + eyeWidth / 2) - blinkNum * (eyeWidth / 7.5) - 5, 
					 y - 10 - blinkNum * dayTime * eyeHeight / 1.6 - i * 20 - 10,
					 x + eyeWidth / 2 + 6 * i - 5, y - 10 - i * 20 - 10);
 
		i % 2 == 1 ? stroke(stripe1) : stroke(stripe2);
		bezier(x - eyeWidth / 2 - 6 * i + 5, y + 10 + i * 20 + 10, 
					 (x - eyeWidth / 2) + blinkNum * (eyeWidth / 7.5) + 5, 
					 y + 10 + blinkNum * dayTime * eyeHeight / 1.6 + i * 20 + 10,  
					 (x + eyeWidth / 2) - blinkNum * (eyeWidth / 7.5) - 5, 
					 y + 10 + blinkNum * dayTime * eyeHeight / 1.6 + i * 20 + 10,
					 x + eyeWidth / 2 + 6 * i - 5, y + 10 + i * 20 + 10);
 
	}
	for(var i = max((height - y) / 20, y / 20) - 1; i >= 0; i--) {
		i % 2 == 1 ? stroke(stripe2) : stroke(stripe1);
		bezier(x - eyeWidth / 2 - 6 * i, y - 10 - i * 20, 
					 (x - eyeWidth / 2) + blinkNum * (eyeWidth / 7.5), 
					 y - 10 - blinkNum * dayTime * eyeHeight / 1.6 - i * 20,  
					 (x + eyeWidth / 2) - blinkNum * (eyeWidth / 7.5), 
					 y - 10 - blinkNum * dayTime * eyeHeight / 1.6 - i * 20,
					 x + eyeWidth / 2 + 6 * i, y - 10 - i * 20);
 
		i % 2 == 1 ? stroke(stripe1) : stroke(stripe2);
		bezier(x - eyeWidth / 2 - 6 * i, y + 10 + i * 20, 
					 (x - eyeWidth / 2) + blinkNum * (eyeWidth / 7.5), 
					 y + 10 + blinkNum * dayTime * eyeHeight / 1.6 + i * 20,  
					 (x + eyeWidth / 2) - blinkNum * (eyeWidth / 7.5), 
					 y + 10 + blinkNum * dayTime * eyeHeight / 1.6 + i * 20,
					 x + eyeWidth / 2 + 6 * i, y + 10 + i * 20);
	}
}
 
function createMonsterEye(x, y , eyeWidth, eyeHeight, stripe1, stripe2, iris) {
	noStroke();
	fill(iris);
	ellipse(x, y, eyeWidth, eyeHeight);
	fill(iris);
	ellipse(x, y, eyeHeight, eyeHeight);
	fill(0);
	ellipse(x, y, eyeHeight / 2, eyeHeight / 2);
 
	noFill();
	var blinkNum = blink();
	for(var i = max((height - y) / 20, y / 20) - 1; i >= 0; i--) {
		i % 2 == 1 ? stroke(stripe2) : stroke(stripe1);
		bezier(x - eyeWidth / 2 - 6 * i + 5, y - 10 - i * 20 - 10, 
					 (x - eyeWidth / 2) + blinkNum * (eyeWidth / 7.5) + 5, 
					 y - 10 - blinkNum * midnight* eyeHeight / 1.6 - i * 20 - 10,  
					 (x + eyeWidth / 2) - blinkNum * (eyeWidth / 7.5) - 5, 
					 y - 10 - blinkNum * midnight * eyeHeight / 1.6 - i * 20 - 10,
					 x + eyeWidth / 2 + 6 * i - 5, y - 10 - i * 20 - 10);
 
		i % 2 == 1 ? stroke(stripe1) : stroke(stripe2);
		bezier(x - eyeWidth / 2 - 6 * i + 5, y + 10 + i * 20 + 10, 
					 (x - eyeWidth / 2) + blinkNum * (eyeWidth / 7.5) + 5, 
					 y + 10 + blinkNum * midnight * eyeHeight / 1.6 + i * 20 + 10,  
					 (x + eyeWidth / 2) - blinkNum * (eyeWidth / 7.5) - 5,
					 y + 10 + blinkNum * midnight * eyeHeight / 1.6 + i * 20 + 10,
					 x + eyeWidth / 2 + 6 * i - 5, y + 10 + i * 20 + 10);
 
	}
	for(var i = max((height - y) / 20, y / 20) - 1; i >= 0; i--) {
		i % 2 == 1 ? stroke(stripe2) : stroke(stripe1);
		bezier(x - eyeWidth / 2 - 6 * i, y - 10 - i * 20, 
					 (x - eyeWidth / 2) + blinkNum * (eyeWidth / 7.5), 
					 y - 10 - blinkNum * midnight * eyeHeight / 1.6 - i * 20,  
					 (x + eyeWidth / 2) - blinkNum * (eyeWidth / 7.5), 
					 y - 10 - blinkNum * midnight * eyeHeight / 1.6 - i * 20,
					 x + eyeWidth / 2 + 6 * i, y - 10 - i * 20);
 
		i % 2 == 1 ? stroke(stripe1) : stroke(stripe2);
		bezier(x - eyeWidth / 2 - 6 * i, y + 10 + i * 20, 
					 (x - eyeWidth / 2) + blinkNum * (eyeWidth / 7.5), 
					 y + 10 + blinkNum * midnight * eyeHeight / 1.6 + i * 20,  
					 (x + eyeWidth / 2) - blinkNum * (eyeWidth / 7.5), 
					 y + 10 + blinkNum * midnight * eyeHeight / 1.6 + i * 20,
					 x + eyeWidth / 2 + 6 * i, y + 10 + i * 20);
	}
}
 
function blink() {
	//increment frameCounter
	if(frameCounter >= 0.98) {
		frameCounter = 0.0;	
	} else if((frameCounter == 0.0 && random(1000) < 5) || 
						(frameCounter > 0.0 && frameCounter < 1.0)) {
		frameCounter += 0.03;
	} else {
		frameCounter = 0.0;
	}
	console.log(frameCounter);
	return 1 - function_AdjustableCenterEllipticWindow(frameCounter, 0.4);
}
 
function checkTime() {
	var h = hour();
	var m = minute();
	var s = second();
	if(h < 22 && h > 5) {
		dayTime = 1.0;
		human = true;
	} else if(h >= 23 || h < 5 || (h == 5 && m < 30)) {
		dayTime = 0.0;
		human = false;
	} else if(h == 22) {
		dayTime = 1.0 - (m * 60 + s) / 3600.0;
		human = true;
	} else {
		dayTime = ((m - 30) * 60 + s )/ 1800.0;
		human = true;
	}
 
	if(h > 6 && h < 19) {
		lightness = 1.0;	
	} else if(h > 20 || h < 4) {
		lightness = 0.1;
	} else if(h >= 19 && h <= 20) {
		lightness = map(((h - 19) * 3600 + m * 60 + s) / 3600, 0, 2, 0.1, 1);
	} else {
		lightness = map(((h - 4) * 3600 + m * 60 + s) / 3600, 0, 3, 0.1, 1);
	}
 
	if(h > 0 && h < 4) {
		midnight = 1.0;
		monster = true;
	} else if(h == 12) {
		midnight = (m * 60 + s) / 3600.0;
		monster = true;
	} else if(h == 4) {
		midnight = 1.0 - (m * 60 + s) / 3600.0;
		monster = true;
	} else {
		midnight = 0.0;
		monster = false;
	}
}
 
//borrowed from Golan Levin: https://github.com/golanlevin/Pattern_Master
function function_AdjustableCenterEllipticWindow(x, a){
  //functionName = "Adjustable-Center Elliptic Window";
 
  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);
 
  var 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;
}