svitoora – 02 Variable Face

Supawat’s Portrait

// 
// Supawat Vitoorapakorn
// Svitoora@andrew.cmu.edu
// Section E
// 
// Variable Portrait
// This code is a spin-off of the variable face example on:
// https://courses.ideate.cmu.edu/15-104/f2017/week-2-due-sep-8/

// Global Setup
	// Global
	var width = 480;
	var height = 640;
	var BLACK = "#5c5e60";
	var BG = "#ebe7e0"
	var SKIN = "#fffbf3"

	// Simple beginning template for variable face.
	var eyeSize_cur = 20;
	var faceWidth_cur = 125;
	var faceHeight_cur = 150;
	var hat_height = -faceHeight_cur + .825 * height;
	var hat_width = faceWidth_cur * 1.25;
	var eye_blacksize = .75;
	var hat_max_cur = height * .20;
	var face_top = (height * .5) - faceHeight_cur
	var mustache_y_cur = (height / 2) + eyeSize_cur * 1.5
	var mustache_low_cur = mustache_y_cur + 10
	var mustache_width_cur = faceWidth_cur / 4;
	var mustache = false

	// Declaring Variable to be assigned in setup
	var eyeSize_next;
	var hat_max_next;
	var faceWidth_next;
	var faceHeight_next;
	var mustache_width_next;
	var mustache_low_next;
	var mustache_width_next;
	var mustache_ratio;
	var click = 0;

function setup() {
	createCanvas(480, 640);
	// Specified next iteration to avoid random in global 
	eyeSize_next = random(10, 30);
	faceWidth_next = random(75, 200);
	faceHeight_next = random(100, 200);
	hat_max_next = height * .1;
	face_top = (height * .5) - faceHeight_cur;
	mustache_low_next = mustache_y_cur + random(10, 30);
	mustache_width_next = faceWidth_cur * random(.1, .3);
	mustache_ratio = 1.1;
	mustache_y_next = (height / 2) + eyeSize_next * random(1, 2)
	mustache_choice = [false, false, true]; // 1/3 chance of getting mustache
}

function draw() {
	background(BG);
	stroke(BLACK);

	// Animation's Derivative for non-linear transition
		var d_faceWidth = (Math.abs(faceWidth_cur - faceWidth_next)) / 4;
		var d_faceHeight = (Math.abs(faceHeight_cur - faceHeight_next)) / 15;
		var d_eyeSize = (Math.abs(eyeSize_cur - eyeSize_next)) / 20;
		var d_hat_max = (Math.abs(hat_max_cur - hat_max_next)) * .25;
		var d_mustache_low = (Math.abs(mustache_low_cur - mustache_low_next)) /
			40;
		var d_mustache_width = (Math.abs(mustache_width_cur -
			mustache_width_next)) / 8;

	// Adding back derivative for non-linear animation
		// facewidth
		if (faceWidth_cur > faceWidth_next) {
			faceWidth_cur = faceWidth_cur - d_faceWidth;
		} else {faceWidth_cur = faceWidth_cur + d_faceWidth;}

		// faceHeight
		if (faceHeight_cur > faceHeight_next) {
			faceHeight_cur = faceHeight_cur - d_faceHeight;
		} else {faceHeight_cur = faceHeight_cur + d_faceHeight;}

		// eyeSize
		if (eyeSize_cur > eyeSize_next) {
			eyeSize_cur = eyeSize_cur - d_eyeSize;
		} else {eyeSize_cur = eyeSize_cur + d_eyeSize;}

		// Hat Max
		if (hat_max_cur > hat_max_next) {
			hat_max_cur = hat_max_cur - d_hat_max;
		} else {hat_max_cur = hat_max_cur + d_hat_max;}

		// Mustache Low
		if (mustache_low_cur > mustache_low_next) {
			mustache_low_cur = mustache_low_cur - d_mustache_low;
		} else { mustache_low_cur = mustache_low_cur + d_mustache_low;}

		// mustache_width
		if (mustache_width_cur > mustache_width_next) {
			mustache_width_cur = mustache_width_cur - d_mustache_width;
		} else {mustache_width_cur = mustache_width_cur + d_mustache_width;}

	// Draw
		// Face
		fill(SKIN);
		strokeWeight(1);
		face_top = (height * .5) - faceHeight_cur;
		strokeWeight(0);
		fill(SKIN);
		ellipse(width / 2, height / 2, faceWidth_cur, faceHeight_cur);
		strokeWeight(1);
		fill(SKIN);

		// Eye Whites
		var eyeLX = width / 2 - faceWidth_cur * 0.25;
		var eyeRX = width / 2 + faceWidth_cur * 0.25;
		ellipse(eyeLX, height / 2, eyeSize_cur, eyeSize_cur);
		ellipse(eyeRX, height / 2, eyeSize_cur, eyeSize_cur);
		noFill();

		// Eye Black
		fill(BLACK);
		ellipse(eyeLX, height / 2,
			eyeSize_cur * eye_blacksize, eyeSize_cur * eye_blacksize);
		ellipse(eyeRX, height / 2,
			eyeSize_cur * eye_blacksize, eyeSize_cur * eye_blacksize);

		// Hat
		rectMode(CENTER);
		hat_height = Math.abs(((height / 2) - eyeSize_cur * 2), face_top)
		hat_width = faceWidth_cur * 1.25;
		rect(width / 2, hat_height, hat_width, 1);

		// Hat Line
		rectMode(CORNERS);
		rect((width / 2) - faceWidth_cur / 2, hat_height,
			(width / 2) + faceWidth_cur / 2, hat_max_cur);

		// Brow
		noFill();
		arc(eyeLX, height / 2 - eyeSize_cur * .75,
			eyeSize_cur * 1.25, eyeSize_cur * 1.25,
			PI + ((2 * PI) * .10), 0 - ((2 * PI) * .10));
		arc(eyeRX, height / 2 - eyeSize_cur * .75,
			eyeSize_cur * 1.25, eyeSize_cur * 1.25,
			PI + ((2 * PI) * .10), 0 - ((2 * PI) * .10));

		// nose
		line(width / 2, (height / 2) + eye_blacksize * 1.2,
			width / 2, (height / 2) + eyeSize_cur);
		rectMode(CORNERS);
		rect((width / 2) - faceWidth_cur * .05, (height / 2) + eyeSize_cur,
			(width / 2) + faceWidth_cur * .05, (height / 2) + eyeSize_cur);

	// Moustache or No Moustache
		if (mustache == false) {
			// Mouth
			arc(width / 2, height / 2 + faceHeight_cur * .20,
				faceWidth_cur * .3, faceWidth_cur * .3,
				0 + ((2 * PI) * .1), PI - ((2 * PI) * .1));
		} else {
			// Mustache
			fill(BLACK);
			var mustache_y = (height / 2) + eyeSize_cur * 1.75
			quad((width / 2) - mustache_width_cur, mustache_y,
				(width / 2) + mustache_width_cur, mustache_y,
				(width / 2) + mustache_width_cur * mustache_ratio,
				mustache_low_cur,
				(width / 2) - mustache_width_cur * mustache_ratio,
				mustache_low_cur);}
}

function mousePressed() {
	// when the user clicks, the next face is randomly generated
	// and eased into via animation
	faceWidth_next = random(75, 200);
	faceHeight_next = random(100, 200);
	eyeSize_next = random(10, 30);
	eye_blacksize = random(.25, .75);
	hat_max_next = random(height * .1, hat_height - 60);
	mustache_y_next = (height / 2) + eyeSize_next * random(1, 2)
	mustache_low_next = mustache_y_next + random(20, 40);
	mustache_width_next = faceWidth_next * random(.1, .3);
	mustache_ratio = random(1, 2);
	mustache_width_cur = mustache_width_cur / 2; // Fans stache out
	mustache = random(mustache_choice);
	click += 1; // tacks amount of clicks
}

As a design and HCI student, one simple method I learn to humanize screen based technology is to make graphic transition less “jarring”. In real life, nature does not just change its shape based on an interaction. Nature morphs, pops, and grows into its next form; nature transitions. And within those transitions are specific qualities and characteristics that brings life and animate the dead into life.

To achieve this vivacity, I created a program that introduces an axis of time. A current state and next state variable is created, and every 60 fps the current state approaches the next state variable. Instead of using the traditional approach of +=1 which results in a linear transition, I created a differences variable(∆) that eases the transition by adding in a fraction of the difference between the current and next state. In pseudo-code, while(cur_state ≠ next state){ cur_state±(∆state*some fraction)}.

In actual code:


Looking back at this, the transition code part of this could be re-written as a function with multiple inputs to clean up the program as such:


Lastly, I chose warm monochromatic color scheme to make my generative appear warm and inviting.

Grays with warm tints and shades to convey warmth.

Initial hand sketch from sketchbook:

 

SaveSave

SaveSave

SaveSave

SaveSave

SaveSave

Leave a Reply