jamieh-final-project

*Move mouse around canvas to move particles

*Click and/or drag to add more particles

sketch

/*
Jamie Ho
jamieh@andrew.cmu.edu
10:30
Project 12
*/

var particles = [];		//array to store circle particles
var d;					//distance between two particles
var md;					//distance between particle and 
var r = 255;
var g = 255;
var b = 255;

function setup(){
	createCanvas(480, 480);
}

function draw(){
	background(0);
	//create particles and push into array
	p = new Particle();
	particles.push(p);
	//if mouse is pressed, then add more particles & invert background colour
	if(mouseIsPressed){
		particles.push(p);
		background(255);
	}
	//first for loop counting backwards of the array
	for(var i = particles.length-1; i > 0; i--){		
		particles[i].show();
		particles[i].update();
		//second for loop counting forwards of the array. two for loops needed to draw lines between all particles
		for(var j = 0; j < particles.length; j++){		
			//distance between two particles
			d = dist(particles[i].x, particles[i].y, particles[j].x, particles[j].y);
			//distance between particle and mouse
			md = dist(particles[i].x, particles[i].y, mouseX, mouseY);
			//if distance between two particles is less than 45 then lines will be blue and thicker
			if(d < 45){
				if(d < 25){
					stroke(102, 204, 255);
					strokeWeight(0.35);
				} else if(d < 45 & d > 25){
					//if mouse is pressed, inverse colours
					if(mouseIsPressed){
						stroke(0);
						strokeWeight(0.1);
					} else{
						stroke(255);
						strokeWeight(0.1);
					}
				}
				line(particles[i].x, particles[i].y, particles[j].x, particles[j].y);
			}
			//if the distance between particle and mouse is between range to determine whether or not to push particles away
			if(md < 50 & mouseIsPressed == false){
				if(particles[i].x > mouseX){
					particles[i].x += random(md/4, md/2);
				} else if(particles[i].x < mouseX){
					particles[i].x -= random(md/4, md/2);
				} else if(particles[i].y > mouseY){
					particles[i].y += random(md/4, md/2);
				} else if(particles[i].y < mouseY){
					particles[i].y -= random(md/4, md/2);
				}
			}
			//if the alpha is less than 0 and returns true
			//then that particle is "killed off" or removed from array
			if(particles[i].finished()){
				particles.splice(i, 1);
			}
		}
	}
}

class Particle{
	//defines locations of particles and velocities and alphas
	constructor(){
		//if mouse is pressed then particle shows up where mouse is clicked
		if(mouseIsPressed){
			this.x = mouseX;
			this.y = mouseY;
		} else {
		//otherwise anywhere on canvas
			this.x = random(width);
			this.y = random(height);
		}
		//size of particles
		this.cSize = random(2, 8);
		//velocities
		this.vx = random(-0.5, 0.5);
		this.vy = random(-0.5, 0.5);
		//brightness of circles
		this.alpha = 255;
	}
	//creates the particles
	show(){
		noStroke();
		if(mouseIsPressed){
			fill(r, g, b, this.alpha);
		} else {
			fill(255, this.alpha);
		}
		ellipse(this.x, this.y, this.cSize);
	}
	//to move the particles
	update(){
		//make particles move
		this.x += this.vx;
		this.y += this.vy;
		//conditions where if particles hit the four edges, bounce
		if(this.x > width-this.cSize/2){
			this.vx -= random(0.5, 1.5);
		} else if(this.x < 0+this.cSize/2){
			this.vx += random(0.5, 1.5);
		} else if(this.y > height-this.cSize/2){
			this.vy -= random(0.5, 1.5);
		} else if(this.y < 0+this.cSize/2){
			this.vy += random(0.5, 1.5);
		}
		//to decrease the brightness of particles
		this.alpha -= 1.25;
	}
	//to "kill off" particles
	finished(){
		return this.alpha < 0; 	//either true or false
	}
}

function mouseDragged(){
	if(particles.push(p) & mouseIsPressed){
		r = map(mouseY, 0, height, 100, 255);
		g -= random(2, 3);	
		b = map(mouseX, 0, width, 150, 255);
	}
}

For my final project, I wanted to work with objects again to become more familiar with it. I chose to use particles and linking those particles based on different conditions. The particles are also interactive so that they’re not just floating circles that move by itself randomly. And while the mouse is clicked, the colours invert to show something more geometrical based on the lines drawn between particles.

svitoora – Evolution

For best viewing experience, please open in full screen here, and let it run for a while in the background. Check on your simulation occasionally to see how they’re evolving.

You can click to add producers, but this is highly unrecommended as it will alter the carrying capacity of the system. I recommend simply letting the software simulation run in the background.

Evolution

// 
// Supawat Vitoorapakorn
// Svitoora@andrew.cmu.edu
// Section E


// EVOLUTION: An overview of genetic algorithms
// 
// Evoltuion is a software that simulates evolution over time
// of a specie of consumers. The three main conceps are:

// VARIABILITY
// At setup, a diverse pool of genetic material is randomly created.

// HERERDITY
// Every time two producers mate, it's geneteics are combined and passed down.

// SELECTION
// The consumer's speed is determine by its darkness.
// Over time natural selection will probabilistically occur.



// CHARACTERS
// 
// PRODUCERS:
// A certain amount of producers is randomly created every time interval.
// The producers gathers sunlight, grows, and if it exceeds a certain amount
// of calories it splits into two. Producers' calories can be consumed
// by consumers, and if producers' calories is negative it dies.

// CONSUMERS:
// A certain amount of consumers is randomly generated at setup.
// Consumers move around randomly searching for food (prdoucers).
// Each movement of a consumer consumes a certain amount of calories, and
// If a consumer's calorie is below zero, it dies. If a consumer's calorie
// reaches a certain threshold, it will stop searching for food and begin
// searching for a potential mate nearby to reproduce with.
// 
// Reproduction consumes energy, and the the consumer may die shortly after.


var time = 1; // Time variable
var w = 480; // width
var h = 480; // height

var turtles_AI = []; // array of consumers

var INDEX = 0; // ID for each turtle
var debug_mode = false; // true to see calorie transfer.


//------------------------------------------
// Creates Turtle
// Each turtle has fitness level based on its color's darkness
// The darker it is the faster it moves to food.
// Each turtle also has a certain amount of energy (calorie).
// Input: x,y,energy, color
// Output: Object turtle
function makeTurtle(tx, ty, energy = random(25, 300),
	c = color(random(0, 255), random(0, 255), random(0, 255))) {
	var turtle = {
		x: tx,
		y: ty,
		life_index: random(-1, 1), //random seed for perlin bheavior
		life_index2: random(-1, 1), //random seed for perlin bheavior
		x_dir: 1,
		y_dir: 1,
		c: c,
		energy: energy,
		index: INDEX,
		mating: false
	};
	turtle.fitness = fitness(turtle); //0 to 100
	turtle.diameter = map(turtle.energy, 0, 100, 1, 20);
	INDEX += 1;
	return turtle;
}

// Determines fitness of Turtle from 0 to 100%
// The darker the color the faster it is.
// Input: Color
// Out: fitness
function fitness(turtle) {
	R = red(turtle.c);
	G = green(turtle.c);
	B = blue(turtle.c);
	avg = (R + G + B) / 3 // 0-255
	return map(avg, 0, 255, 100, 0); //Darker the fitter
}

// Moves Turtle with noise
function turtle_move_AI() {
	speed = .045;
	for (i in turtles_AI) {
		life = turtles_AI[i].life_index;
		life2 = turtles_AI[i].life_index2;
		x_dir = turtles_AI[i].x_dir;
		y_dir = turtles_AI[i].y_dir;

		// Moves turtle with Perlin Noise
		SIZE = 10; //Radius of displacement
		turtles_AI[i].x += x_dir * (noise(time * life) - .5) * SIZE;
		turtles_AI[i].y += y_dir * (noise(time * life2) - .5) * SIZE;
		turtles_AI[i].energy -= .01; //Caloric Cost of movement


	}
}

// Contains any group of objects within the screen
// If object touch boundary of screen reverse direction
// Input: group
function contain(group) {
	for (i in group) {
		// Min
		if (group[i].x < 0) {
			group[i].x = 0
			group[i].x_dir *= -1;
			group[i].dx *= -1;
		}
		if (group[i].y < 0) {
			group[i].y = 0
			group[i].y_dir *= -1;
			group[i].dy *= -1;
		}
		// Max
		if (group[i].x > w) {
			group[i].x = w
			group[i].x_dir *= -1;
			group[i].dx *= -1;
		}
		if (group[i].y > h) {
			group[i].y = h
			group[i].y_dir *= -1;
			group[i].dy *= -1;
		}
	}
}

// Draws Turtle
function drawTurtle() {
	for (var i = 0; i < turtles_AI.length; i++) {
		d = map(turtles_AI[i].energy, 0, 100, 1, 20);
		fill(turtles_AI[i].c);
		ellipse(turtles_AI[i].x, turtles_AI[i].y, d, d)
		fill(255);
		textSize(10);
		if (debug_mode == true) {
			text(floor(turtles_AI[i].energy), turtles_AI[i].x, turtles_AI[i].y)
		}
	}
}

// Removes dead turtles
// If turtle has less than zero energy left it dies.
function turtle_update() {
	for (var i = 0; i < turtles_AI.length; i++) {
		if (turtles_AI[i].energy > 250) {
			turtles_AI[i].mating = true;
		}
		if (turtles_AI[i].energy < 0) {

			turtles_AI.splice(i, 1);
			return
		}
	}
}


//------------------------------------------
// Hunt Food
// Input: individuall turtle
// Output: individiual turtle's target
function hunt(turtle) {
	x = turtle.x;
	y = turtle.y;
	// If no food return false
	if (Object.keys(PRODUCERS) == 0) {
		return false
	} else {
		// Else target is the closest food
		target = {
			x: null,
			y: null,
			d: null
		};
		// Search all food
		for (j in PRODUCERS) {
			// Distance to food
			d_food = dist(x, y, PRODUCERS[j].x, PRODUCERS[j].y);
			// If target is null or closer than previous
			// Reassign target to closest producers
			if (target.d == null || d_food < target.d) {
				target.x = PRODUCERS[j].x;
				target.y = PRODUCERS[j].y;
				target.d = d_food;
				target.i = j
			}
		}
		return PRODUCERS[target.i];
	}
}

// Make all turtle hunt for Food
function HUNT() {
	for (var i = 0; i < turtles_AI.length; i++) {
		target = hunt(turtles_AI[i]);
		if (target != false & turtles_AI[i].mating == false) {
			moveToward(turtles_AI[i], target, turtles_AI[i].fitness);
			eat(turtles_AI[i], target)
		}
	}
}

// Makes Object X moves towards Object Y with V velocity
function moveToward(X, Y, V) {
	v = map(V, 0, 100, 0, .1);
	X.x = lerp(X.x, Y.x, v / 2);
	X.y = lerp(X.y, Y.y, v / 2);
}


// Eat food if food is inisde circle
// Input: individual food, FOOD array
function eat(turtle, target) {
	d = dist(turtle.x, turtle.y, target.x, target.y)
		// If food is inside turtle
	if (d < (turtle.diameter / 2)) {
		target.energy -= .2;
		turtle.energy += 1;
	}
}


//------------------------------------------
// Mating function of turtles
// Turtle becomes mature at 200 calories and seeks to reproduce
// Input: individual turtle
// Ouput: closest mateable target
function mate(turtle) {
	x = turtle.x;
	y = turtle.y;
	target = {
		x: null,
		y: null,
		d: null
	};
	// Search all potential mate
	mate_count = 0;
	for (var j = 0; j < turtles_AI.length; j++) {
		// If Mate-able and not self
		if (turtles_AI[j].mating == true & turtles_AI[j] != turtle) {
			mate_count += 1;
			d = dist(turtles_AI[j].x, turtles_AI[j].y, turtle.x, turtle.y)
			if (target.d == null || d < target.d) {
				target.x = turtles_AI[j].x;
				target.y = turtles_AI[j].y;
				target.d = d;
				target.i = j
			}
		}
	}
	// If there is no mate return false.
	if (mate_count == 0) {
		return false
	}
	// If there is mate return target.
	else {;
		return turtles_AI[target.i];
	}
}

// Makes turtles have sex.
// If mateable turtles touch one another they both lose 100 calorie
// and creates 1 baby. Turtle bcomes mateable at 200 calories.
function sex(turtle, target) {
	d = dist(turtle.x, turtle.y, target.x, target.y)
	if (d < turtle.diameter / 2) {
		turtle.energy -= 100;
		target.energy -= 100;
		// Genetic Averaging and Mutation
		c = lerpColor(turtle.c, target.c, random(.3, .7));
		x = (turtle.x + target.x) / 2;
		y = (turtle.y + target.y) / 2;
		turtles_AI.push(makeTurtle(x, y, 66, c))
	}
}


// Loop through turtles to and make them mate
function MATE() {
	for (var i = 0; i < turtles_AI.length; i++) {
		target = mate(turtles_AI[i]);
		if (target != false & turtles_AI[i].mating == true) {
			moveToward(turtles_AI[i], target, turtles_AI[i].fitness);
			sex(turtles_AI[i], target);
		}
	}
}


//------------------------------------------
// Control

// Adds producers where mouse is clicked
function mouseClicked() {
	// FOOD.push(new makeFood(mouseX, mouseY));
	producer = (new makeProducer(mouseX, mouseY, 30));
	PRODUCERS[time] = producer;
	print(PRODUCERS);
}

// Adds producers where mouse is dragged
function mouseDragged() {
	if (millisecond % 2 == 0) {
		producer = (new makeProducer(mouseX, mouseY, 30));
		PRODUCERS[time] = producer;
	}
}


//------------------------------------------
// Producers
// Make food from sunlight
// Grows overtime and increase cell amount
var PRODUCERS = {};

// Creates prodcuers that grows from light
// Producers are eaten by turtles
// Input: x,y, energy, dx, dy
function makeProducer(x, y, energy = 10, dx = 0, dy = 0) {
	this.x = x;
	this.y = y;
	this.life_index = random(-1, 1); //random seem for perlin beheavior
	this.life_index2 = random(-1, 1); //random seem for perlin beheavior
	this.energy = energy;
	this.c = color(0, 255 / 2, 0, 255 * random(.5, 1));
	this.mitosis = false;

	this.dx = dx;
	this.dy = dy;
}


// Draws producers
function drawProducer() {
	for (key in PRODUCERS) {
		x = PRODUCERS[key].x;
		y = PRODUCERS[key].y;
		c = PRODUCERS[key].c;
		energy = PRODUCERS[key].energy;
		fill(c);
		strokeWeight(1)

		// Perlin noise to size to give it life
		base_vivacity = 5;
		speed = 1
		life = base_vivacity * (sin(time / 5 * speed))

		// Make rectangles rotate randomly
		push();
		rectMode(CENTER);
		translate(x, y);
		rotate((noise(PRODUCERS[key].life_index) * 360));
		rect(0, 0, energy + life, energy + life);
		pop();

		// Debug mode
		if (debug_mode == true) {
			fill(255);
			textAlign(CENTER);
			text(round(PRODUCERS[key].energy), x, y);
		}
	}
}


// Makes producer grow and reproduce if it has enough energy
function growProducer() {
	for (key in PRODUCERS) {
		// Grow Producer
		life_index = PRODUCERS[key].life_index;
		PRODUCERS[key].energy += noise(time * life_index) / 4;
		// Reproduce
		if (PRODUCERS[key].energy > 50) {
			PRODUCERS[key].mitosis = true
		}
	}
}


// Producers preform mitosis by using it's energy to reproduce
function mitosisProducer() {
	for (key in PRODUCERS) {
		if (PRODUCERS[key].mitosis == true) {
			energy = PRODUCERS[key].energy
				// Create 2 new cells
			for (i = 0; i < 2; i++) {
				producer = (new makeProducer(
					PRODUCERS[key].x,
					PRODUCERS[key].y,
					5,
					random(-energy / 4, energy / 4),
					random(-energy / 4, energy / 4)));
				PRODUCERS[time] = producer;
				PRODUCERS[key].energy -= 25;
			}
		}
	}
}

// Basic Physics for producer while splittig
function mitosisPhysic() {
	for (key in PRODUCERS) {
		PRODUCERS[key].x += PRODUCERS[key].dx;
		PRODUCERS[key].y += PRODUCERS[key].dy;
		PRODUCERS[key].dx = lerp(PRODUCERS[key].dx, 0, .1);
		PRODUCERS[key].dy = lerp(PRODUCERS[key].dy, 0, .1);
	}

}


// Kills Producer if its energy is below 0.
function dieProducer() {
	for (key in PRODUCERS) {
		if (PRODUCERS[key].energy < 0) {
			delete PRODUCERS[key]
		}
	}
}

// Give Producer Perlin noise to make it look alive
function lifeProducer() {
	for (key in PRODUCERS) {
		SIZE = .5;
		lifeX = (noise(time * PRODUCERS[key].life_index) - .5) * SIZE;
		lifeY = (noise(time * PRODUCERS[key].life_index2) - .5) * SIZE;
		PRODUCERS[key].x += lifeX;
		PRODUCERS[key].y += lifeY;

	}
}


var producer_timer = 1;
// Adds new producer into the system every producer timer interval
function add_food(interval) {
	producer_timer += 1;
	print(producer_timer)
	if (producer_timer % 500 == 0) {
		for (var i = 0; i < random(0, 6); i++) {
			PRODUCERS[time] = (new makeProducer(random(0, w), random(0, h), 1));
			time += .1;
		}
	}
}


//------------------------------------------
// SETUP
function preload() {
	w = windowWidth;
	h = windowHeight;
}

// Creates petri dish
function setup() {
	createCanvas(w, h);
	background(255);
	num_node = 100;
	// Create a diverse genetic pool of consumers
	for (i = 0; i < num_node; i++) {
		t = makeTurtle(random(0, w), random(0, h));
		turtles_AI.push(t);
	}
	// Initial set of producers
	for (i = 0; i < num_node / 10; i++) {
		PRODUCERS[i] = (new makeProducer(random(0, w), random(0, h)));
	}
}

//------------------------------------------

function draw() {
	background(255, 255, 255, 255 * .33);
	millisecond = floor(millis()) % 2000;
	// Model
	time += .1;
	turtle_move_AI();
	contain(turtles_AI);
	contain(PRODUCERS);
	HUNT();
	MATE()
	turtle_update();

	// Producers 
	growProducer();
	dieProducer();
	mitosisProducer();
	mitosisPhysic();
	lifeProducer();
	add_food(1000);

	// Draw
	noStroke();
	drawTurtle();
	drawProducer();
}

Producer splits into two after growing big enough:

Producers splitting.

Consumers mate after it exceeds a certain caloric threshold and combines their genes with some amount of mutation:

Consumers mating.

At first, the simulation begins with a lot genetic diversity:

The simulation begins with a lot gentic diversity.

But over time due to natural selection for faster (darker) consumers, the genetic diversity is significantly decreased:

Over time natural selection reduces genetic diversity.

Interestingly, membrane-like structures also start to emerge from how food is being consumed. Here, the producers form a membrane around a dense cluster of producers that is rapidly splitting.

Membrane-like structures emerged although it wasn’t explicity programmed.

These membrane-like and clusters macrostructures were not explicitly programmed, they emerge probabilistically out of the specification and rules of this simulation. Here, a wall of producer is protecting a rapidly splitting colony of producers.

Wall of producers protecting a rapidly growing colony.

Group behavior also emerged out of the algorithm. Here, the producers began to form almost cell like clusters where food is rapidly consumed and reproduction distance is significantly decreased thereby creating a cell-like unit.

Individual cell-like clusters of rapidly reproducing and consuming begin to emerge.
Here, one cell-like cluster separately moves towards another group of producers.
Last colony of producers consumed.

As the system reaches its carrying capacity, the producer begins to starve and die.

No more food left.
Consumers begin to starve and die.
As food emerge, the cycle of population repeats.

And as this cycle of extreme population swing for both the producers and consumers restarts, the genetic diversity of population doesn’t. Over time, despite natural mutation rate, the consumer population becomes abosolutely homogenous without any genetic diversity. In real life, this means that the population is extremely susceptible to a disease that can complete wipe its whole population out. 

No more genetic diversity is left.

Conclusion:

  1. Features can be created implicitly through explicitly programming beheaviors. Alhtough certain feaures of this simulation wasn’t explicity programmed, it emerge through the algorithms of the character’s beheavior.
  2. A system of genetic evolution will never reach its ultimate form. Although the black ones were the fastest, they needed to reproduced with inferior consumers thereby creating sub par offpsrings. Over time, this means that humans as a geneticically evolving organsims will never reach our ultimate form, if such form exist.
  3. What happens when there is no genetic diversity? If life on earth continued to evolve due to natural selection and converge towards an ideal form and homogenity what would happen?

 

SaveSave

SaveSave

SaveSave

mjanco – Final Project – Section B

sketch

//Michelle Janco
//mjanco@andrew.cmu.edu
//Final Project - Section B

var fish = [];
var tree = .05;
var treeSpeed = .0009;
var lX = 300;
var song;

//yellow sky color
var yellowSkyR = 240;
var yellowSkyG = 215;
var yellowSkyB = 87;

//new sky color
var skyR = 217;
var skyG = 98;
var skyB = 88;

function setup() {
    createCanvas(640, 240);
    // create fish
    for (var i = 0; i < 5; i++){
        var rx = random(width);
        fish[i] = makefish(rx);
    }
  frameRate(30);
}

function draw() {
    background(yellowSkyR, yellowSkyG, yellowSkyB);
    displayHorizon();
    makeTree();
    updateAndDisplayfish();
    removefishThatAreOutOfView();
    addNewfishWithSomeRandomProbability();

    //change background color as mouse
    //moves across canvas
    if ((mouseX >= 640) & (mouseX <=640)){
      mouseX = 640;
  }
    if ((mouseX > 0) & (mouseX < 640)){
        yellowSkyR = mouseX*((217-240)/640) + 240;
        yellowSkyG = mouseX*((98-215)/640) + 215;
        yellowSkyB = mouseX*((88-87)/640) + 87;
    }

    //big cloud
    fill(255);
    ellipse(mouseX + 5, 55, 20);
    ellipse(mouseX + 25, 50, 35);
    ellipse(mouseX + 40, 60, 25);
    ellipse(mouseX + 55, 50, 40);
    ellipse(mouseX + 80, 50, 25);
    ellipse(mouseX + 95, 55, 15);

    //smaller cloud
    fill(255);
    ellipse(mouseX + 68, 10, 10);
    ellipse(mouseX + 80, 10, 20);
    ellipse(mouseX + 95, 10, 25);
    ellipse(mouseX + 110, 10, 30);
    ellipse(mouseX + 125, 8, 30);
    ellipse(mouseX + 140, 10, 15);
    ellipse(mouseX + 145, 10, 10);

    //smallest cloud
    fill(255);
    ellipse(mouseX + 268, 40, 10);
    ellipse(mouseX + 280, 40, 20);
    ellipse(mouseX + 295, 40, 25);
    ellipse(mouseX + 310, 40, 40);
    ellipse(mouseX + 325, 38, 30);
    ellipse(mouseX + 340, 40, 15);
    ellipse(mouseX + 345, 40, 10);
}

function mouseClicked() {
  //if mouse is inside sun, draw text
    var d = dist(mouseX, mouseY, 550, 50);
    if (d < 100) {
    showText(true);
    }
}

function showText(mouse) {
    if (mouse==true) {
      textSize(32);
      fill(0);
      text("Salmon Skies", 20, 50);
    }
}

//make trees
function makeTree() {
    noStroke();
    fill(22, 60, 28);
    beginShape();
    for (var i = 0; i < width; i++) {
        var x = (i * tree) + (millis() * treeSpeed);
        var y = map(noise(x), 0, 1, height/2, height/3);
        vertex(i, y);
    }
    vertex(width, height-height/2);
    vertex(0, height-height/2);
    endShape();
}

function displayHorizon() {
    noStroke();
    line(0,height-height/2, width, height-height/2);
    //pond
    fill(44, 80, 108);
    rect(0, height-height/2, width, height-height/2);
    //sun
    fill(240);
    noStroke();
    ellipse(550, 50, 50, 50);
}

function updateAndDisplayfish(){
    // update fish positions, display them
    for (var i = 0; i < fish.length; i++){
        fish[i].move();
        fish[i].display();
    }
}

function removefishThatAreOutOfView(){
    //if entirety of the fish are off canvas
    //then remove them
    var fishToKeep = [];
    for (var i = 0; i < fish.length; i++){
        if (fish[i].x + fish[i].breadth > 0) {
            fishToKeep.push(fish[i]);
    }
        }
  fish = fishToKeep;
}

function addNewfishWithSomeRandomProbability() {
    //small probability, add a new fish to the end
    var newfishLikelihood = 0.005;
    if (random(0,1) < newfishLikelihood) {
        fish.push(makefish(width));
    }
}

//update position of fish
function fishMove() {
    this.x += this.speed;
}

//draw the fish
function fishDisplay() {
    var fishHeight = random(10, 15);
    var fHeight = (20);
    fill(219, 97, 87, 150);
    noStroke();
    push();
    translate(this.x, height - 60);
    //fish body
    ellipse(0, -fHeight, this.breadth, fishHeight);
    fill(255);
    //fish eyes
    ellipse(-13, -fHeight, 4, 6);
    fill(0);
    ellipse(-13, -fHeight, 2, 3);
    //fish fins
    fill(219, 97, 87, 150);
    ellipse(0, -fHeight+7, 4, 8);
    fill(219, 97, 87, 150);
    ellipse(-2, -fHeight-7, 6, 8);
    fill(219, 97, 87, 150);
    arc(23, -fHeight, 30, 30, 0, HALF_PI);

    //fish reflection
    fill(219, 97, 87, 30);
    ellipse(0, fHeight, this.breadth, fishHeight);
    //reflection fish eyes
    fill(255, 30);
    ellipse(-13, fHeight, 4, 6);
    fill(0, 30);
    ellipse(-13, fHeight, 2, 3);
    //reflection fish fins
    fill(219, 97, 87, 30);
    ellipse(0, fHeight+7, 4, 8);
    fill(219, 97, 87, 30);
    ellipse(-2, fHeight-7, 6, 8);
    fill(219, 97, 87, 30);
    arc(23, fHeight, 30, 30, 0, HALF_PI)
    pop();
}

function makefish(birthLocationX) {
    var fis = {x: birthLocationX,
                breadth: 50,
                speed: -2.0,
                nlil: round(random(2,40)),
                move: fishMove,
                display: fishDisplay}
    return fis;
}

For this project, I wanted to go back to a concept that I struggled with. During the generative landscape project, I ran into quite a few problems and was not able to make an image that I found aesthetically pleasing. I wanted to return to this, and have more time to work through the concepts to be able to make something calming to watch. I am drawn to imagery that relaxes the viewer, and I imagine this is the type of image that could be watched to slow down a person’s heart rate, as the fish move at a calming pace. I love going to the lake back home with my Dad and watching the fish go by, which was always a therapeutic activity. This image in my head was what I wanted to emulate. Considering the amount of difficulty and trouble I have faced in this class, I’m glad I had extra time to really focus, take my time, and make something that feels fairly complete. I learned a lot more skills from being able to slow down and focus on these concepts.

Final Project

sketch

//Cora Hickoff
//Section D
//chickoff@andrew.cmu.edu
//Final-Project

//image of dog
var imgD;
//image of fish
var imgF;

//for iterating through dog and fish photos
var num = 0;

//images of dog
var loadedImagesD = [];
//images of fish
var loadedImagesF = [];

//for dragging mouse
var xarray = [];
var yarray = [];

//variables for moth
var x;
var y;
var dx;
var dy;

//variables for blue moth
var w;
var z;
var dw;
var dz; 

//variables for bee
var b;
var p;
var db;
var dp;

function preload() {

    var backgroundImageURL = "https://i.imgur.com/qDrD310.png";
    backgroundIMG = loadImage(backgroundImageURL);

    var sunImageURL = "https://i.imgur.com/6SDAJjt.png";
    sunIMG = loadImage(sunImageURL);

    var mothImageURL = "https://i.imgur.com/bGUODEv.png?1";
    mothIMG = loadImage(mothImageURL);

    var bluemothImageURL = "https://i.imgur.com/FR3gCsQ.png";
    bluemothIMG = loadImage(bluemothImageURL);

    var beeImageURL = "https://i.imgur.com/oTcCy45.png";
    beeIMG = loadImage(beeImageURL);

        //dog photos
        for (var i = 0; i < dogPhotos.length; i++) {
            loadedImagesD.push(loadImage(dogPhotos[i]));
        }

        //fish photos
        for (var i = 0; i < fishPhotos.length; i++) {
            loadedImagesF.push(loadImage(fishPhotos[i]));
        }
}

function setup() {

    createCanvas(700, 500); 

        var d = floor(random(0, dogPhotos.length));
        var f = floor(random(0, 0, fishPhotos.length));

        //images of dog
        imgD = loadedImagesD[d];
        //images of fish
        imgF = loadedImagesF[f];

        x = random(width);
        y = random(height);
        dx = random(-5, 5);
        dy = random(-5, 5);

        w = random(width);
        z = random(height);
        dw = random(-5, 5);
        dz = random(-5, 5);

        b = random(width);
        p = random(height);
        db = random(-5, 5);
        dp = random(-5, 5);
}

function draw() {

    background(200); 

    //photo of background
    image(backgroundIMG, 0, 0, 0);
    //photo of sun
    image(sunIMG, 0, 0, 0);
    //photo of dog
    image(imgD, 0, 0, imgD.width, imgD.height);
    //photo of fish
    image(imgF, 0, 0, imgF.width, imgF.height);

    //drags smaller dog image along canvas
    for (var i = 0; i < xarray.length; i++) {
        fill(0);
        image(imgD, xarray[i], yarray[i], imgD.width/2.4, imgD.height/2.4);
    }
    
    if (xarray.length > 8) {
        xarray.shift(1); 
        yarray.shift(1);
    }

    //photo of moth
    image(mothIMG, x, y, mothIMG.width/2.3, mothIMG.height/2.3);

        x += dx;
        y += dy;

        //if image exceeds canvas width,
        //set back to 0
        if (x > width) x = 0;
        else if (x < 0) x = width;
        //if image exceeds canvas height,
        //set back to 0
        if (y > height) y = 0;
        else if (y < 0) y = height;

    //photo of blue moth
    image(bluemothIMG, w, z, bluemothIMG.width/1.9, bluemothIMG.height/1.9);

        w += dw;
        z += dz;

        //if image exceeds canvas width,
        //set back to 0
        if (w > width) w = 0;
        else if (w < 0) w = width;
        //if image exceeds canvas height,
        //set back to 0
        if (z > height) z = 0;
        else if (z < 0) z = height;

    //photo of bee
    image(beeIMG, b, p, beeIMG.width/2.5, beeIMG.height/2.5);

        b += db;
        p += dp;

        //if image exceeds canvas width,
        //set back to 0
        if (b > width) b = 0;
        else if (b < 0) b = width;
        //if image exceeds canvas height,
        //set back to 0
        if (p > height) p = 0;
        else if (p < 0) p = height;

    //lightens screen when mouse over sun
    if (mouseX <= 398 & mouseX >= 320 && mouseY >= 30 && mouseY <= 105) {

        rectMode(CORNER);
        noStroke();
        fill(246, 215, 161, 70);
        rect(0, 0, 700, 500);
    }
}

function mouseDragged() {

    xarray.push(mouseX);
    yarray.push(mouseY);
}

function mousePressed() {

    //iterates through dog photos when mouse clicks its body
    if (mouseX <= 500 & mouseX >= 150 && mouseY >= 275 && mouseY <= 400) {
        var d = num % dogPhotos.length;
        imgD = loadedImagesD[d];
        num++;
    }

    //iterates through fish photos when mouse clicks blanket
    if (mouseX <= 700 & mouseX >= 0 && mouseY >= 130 && mouseY <= 250) {
        var f = num % fishPhotos.length;
        imgF = loadedImagesF[f];
        num++;
    }
}

var dogPhotos = [

    //rusty-colored dog
    "https://i.imgur.com/2vglMnn.png",
    //white/pink dog
    "https://i.imgur.com/IWbYamo.png",
    //lilac dog
    "https://i.imgur.com/I5xU7LF.png",
    //black dog
    "https://i.imgur.com/AAQQgCj.png",
    //blue dog
    "https://i.imgur.com/CTZSr9D.png",
    //mulberry dog
    "https://i.imgur.com/kt5rN0R.png"]

var fishPhotos = [
    
    //first fish
    "https://i.imgur.com/bD3GBjE.png",
    //second fish
    "https://i.imgur.com/eUWa3dx.png",
    //third fish
    "https://i.imgur.com/XJFlBTd.png"]
    

https://vimeo.com/246466644

Final Project Still

When I started this project, I knew that I wanted to give users a hands-on experience, similar to the  I Spy computer games that I’d play as a kid, which I drew inspiration from. The worlds created in these games still felt genuine despite the fact that they were within a computer screen. This is in part because photos from real life were used.

Moreover, I really loved the Mixies Assignment and that it felt like we were playing with puzzle pieces and working with our hands, even though we were doing this electronically. This is why I chose to use photos that I’ve taken and use similar methods from that assignment to make this project interactive and fun.

To download this project, click this zip file: chickoff-final-project

daphnel- Final Project- Section D

Final

//Daphne Lee
//15-104::1 (9:30am) Section D
//daphnel@andrew.cmu.edu
//Final-Project

//x is x location of ball;
//y is y location of ball;
var x = 200;
var y = 200;
var dirx = 1;
var diry = 5;
var speed = 0.75;
var paddleWidth = 75;
var paddleHeight = 10;
var ballDiam = 20;
var score = 0;
var lives = 3;
var scaled = 0.6;

var blockRowCount = 4;
var blockColCount = 13;
var blockWidth = 50;
var blockHeight = 20;
var blockPadding = 10;
var blockOffsetX = 30;
var blockOffsetY = 10;

var blockXArr = [];
var blockYArr = [];
var statusArr = [];
var started = false;

//preloaded items
var bg;
var girlLeft;
var girlRight;
var girlYPosition = 150;
var girlsSize = 100;
var press;
var enter;

function preload() {
    bg = loadImage("https://i.imgur.com/kElOb6m.jpg");
        //items on the start page before game starts
        if(started==false){
            press = loadImage("https://i.imgur.com/kbAP99M.png");
            enter = loadImage("https://i.imgur.com/4ijF2zc.png")
            // the girl gifs on the start screen
            girlLeft = createImg("https://i.imgur.com/IjEVc03.gif");
            girlLeft.position(150 * scaled, girlYPosition * scaled);
            girlLeft.size(girlsSize, girlsSize)
            girlRight = createImg("https://i.imgur.com/IjEVc03.gif");
            girlRight.position(500 * scaled, girlYPosition * scaled);
            girlRight.size(girlsSize, girlsSize);
        }
}

function setup() {
    createCanvas(800, 400);
    for(var i = 0; i < blockRowCount * blockColCount; i++) {
        //status makes sure if blocks disappear or remain
        statusArr.push(1);
    }
}

function collision(){
    for(var c = 0; c < blockColCount; c++){
        for(var r = 0; r < blockRowCount; r++){
            if(statusArr[r * blockColCount + c] == 1) {
                var radius = ballDiam / 2;

                //creating the blocks;
                var blockX = c * (blockWidth + blockPadding) + radius
                var blockY = r * (blockHeight + blockPadding) + radius;

                //collision points for each block;
                var minXCollide = blockX - radius;
                var maxXCollide = blockX + radius + blockWidth;
                var minYCollide = blockY - radius;
                var maxYCollide = blockY + radius + blockHeight;

                var collidesX = (x > minXCollide & x < maxXCollide);
                var collidesY = (y > minYCollide & y < maxYCollide)

                //what happens as a result of the collision;
                if(collidesX & collidesY) {
                    diry = -diry;
                    score++;
                    statusArr[r * blockColCount + c] = 0;
                }
            }
        }
    }
}

function countScore(){
    stroke(255);
    strokeWeight(1);
    textSize(15);
    //gives you a score when you lose
    text("Score: " + score, width - 70, height - 10);
}

function drawLives(){
    stroke(255);
    strokeWeight(1);
    textSize(15);
    //shows you the number of lives you have
    text("Lives: " + lives, 10, height - 10);
}

//creating the end result of what happens if you destroy all the blocks
function congrats(){
    if(score == blockColCount * blockRowCount){
        fill("lavender");
        textStyle(BOLD);
        textFont("cursive", 50);
        text("Congratulations You Won!", 120, 200);
    }
}

function draw() {
    scale(scaled);
    background(bg);
    noStroke();
    var textWidth = 150;
    var textHeight = 50;

    //creates the beginning press enter text before the game starts
    if(started == false){
        image(press, 340, 150, textWidth, textHeight);
        image(enter, 340, 210, textWidth, textHeight);
    }

    if(started){
        //ping pong ball
        rectMode(CENTER);
        fill(255);
        ellipse(x, y, ballDiam, ballDiam);

        //the paddle on the bottom
        fill(100, 300);
        rect(mouseX * scaled * 3, height - paddleHeight, paddleWidth,
        paddleHeight);

        //moves the ball
        x += dirx * speed;
        y += diry * speed;

        //left and right wall boundaries
        if(x > width - ballDiam / 2 || x < ballDiam / 2) {
            dirx = -dirx;
        }
        //top wall boundary
        if(y < ballDiam / 2){
            diry = -diry;
        }

        // else if(y > height + ballDiam / 2){
        if(lives > 0 & y > height + ballDiam / 2){
            x = width / 2;
            y = height / 2;
            lives --;
        } else if(lives == 0){
            speed = 0;
            stroke(0);
            fill("lavender");
            textStyle(BOLD);
            textFont("cursive", 50);
            //creating text to show if you fail to complete game
            text("GAME OVER", 250, 200);
            textSize(20);
            text("Score: " + score, 350, 250);
        }

        //Collision detection for the paddle
        var radius = ballDiam / 2;
        var minXCollide = (mouseX * scaled * 3) - radius - paddleWidth / 2;
        var maxXCollide = (mouseX * scaled * 3) + radius + paddleWidth / 2;
        var minYCollide = (height - paddleHeight) - radius - paddleHeight / 2;
        var maxYCollide = (height - paddleHeight) + radius + paddleHeight / 2

        var collidesX = (x >= minXCollide & x <= maxXCollide);
        var collidesY = (y >= minYCollide & y <= maxYCollide)

        if(collidesX && collidesY) {
            if(diry > 0) {
                diry = -diry;
                dirx = random(-5, 5);
                dirx = -dirx;
            }
        }

        countScore();
        drawLives();
        rectMode(CORNER);

        //creating the blocks
        for(var c = 0; c < blockColCount; c++){
            for(var r = 0; r < blockRowCount; r++) {
                if(statusArr[r * blockColCount + c] == 1) {
                    var blockX = c * (blockWidth + blockPadding) + ballDiam / 2;
                    var blockY = r * (blockHeight + blockPadding) + ballDiam / 2;
                    fill(100, 100);
                    stroke(255);
                    strokeWeight(1);
                    rect(blockX, blockY, blockWidth, blockHeight);
                }
            }
        }
        collision();
        congrats();
    }
}

function keyPressed() {
    // start the game
    if(keyCode == 13){
        //hides the girls running in the beginning frame before the game starts
        girlLeft.hide();
        girlRight.hide();
        started = true;
    }
}

I had a lot of ideas for my proposal but I found implementing the ideas I had difficult. For my project, I focused on keeping it at one level. You start out with three lives and whenever you die, the ball will reset at the middle of the canvas and move downwards from there. Once the 3 chances are used, it’s game over. I made it so that even if you are late in hitting the ball and you hit the left or right edge of the paddle, the ball could still be saved and move back up. As long as any contact with the paddle is made, the ball will be in play. When two side by side blocks are simultaneously hit by a ball due to the ball hitting the blocks directly in between the blocks, both blocks will disappear and the ball will continue moving upwards or diagonally until it has reached a solid border like the wall or the edge of another block.

Some Key Points

  1. Note that the ball moves randomly at all times so you need to make sure to keep your focus on the direction of the ball at all times.
  2.  Refresh the page to play the game again.
  3. The ball is reset in the middle of the screen when you die and will immediately start moving again towards a random downwards direction.

For Graders:: TIP! Playing this game takes a while so if you would like, you can increase the PaddleWidth from 75 to 800 or 900 and center it on the screen and let it just run itself until the blocks are all gone.

This game does not work as well on WordPress due to some parts of the code that could not be scaled well. Please click below for the Zip File to the full size of the game.

Click Here for the Game

rmanagad-finalproject-sectione

sketch

//Robert Managad
//Section-E
//rmanagad@andrew.cmu.edu
//Final Project -- Trigonometric Loading Icons

//This code loops on the basis of the trigonometric functions cosine, sine, and tangent.
//It can be modified to produce three distinct animations, depending on the trig function used,
// and on the axis be affected.


var spins = [];
var drips = [];
var warps = [];
var pluss = [];

function setup() {
	createCanvas(420, 420);

	//Spin Loop, center
	push();
	for (var i = 0; i < 12; i++){
		var spinposition = createVector(i * 20, i);
		var spinradius = i * 1; // Radius increases by 200% for every iteration of i, up to 12.
		spins[i] = new Spin(spinposition, spinradius); //holds circle function in the array
	}  
	pop();

	//Drip loop, top center
	for (var i = 0; i < 8; i++){
		var dripposition = createVector(i*18, i);
		var dripradius = i * 2;
		drips[i] = new Drip(dripposition, dripradius);
	}

	//Warp loop, left center
	for (var i = 0; i < 12; i++){
		var warpposition = createVector(i*20, i);
		var warpradius = i * 1.5;
		warps[i] = new Warp(warpposition, warpradius);
	}

	//Plus loop, left center
	for (var i = 0; i < 12; i++){
		var plusposition = createVector(i*20, i);
		var plusradius = i * 1.5;
		pluss[i] = new Plus(plusposition, plusradius);
	}
}

function draw() {
	background(0);

	//drip draw
	for(var i = drips.length - 1; i >= 0; i--){
		var drip = drips[i];
		drip.update();
		drip.display();
	}  

	//rectangle mask
	fill(0);
	noStroke();
	rect(140, 130, 140, 300);

	//warp draw
	for(var i = warps.length - 1; i >= 0; i--){
		var warp = warps[i];
		warp.update();
		warp.display();
	}  

	//spin draw
	for(var i = spins.length - 1; i >= 0; i--){
		var spin = spins[i];
		spin.update();
		spin.display();
	}  

	//plus draw
	for(var i = pluss.length - 1; i >= 0; i--){
		var plus = pluss[i];
		plus.update();
		plus.display();
	}  
}

//--------------------

//elements of each circle are compounded into the Spin function.
function Spin(spinposition, spinradius){
  this.position = spinposition;
  this.radius = spinradius;
  this.direction = 1;
  this.time = 0; //controls the placement of the starting anchor, time to jump towards.
  //updates position every time draw is called.
  this.update = function(){
	this.time += this.direction * this.radius * 0.007; //holds value of velocity. Higher radius value = higher speed.
	this.position.y = (200 + 90 * cos(this.time))/2;
	this.position.x = (200 + 90 * sin(this.time))/2;
	//trig function directs the animation (slowing down in middle/accelerating the circles as they approach the edges)
  }

  //holds visual presentation
  this.display = function(){
  	noFill();
    stroke(255);
    strokeWeight(1.5);
    ellipse(this.position.x - 20, this.position.y - 20, this.radius, this.radius);  
  }
}

//---------------------

function Drip(dripposition, dripradius){
  this.position = dripposition;
  this.radius = dripradius;
  this.direction = 1;
  this.time = 0; //controls the placement of the starting anchor, time to jump towards.
  //updates position every time draw is called.
  this.update = function(){
	this.time += this.direction * this.radius * 0.004; //holds value of velocity. Higher radius value = higher speed.
	this.position.y = (200 + 90 * tan(this.time))/3;
	//trig function directs the animation (slowing down in middle/accelerating the circles as they approach the edges)
  }

  //holds visual presentation
  this.display = function(){
  	noFill();
    stroke(255);
    strokeWeight(1.5);
    ellipse(this.position.x + 275, this.position.y - 10, this.radius, this.radius);  
  }
}

//------------------------

function Warp(warpposition, warpradius){
  this.position = warpposition;
  this.radius = warpradius;
  this.direction = 1;
  this.time = 0; //controls the placement of the starting anchor, time to jump towards.
  //updates position every time draw is called.
  this.update = function(){
	this.time += this.direction * this.radius * 0.002; //holds value of velocity. Higher radius value = higher speed.
	this.position.y = 200 + 30 * cos(this.time);
	this.position.x = 200 + 30 * tan(this.time);
	//trig function directs the animation (slowing down in middle/accelerating the circles as they approach the edges)
  }

  //holds visual presentation
  this.display = function(){
  	noFill();
    stroke(255);
    strokeWeight(1.5);
    ellipse(this.position.x , this.position.y + 160, this.radius, this.radius);  
  }
}

//----------------------------

function Plus(plusposition, plusradius){
  this.position = plusposition;
  this.radius = plusradius;
  this.direction = 1;
  this.time = 0; //controls the placement of the starting anchor, time to jump towards.
  //updates position every time draw is called.
  this.update = function(){
	this.time += this.direction * this.radius * 0.002; //holds value of velocity. Higher radius value = higher speed.
	this.position.y = 200 + 40 * 1/cos(this.time)/3;
	this.position.x = 200 + 40 * 1/sin(this.time)/3;
	//trig function directs the animation (slowing down in middle/accelerating the circles as they approach the edges)
  }

  //holds visual presentation
  this.display = function(){
  	noFill();
    stroke(255);
    strokeWeight(1.5);
    ellipse(this.position.x , this.position.y, this.radius, this.radius);  
  }
}

If gifs below are frozen: right-click and open the image in a new tab.

Helix loading icon
Warp loading icon
Drip loading icon
Plus loading icon
Spin loading icon
Chain Loading Icon

 

My final project culminated in a series of six loading icons. I used trigonometric functions to calculate x and y positions — because of their cyclical behaviours, sin, cosine, and tangent worked well for looping animations. Individual animation gifs can be seen above. Process work can be seen below

From a design standpoint, I developed these loading icons with several human factors in mind — from longevity and human attention span, to interactivity and engagement. Repetition in form, symmetry in composition and shape, and cyclical rhythm all contribute to keeping people from becoming disengaged with the content. As a component living in a transitionary space, the loading icon serves to bridge one space to the next.

 

Loading icon sketches on Illustrator
initial sketches

jennyzha – final project

Final_Project

sketch

// Jenny Zhang
// jennyzha
// Section D 
// Final Project

var img;
//setting a global variable for picture the user can paint
var col = [0, 0, 0, 0];
//setting a an initial color for the paintbrush color
var aSlider;
//setting a global variable for the slider that will control the width of the brush
var SizeOfBrush = 10;
//setting initial SizeOfBrush for the width of the brush

function preload() {
    var imgs = [];
    //preloading all of the possible images the user can paint into an array

    imgs[0] = "http://i.imgur.com/UyyTNyF.png";
    imgs[1] = "http://i.imgur.com/sW4fWRP.png";
    imgs[2] = "http://i.imgur.com/6HBfsCu.png";    
    imgs[3] = "http://i.imgur.com/60ITEbG.png";
    imgs[4] = "http://i.imgur.com/Zli7OsC.png";
    imgs[5] = "http://i.imgur.com/ztRhRts.png";
    imgs[6] = "http://i.imgur.com/Qx8zJO4.png";    
    imgs[7] = "http://i.imgur.com/mzxbto4.png";
    imgs[8] = "http://i.imgur.com/vXdn15r.png";
    imgs[9] = "http://i.imgur.com/shwrAy5.png";
    imgs[10] = "http://i.imgur.com/manEqm3.png";
    imgs[11] = "http://i.imgur.com/oh8iKpK.png";
    imgs[12] = "http://i.imgur.com/oh8iKpK.png";
    imgs[13] = "http://i.imgur.com/306OVy6.png";
    imgs[14] = "http://i.imgur.com/xJVNo7U.png";
    imgs[15] = "http://i.imgur.com/4mv8uhP.png";
    imgs[16] = "http://i.imgur.com/MvNo2DV.png";
    imgs[17] = "http://i.imgur.com/dxlMoKR.png";
    imgs[18] = "http://i.imgur.com/mxti4c2.png";
    imgs[19] = "http://i.imgur.com/hDtDFhD.png";
    imgs[20] = "http://i.imgur.com/shwrAy5.png";
    
    var num = floor(random(0, imgs.length));
    //randomly choosing a picture from the array to paint
    img = loadImage(imgs[num]);
    //assigning the image to the variable img
}

function setup() {
    createCanvas(800, 650);
    background(145, 200, 200);

    aSlider = createSlider(10, 50, SizeOfBrush);
    aSlider.position(670, 620);
    //creating the slider, placing it at the bottom of the canvas

    fill(250);
    noStroke();
    rect(0, 300, 800, 650); 
    //creating the canvas that the user will draw on 

    fill(145, 200, 200);
    noStroke();
    rect(0, 590, 800, 650); 
    //creating the canvas that the user will draw on 

    fill(0);
    textSize(20);
    text("Paint Away!", 0, 20);
    //writing the title of the page

    fill(0);
    textSize(15);
    text("Click on the painting to choose your color, then paint by clicking on the bottom canvas provided.", 0, 40); 
    //writing the instructions to choosing your color    

    fill(0);
    textSize(15);
    text("Move the slider in the bottom right corner to change the size of your paint brush.", 0, 55); 
    //writing the instructions to choosing your paintbrush size
}

function mousePressed(){
    SizeOfBrush = aSlider.value();
    //setting the width of the brush SizeOfBrush to the slider value
    //this is adapted from lab 15

    if (mouseX > 0 & mouseX < 800 && mouseY > 0 && mouseY < 350) {
        col = get(mouseX, mouseY);
    }
    //creating a restriction that the user can only get their color from the provided image

    else if  (mouseX > 0 & mouseX < 800 && mouseY > 350 && mouseY < 600 - SizeOfBrush/2){
        fill(color(col));
        ellipse(mouseX, mouseY, SizeOfBrush);
    }
    //telling the program that anywhere else besides the picture, they can draw a single ellipse

    fill(145, 200, 200);
    rect(70, 615, 250, 15);
    //effectively "erases"/"refreshes" the text every time the draw function is called

    fill(0);
    textSize(12);
    text("Your brush color is (" + col + ")", 70, 625); 
    //creates a text box telling you your current paintbrush color
    //this is adapted from assignment 11A, text rain
}

function mouseDragged() {
    SizeOfBrush = aSlider.value();
    //setting the brush size to the slider value

    if (mouseX > 0 & mouseY > 0 && 
        mouseX < 800 && mouseY > 300 && 
        mouseY < 600 - SizeOfBrush/2) {
            noStroke();
            fill(color(col));
            //setting the restriction so that when the mouse is dragged the user can draw in the provided canvas area

            ellipse(mouseX, mouseY, SizeOfBrush);
    }
}

function draw() {
    image(img, 0, 60); 
    //place current picture the user will draw

    fill(color(col));
    rect(10,595,50,50);
    //creates the square in the botton that shows the current color the user is using
}

My final project is essentially a virtual coloring book. The steps are very simple and easy. All you have to do is refresh the page if you don’t want to paint the painting provided until you are satisfied with the one loaded. Next, choose your first color to paint with by clicking on the color in the painting. Afterwards, choose the thickness of your brush by sliding the slider in the bottom right corner. Finally, click or drag across the bottom half of the screen, or your “canvas” to draw!

*unfortunately, wordpress is unable to handle the canvas size of the program, that being said, please refer to the attached zip file for the full effect – thank you!*

ashleyc1-Section C-Final Project

RULES:

-Use the mouse (either by pressing or moving around) to interact with animations

-Click any key to cycle through different animations (click within the canvas first if not working because wordpress is weird)

Note: If file is opened through google browser, mouse isn’t limited on first animation and they all generally run smoother

sketch

//Ashley Chan
//Section C
//ashleyc1@andrew.cmu.edu
//Final Project - Type in Motion

/////////////////////////////////////
// MAIN  CONTROL //
///////////////////////////////////

var index = 0;

//global var for images
var revealImg;
var twinkleImg;
var eraseImg;
var sprayHintImage;
var sprayOverlayImage;

function preload() {

//load all the images
revealImg = loadImage("https://i.Imgur.com/giFSIcI.jpg?1");
twinkleImg = loadImage("https://i.imgur.com/hqn6Cv9.jpg?1");
eraseImg = loadImage("https://i.imgur.com/VO4buSz.png");
sprayHintImage = loadImage("https://i.imgur.com/P6LkUET.jpg");
sprayOverlayImage = loadImage("https://i.imgur.com/dr6JWoH.png");

}

function setup() {

    createCanvas(480, 180);
    background(0);

    //index that cycles through animations
    //call each animations' setups 
    //depending on index number
    if(index == 0){
        revealSetup();
    }

    if(index == 1){
        twinkleSetup();
    }

    if(index == 2){
        eraseSetup();
    }

    if(index == 3){
        spraySetup();
    }
}


function draw() {

    //call each animations' draw depending on index number
    if(index == 0) {
        revealDraw();
    }

    if(index == 1){
        twinkleDraw();
    }

    if(index == 2){
        eraseDraw();
    }

    if(index == 3){
        sprayDraw();
    }

}

// Cycles through animations every time 
//any key is pressed
function keyPressed(){

    index = (index + 1)%4;

    setup();

}

////////////////////////////////////////////
// REVEAL  ANIMATION //
///////////////////////////////////////////

//var that will spell out reveal
var revealCircles; 
var revealDots;


function revealSetup(){
    
    //Draw a black rectangle for every animation 
    //because we need to make sure the animations 
    //restart without actually calling setup 
    //and messing everything up
    fill(0);
    rect(0, 0, 480, 180);

    revealCircles = [];
    revealDots = [];

    // iterate through pixels
    image(revealImg, 0, 100);
    revealImg.loadPixels();
    
    for(var i = 0; i < 2 * revealImg.width; i++) {
        for(var j = 0; j < revealImg.height; j++) {

            var revealIndex = i * 2 + (j * revealImg.width)*8;
            
            //locate the dark pixels of underlaying image
            //where there is dark, make a revealCircle
            if (revealImg.pixels[revealIndex] < 200){

                revealDots.push(createVector( i / 2, j * 2));
            }
        }
    }

        var num_circles = 0;
    
    //draw certain number of revealCircles every frame
    while (num_circles < 400){
        
        var c = revealCreateCircle();
    
        if (c) {

            revealCircles.push(c);
            num_circles++;

        }
    }
}


function revealDraw(){

    background(0);
  
    //draw the revealCircles
    for(var i = 0; i < revealCircles.length; i++) {
      
        var x = revealCircles[i].x;
        var y = revealCircles[i].y;
        var r = revealCircles[i].r;

    //calculate distance around mouse
    //any space within radius will be drawn and shown
    var d = dist(x, y, mouseX, mouseY);

    if (d  < 90) {
        //center image
        push();
        scale(.5, .5);
        translate(150, 0);
        revealCircles[i].show();
        pop();

        }
    }
}

//While creating a new revealCircle
//Do not create one inside existing one
function revealCreateCircle(){

    var x;
    var y;
    
    // find a random position to create a revealCircle
    var position = int(random(0, revealDots.length));

    x = revealDots[position].x;
    y = revealDots[position].y;

    var valid = true;

    for (var i = 0; i < revealCircles.length; i++) {

        //Don't draw inside another revealCircle
        // If generated already drawn revealCircle, redo
        if (dist(x, y, revealCircles[i].x, revealCircles[i].y) <= revealCircles[i].r) {

            valid = false;
            break;
        }
    }

    //if no revealCircle drawn at spot, draw one 
    if (valid) {

        return new revealCircle(x, y, width, height);
    }

    return false;
}

//define the circles
function revealCircle(xPos, yPos, gwidth, gheight){

    this.x = xPos;
    this.y = yPos;
    this.r = 8;

    this.width = gwidth;
    this.height = gheight;

    //controls the look of the revealCircles 
    //randomizes color when mouse is pressed
    this.show = function(){

        fill(255);
        noStroke();
        strokeWeight(1);
        ellipse(this.x, this.y, this.r);

        }
}


////////////////////////////////////////////////
//  TWINKLE   ANIMATION //
///////////////////////////////////////////////

//var that will spell out twinkle
var twinkleCircles;
var twinkleDots;


function twinkleSetup(){
    
    fill(0);
    rect(0, 0, 480, 180);

    twinkleCircles = [];
    twinkleDots = [];
    
    // iterate through pixels
    //image(twinkleImg, 0, 100);
    twinkleImg.loadPixels();
    
    //locate the dark pixels of underlaying image
    //where there is dark, make a twinkleCircle
    for(var i = 0; i < 2 * twinkleImg.width; i++) {
        for(var j = 0; j < twinkleImg.height; j++) {

            var twinkleIndex = i * 2 + (j * twinkleImg.width)*8;
            
            if (twinkleImg.pixels[twinkleIndex] < 200){

                twinkleDots.push(createVector( i / 2, j * 2));
            }
        }
    }

    var num_circles = 0;
    
    //draw certain number of twinkleCircles every frame
    while (num_circles < 300){
        
        var c = twinkleCreateCircle();

        if (c) {

            twinkleCircles.push(c);
            num_circles++;

        }

    }
    
    //draw the twinkleCircles
    for(var i = 0; i < twinkleCircles.length; i++) {
      
        var x = twinkleCircles[i].x;
        var y = twinkleCircles[i].y;
        var r = twinkleCircles[i].r;

        //center image
        push();
        scale(.6, .6);
        translate(50, 80);
        twinkleCircles[i].show();
        pop();
        
    }
}


function twinkleDraw(){

    //if mouse pressed, "redraw" circles
    //so that color is randomized
    if(mouseIsPressed) {
        for(var i = 0; i < twinkleCircles.length; i++) {
      
            var x = twinkleCircles[i].x;
            var y = twinkleCircles[i].y;
            var r = twinkleCircles[i].r;

            //center image
            push();
            scale(.6, .6);
            translate(50, 80);
            twinkleCircles[i].show();
            pop();

        }
    }
}

//While creating a new twinkleCircle
//Do not create one inside existing one
function twinkleCreateCircle(){

    var x;
    var y;
    
    // find a random position to create a twinkleCircle
    var position = int(random(0, twinkleDots.length));

    x = twinkleDots[position].x;
    y = twinkleDots[position].y;

    var valid = true;

    for (var i = 0; i < twinkleCircles.length; i++) {

        //Don't draw inside another twinkleCircle
        // If generate already drawn twinkleCircle, redo
        if (dist(x, y, twinkleCircles[i].x, twinkleCircles[i].y) <= twinkleCircles[i].r) {

            valid = false;
            break;
        }
    }

    //if no twinkleCircle drawn at spot, draw one 
    if (valid) {

        return new twinkleCircle(x, y, width, height);
    }

    return false;
}

//define twinkleCircles
function twinkleCircle(xPos, yPos, gwidth, gheight){

    this.x = xPos;
    this.y = yPos;
    this.r = 8;

    this.width = gwidth;
    this.height = gheight;

    //controls the look of the twinkleCircles 
    //randomizes color when mouse is pressed
    this.show = function(){

        stroke(255);
        strokeWeight(1);

        //start out as empty twinkleCircles
        var value = 0;
        fill(value);

        //change the color every time mouse is pressed
        if (mouseIsPressed) {

        value = random(0, 255);

        fill(value, value, value);

        }

        ellipse(this.x, this.y, this.r);

        }
}


////////////////////////////////////////////
// ERASE  ANIMTATION //
///////////////////////////////////////////

//var for erase animation
var eraseLastMinute;
var eraseCurrentMinute;

//eraseIndex for animations
var eraseIndex = 0;


function eraseSetup() {
    //scale(.5, .5);
    
    fill(0);
    rect(0, 0, 480, 180);
    eraseImg.loadPixels();

    eraseLastMinute = minute();
    
    //make for loop to control how many images we are making
    for (i = 0; i < 50; i++) {

        image(eraseImg, random(-50, 800), 
            random(-50, height), 100, 43);

    }
}

function eraseDraw() {

    fill(0);
    noStroke();
    ellipse(mouseX, mouseY, 30, 30);

    //call setup and reset every minute
    eraseCurrentMinute = minute();

    //if a minute has passed, call setup and generate 
    //different arrangement of words
    if (eraseLastMinute != eraseCurrentMinute) {

    eraseSetup();

    }
}

///////////////////////////////////////////
//  SPRAY  ANIMATION //
//////////////////////////////////////////

var sprayDots; 

function spraySetup() {

    fill(0);
    rect(0, 0, 480, 180);
    noCursor();   
    noStroke();   
  
    sprayDots = [];

}

function sprayDraw() {

    image(sprayHintImage, 50, 100);
    sprayHintImage.resize(300, 100);

    background(0);
  
    var position = createVector(mouseX, mouseY);

    //if mouse is pressed, draw a bunch of sprayCircles
    //that move toward the overlaying image
    if (mouseIsPressed) {
    var target = sprayFindPixel();    
    var spraydot = new sprayDot(position, target);

    sprayDots.push(spraydot);

    //allow user to draw a bunch of dots
    if (sprayDots.length > 2000) sprayDots.shift();    

    }  
          //draws the sprayDots
          for (var i = 0; i < sprayDots.length; i++) {

            //make sure sprayDots align with hint image
            push();
            translate(100, 50);
            sprayDots[i].update();
            sprayDots[i].draw();
            pop();

        }   

            //call image of Spray outline
            image(sprayOverlayImage, 100, 50);
            sprayOverlayImage.resize(300, 100);
}

//calculate where the underlying image is dark
//and then draw a spraydot overtop
function sprayFindPixel() {

var x;
var y;

    for (var i = 0; i < 200; i++) {

        x = floor(random(sprayHintImage.width));
        y = floor(random(sprayHintImage.height));
    
        if (red(sprayHintImage.get(x, y)) < 255) break;

  }

  return createVector(x, y);

}

//create sprayDots and define characteristics
function sprayDot(position, target) {

    this.position = position;
    this.target = target;
    this.diameter = random(10, 20);

}

sprayDot.prototype.update = function() {   
  
    this.position = 
    p5.Vector.lerp(this.position, this.target, 0.1);

};

sprayDot.prototype.draw = function() { 
  
    //color of sprayDots
    fill(255);
    ellipse(this.position.x, this.position.y, this.diameter, this.diameter);

};


For this project, I was interested in creating interactive animations that allowed type to be dynamic. I wanted the user to interact with the text based on the actions the words described (erase = erase the words). This project was super challenging and I wish I had more time to generate more interactive animations but I’m overall satisfied with it because I like exploring how to make type more dynamic that I can now use within my own art and design practice.

creyes1-FinalProject

creyes1 Final Project – Beat Machine

//Christopher Reyes
//creyes1@andrew.cmu.edu
//Section D
//Capstone Project

//This program is a reinterpretation of Patatap (www.Patatap.com) created by
//Jono Brandel (jonobr1.com) and Lullatone (www.lullatone.com).

//Animations and drumkit sounds play if a key is pressed on the keyboard
//To differentiate from Patatap, there is a beat loop functionality that
//records the users\'s actions, then plays them in sequence for beatmaking

//DIRECTIONS

//SPEAKERS ON
//Press SPACEBAR to begin loop recording, play drum sounds with KEYS 1-5,
//Press SPACEBAR AGAIN to stop recording and play recorded beat
//Press SPACEBAR TWICE to clear loop

//Note: Records/plays one loop at a time

var introTextX = 30; //Initalizes intro text position, sets to undefined to hide

//Color scheme - RGB values
var softYellow = [248, 249, 197];  //Background
var softPink = [252, 202, 199];    //For bigSlideH
var softViolet = [187, 170, 192];  //For bigSlideV
var lightCyan = [187, 252, 223];   //For rectSlide
var darkerTeal = [85, 200, 190];   //For ringPulse
var transWhite = [255, 255, 255, 100]; //Transparent white for bg rings

//Looping variables and arrays
var actions = []; //Lists actions performed
var actionFrames = []; //Frames on which actions were performed
var actionSounds = []; //Lists sounds to be played
var counter = 0; //Counts frames
var counterMax; //Cap on frame count, signals when to loop
var actionIndex; //Relationship between actions, actionFrames, actionSounds

//Drumkit sound samples: "Urban Drum Samples" by user Biochron of soundpacks.com
//Source Link: https://soundpacks.com/free-sound-packs/urban-drum-samples/
var snare;
var bass;
var hihat;
var hightom;
var lowtom;

function preload() {
    snare = loadSound("https://courses.ideate.cmu.edu/15-104/f2017/wp-content/"
            + "uploads/2017/12/creyes1-snare.wav");
    snare.setVolume(.3);
    bass = loadSound("https://courses.ideate.cmu.edu/15-104/f2017/wp-content/"
           + "uploads/2017/12/creyes1-bass.wav");
    bass.setVolume(1);
    hihat = loadSound("https://courses.ideate.cmu.edu/15-104/f2017/wp-content/"
            + "uploads/2017/12/creyes1-hihat.wav");
    hihat.setVolume(.6);
    hightom = loadSound("https://courses.ideate.cmu.edu/15-104/f2017/"
              + "wp-content/uploads/2017/12/creyes1-hightom.wav");
    hightom.setVolume(.5);
    lowtom = loadSound("https://courses.ideate.cmu.edu/15-104/f2017/wp-content/"
             + "uploads/2017/12/creyes1-lowtom.wav");
    lowtom.setVolume(.5);
}

function setup() {
    createCanvas(480, 480);
    background(softYellow);
}

function draw() {
    background(softYellow);

    //Background detail, layered circles

    noStroke();
    fill(transWhite);
    ellipse(width/2, height/2, 400, 400);

    fill(softYellow);
    ellipse(width/2, height/2, 350, 350);

    stroke(transWhite);
    noFill();
    strokeWeight(10);
    ellipse(width/2, height/2, 320, 320);

    strokeWeight(5);
    ellipse(width/2, height/2, 500, 500);

    noStroke();
    fill(transWhite);
    ellipse(width/2, height/2, 200, 200);

    //Introductory Text & Instructions
    //Disappears once user begins playing

    noStroke();
    fill(255);
    rect(introTextX-10, 420, 440, 50, 10);

    fill(100);
    textStyle(BOLD);
    textFont("Courier New", 12);
    text('Press any key 1-5, press SPACEBAR to start/stop recording your loop. '
         + 'Double-tap SPACEBAR to stop loop. (Speakers up)',
         introTextX, 430, 450, 50);

    //Starts initial counter to track actions
    if (recording == true) {
        counter++;
    }

    //If in playback, and items are in array, play on a loop
    if (recording == false & actionFrames.length > 0) {

        counter++;

        //Loops counter after hitting max
        if (counter === counterMax) {
            counter = 0;
        }

        //Plays action if counter is the same as the frame which action occurred
        var success = false;
        for (i = 0; i < actionFrames.length; i++) {
            if (counter === actionFrames[i]) {
                success = true;
                actionIndex = i;
                break;
            }
        }

        //Plays the action if counter number matches the stored frame count
        if (success == true) {
            (actions[actionIndex])();
            (actionSounds[actionIndex]).play();
        }

    }

    //Functions check if anything is in their respective arrays, then draw
    playSlideH();
    playSlideV();
    playRectSlide();
    playRingPulse();
    playDotSpray();

    //Visual feedback

    //Red dot when recording
    if (recording == true) {
        noStroke();
        fill(204, 67, 67);
        ellipse(30, 30, 10, 10);
    }

    //Playback icon if looping with actions in array
    if (recording == false & actions.length > 0) {
        noStroke();
        fill(110, 142, 105);
        triangle(25, 25, 25, 35, 35, 30);
    }

}

var recording = false;

function keyPressed() {

    //Hides intro text once user begins playing
    if (keyCode == 32 || keyCode == 49 || keyCode == 50 ||
        keyCode == 51 || keyCode == 52 || keyCode == 53) {
        introTextX = undefined;
    }

    //Toggles a loop recorder
    if (keyCode == 32 & recording == false) {
        counter = 0;
        actions = [];
        actionFrames = [];
        actionSounds = [];
        recording = true;
        print("start");
    } else if (keyCode == 32 & recording == true) {
        counterMax = counter; //Creates a cap for counter to loop
        counter = 0;
        recording = false;
        print("stop");
    }

    //Triggers an animation and sound clip on press
    //If recording, stores action, the frame that action occurred, and the sound
    //into respective arrays - actions[], actionFrames[], actionSounds[]

    //Excluding dotSpray, clears arrays on press

    //Pressing 1 sprays dots while playing a highhat clip
    if (keyCode == 49) {
        hihat.play();
        dotSpray();
        if (recording == true) {
            print('1');
            actionFrames.push(counter);
            actions.push(dotSpray);
            actionSounds.push(hihat);
        }
    }

    //Pressing 2 slides two rectangles while playing a bass drum clip
    if (keyCode == 50) {
        bass.play();
        rectSliders = [];
        rectSlide();
        if (recording == true) {
            print('2');
            actionFrames.push(counter);
            actions.push(rectSlide);
            actionSounds.push(bass);
        }
    }

    //Pressing 3 creates a scaling ring while playing a snare clip
    if (keyCode == 51) {
        snare.play();
        rings = [];
        ringPulse();
        if (recording == true) {
            print('3');
            actionFrames.push(counter);
            actions.push(ringPulse);
            actionSounds.push(snare);
        }
    }

    //Pressing 4 slides a large panel horizontally, plays a hightom clip
    if (keyCode == 52) {
        hightom.play();
        bigSliderH = [];
        bigSlideH();
        if (recording == true) {
            print('4');
            actionFrames.push(counter);
            actions.push(bigSlideH);
            actionSounds.push(hightom);
        }
    }

    //Pressing 5 slides a large panel vertically, plays a lowtom clip
    if (keyCode == 53) {
        lowtom.play();
        bigSliderV = [];
        bigSlideV();
        if (recording == true) {
            print('5');
            actionFrames.push(counter);
            actions.push(bigSlideV);
            actionSounds.push(lowtom);
        }
    }

}

/*----Dot Spray Functions-----------------------------------------------------*/
//Dots fly out from a random position

var dots = []; //For dotSpray

//Places dots into array
function dotSpray() {
    var dotOriginX = random(0, width);
    var dotOriginY = random(0, height);
    for (var i = 0; i < 10; i++) {
        dots.push(makeDot(dotOriginX, dotOriginY));
    }
}

//Creates dot object
function makeDot(inputX, inputY) {
    var dot = {x: inputX,
               y: inputY,
               stepX: random(-20, 20),
               stepY: random(-20, 20),
               color: [random(0, 255), random(0, 255), random(0, 255)],
               size: random(5, 10),
               move: dotStep,
               display: drawDot}
    return dot;
}

//Render dot
function drawDot() {
    noStroke();
    fill(this.color);
    ellipse(this.x, this.y, this.size);
}

//Moves dot
function dotStep() {
    this.x += this.stepX;
    this.y += this.stepY;
}

//If something is in the array, execute animation
function playDotSpray() {
    //Checks if anything exists inside array
    if (dots.length > 0) {
        for (var i = 0; i < dots.length; i++) {
            //Moves and renders
            dots[i].move();
            dots[i].display();

            //If dot leaves canvas, remove it from dots array
            if (dots[i].x < 0 || dots[i].x > width ||
                dots[i].y < 0 || dots[i].y > height) {
                dots.splice(i, 1);
            }
        }
    }
}
/*----------------------------------------------------------------------------*/

/*----rectSlide Functions-----------------------------------------------------*/
//Two rectangles slide vertically across the screen, direction random

var rectSliders = []; //For rectSliders

//Places slider into array
function rectSlide() {
    var startingY = 480;
    var slideRate = 20;

    var sliderY1;    //Input for the first rectangle's Y position
    var sliderStep1; //Input for the first rectangle's move speed

    var sliderY2;    //Input for the second rectangle's Y position
    var sliderStep2; //Input for the first rectangle's move speed

    //50/50 chance on which direction the sliders travel
    var coin1 = coinToss();
    if (coin1 == true) {
        sliderY1 = -startingY;
        sliderStep1 = slideRate;
    } else {
        sliderY1 = startingY;
        sliderStep1 = -slideRate;
    }

    rectSliders.push(makeSlider(100, sliderY1, sliderStep1));

    var coin2 = coinToss();
    if (coin2 == true) {
        sliderY2 = -startingY;
        sliderStep2 = slideRate;
    } else {
        sliderY2 = startingY;
        sliderStep2 = -slideRate;
    }

    rectSliders.push(makeSlider(300, sliderY2, sliderStep2));
}

//Creates slider object
function makeSlider(inputX, inputY, inputS) {
    var slider = {x: inputX,
                  y: inputY,
                  w: 150,
                  h: 480,
                  color: lightCyan,
                  stepY: inputS,
                  move: slideStep,
                  display: drawSlider};
    return slider;
}

//Renders slider
function drawSlider() {
    noStroke();
    fill(this.color);
    rect(this.x, this.y, this.w, this.h);
}

//Moves slider
function slideStep() {
    this.y += this.stepY;
}

//If something is in the array, execute animation
function playRectSlide() {
    //Checks if anything exists inside array
    if (rectSliders.length > 0) {
        for (var i = 0; i < rectSliders.length; i++) {
            rectSliders[i].move();
            rectSliders[i].display();

            //If rectangle leaves canvas, remove it from array
            if (rectSliders[i].y < -500 || rectSliders[i] > 500) {
                rectSliders.splice(i, 1);
            }
        }
    }
}
/*----ringPulse Functions-----------------------------------------------------*/
//Creates a ring that grows or shrinks in size from center
var rings = [];

//Places ring into array
function ringPulse() {
    var scaleRate = 50;
    var startingSize; //Input for ring's starting size
    var scaleTick; //Input for ring scale rate
    var coin = coinToss();

    //Determines direrction ring is scaling
    if (coin == true) {
        //Ring explodes outwards
        startingSize = 0;
        scaleTick = scaleRate;
    } else {
        //Ring collapses inwards
        startingSize = width*1.5;
        scaleTick = -scaleRate;
    }

    rings.push(makeRing(startingSize, scaleTick));

}

//Creates ring object
function makeRing(inputSize, inputStep) {
    var ring = {x: width/2,
                y: height/2,
                size: inputSize,
                step: inputStep,
                color: darkerTeal,
                weight: 50,
                scale: ringStep,
                display: drawRing};
    return ring;
}

//Renders ring
function drawRing() {
    strokeWeight(this.weight);
    stroke(this.color);
    noFill();
    ellipse(this.x, this.y, this.size);
}

//Scales ring
function ringStep() {
    this.size += this.step;
}

//If something is in the array, execute animation
function playRingPulse() {

    if (rings.length > 0) {

        //Renders and scales ring
        for (var i = 0; i < rings.length; i++) {

            rings[i].scale();
            rings[i].display();

            //If ring gets too big or too small, remove it from array
            if (rings[i].size > width*2 || rings[i].size < 0) {
                rings.splice(i, 1);
            }
        }
    }
}
/*----------------------------------------------------------------------------*/

/*----bigSlideH Functions-----------------------------------------------------*/
//Slides a big block of color horizontally across the screen
var bigSliderH = [];

//Places slider into array
function bigSlideH() {
    var startingX = 580;
    var moveRate = 50;
    var sliderHX; //Input for X position of slider
    var sliderHS; //Input for movement rate of slider
    var coin = coinToss();

    //50/50 chance of coming in from either side of canvas
    if (coin == true) {
        sliderHX = -startingX;
        sliderHS = moveRate;
    } else {
        sliderHX = startingX;
        sliderHS = -moveRate;
    }

    bigSliderH.push(makeSliderH(sliderHX, sliderHS));
}

//Creates slider object
function makeSliderH(inputX, inputS) {
    var sliderH = {x: inputX,
                   y: 0,
                   w: width+100,
                   h: height,
                   speed: inputS,
                   col: softPink,
                   move: sliderHStep,
                   display: drawSliderH};
    return sliderH;
}

//Renders slider
function drawSliderH() {
    noStroke();
    fill(this.col);
    rect(this.x, this.y, this.w, this.h);
}

//Moves slider
function sliderHStep() {
    this.x += this.speed;
}

//If something is in the array, execute animation
function playSlideH() {
    if (bigSliderH.length > 0) {
        for (var i = 0; i < bigSliderH.length; i++) {
            bigSliderH[i].display();
            bigSliderH[i].move();

            //Clear array if sliders leave canvas
            if (bigSliderH[i].x < -580 || bigSliderH[i].x > 580) {
                bigSliderH = [];
            }
        }
    }
}
/*----------------------------------------------------------------------------*/

/*----bigSlideV Functions-----------------------------------------------------*/
//Slides a big block of color horizontally across the screen
var bigSliderV = [];

//Places slider into array
function bigSlideV() {
    var startingY = 580;
    var moveRate = 50;
    var sliderVY; //Input for Y position of slider
    var sliderVS; //Input for movement rate of slider
    var coin = coinToss();

    //50/50 chance of coming in from either side of canvas
    if (coin == true) {
        sliderVY = -startingY;
        sliderVS = moveRate;
    } else {
        sliderVY = startingY;
        sliderVS = -moveRate;
    }

    bigSliderV.push(makeSliderV(sliderVY, sliderVS));

}

//Creates slider object
function makeSliderV(inputY, inputS) {
    var sliderV = {x: 0,
                   y: inputY,
                   w: width,
                   h: height + 100,
                   speed: inputS,
                   col: softViolet,
                   move: sliderVStep,
                   display: drawSliderV};
    return sliderV;
}

//Renders slider
function drawSliderV() {
    noStroke();
    fill(this.col);
    rect(this.x, this.y, this.w, this.h);
}

//Moves slider
function sliderVStep() {
    this.y += this.speed;
}

//If something is in the array, execute animation
function playSlideV() {
    if (bigSliderV.length > 0) {
        for (var i = 0; i < bigSliderV.length; i++) {
            bigSliderV[i].display();
            bigSliderV[i].move();

            //Clear array if sliders leave canvas
            if (bigSliderV[i].y < -580 || bigSliderV[i].y > 580) {
                bigSliderV = [];
            }
        }
    }
}
/*----------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------*/
//Returns a Boolean value with a 50/50 chance
function coinToss() {
    var coin = floor(random(0, 2));
    if (coin === 0) {
        return true;
    } else {
        return false;
    }
}

DIRECTIONS: Press number keys 1-5 to play various drum sounds, hit spacebar to record your beat, then spacebar once more to play it back – double-tap spacebar to clear your loop. A red dot in the upper-left indicates that you are recording, and an arrow indicates a loop is being played. Please turn on your speakers.

This program is a reinterpretation of Patatap created by Jono Brandel and Lullatone. While I really enjoyed my experience with Patatap, I felt that it was lacking a system to create a looping beat capability seen on percussion pads or Launchpads. Because of this, I wanted to focus less on creating a complete keyboard’s worth of complex animation and moreso on creating that kind of robust looping system.

The program works by having a running frame counter once recording begins, and storing the action with its respective animation and sound, as well as the frame that it was performed into separate arrays. To loop, the counter resets, and whenever the counter number is the same as a stored frame number, the associated action is executed. For me, the biggest challenge was having all of these arrays relate to each other and keeping everything organized for a clean loop, however once I figured out the core system down, it was just a matter of adding more content in terms of possible actions.

In the future, I’d like to develop this further to have a full keyboard’s worth of sound and animation, as well as being able to perform several loops at once (likely involving storing arrays within arrays), but for now, I’m really pleased with the final result. I’ve played percussion for several years, so it was really enjoyable to get the chance to translate that interest over to a different medium.

Sounds are from Urban Drum Samples on SoundPacks.com, by user Biochron.

kyungak + dayoungl – finalproject

For our final project, Sharon and I made a game named, “TOM.” In order to play TOM, users are to:

  1. Use the arrow keys (up, down, left, right) to move the main character, TOM
  2. Avoid contact with bombs – if contact is made, TOM dies and the game is over
  3. Avoid contact with puffs – if contact is made, TOM loses a point (-1)
  4. Consume hearts – if contact is made, TOM earns a point (+1)
  5. Notice: If bombs make contact with hearts, the bomb bursts and gets rid of the hearts
  6. Notice: Hearts randomly get patched across the canvas. It shrinks in size and disappears if not consumed by TOM.
  7. Notice: When the game starts, you might immediately see the game over sign because the randomly generated bombs overlap with the initial position of TOM (100,100). Don’t panic and just refresh the webpage so you can restart.

Your goal is to get as much point as possible. Compete with friends and see who can get the most points!

Please download the file and use the local file to access the html file of TOM.

PLAY ME

GRADE ME

sketch

//Kyunga Ko
//15104B
//kyungak@andrew.cmu.edu
//Capstone Project:Tom and Toms (COMPLETE VERSION)

var bomb;
var puff;
var heart;
var explosion;
var tomcharacter; 
var eating;
var gameover;
var scoreboard;
var score = 0;

function preload() {

  //Preloading gameover and scoreboard image
  gameover = loadImage("https://i.imgur.com/VlLC4xC.png");
  scoreboard = loadImage("https://i.imgur.com/8ke3Z26.png");

}

function setup() {
  createCanvas(800,800);  

  //Grouping variables
  bomb = new Group();
  puff = new Group();
  heart = new Group();
  explosion = new Group();
  tomcharacter = new Group();
  eating = new Group();

  //Create bomb at random locations on canvas
  for(var i=0; i<15; i++) {
    createBomb(random(0,width), random(0,height));
  }

  //Single character "Tom" is created
  for(var i=0; i<1; i++) {
    createTom(100, 100);
  }

}

function draw() {
  background(230);
  //Reduce the size of the heart by the rate of the frameCount
  if(frameCount%60==0 & heart.length<5) {
    //Create hearts at random locations on the canvas
    createHeart(random(0,width), random(0,height)); 
  }
  
  //Recycle puff on four sides = randomly displaced
  if(frameCount%60==0 & puff.length<10) {
    var canvasside = floor(random(0,4));
    
    if(canvasside == 0) //left
      createPuff(0, random(height));
    if(canvasside == 1) //right
      createPuff(width, random(height));
    if(canvasside == 2) //top
      createPuff(random(width), 0);
    if(canvasside == 3) //bottom
      createPuff(random(width), height);
      
    }
  
  //(BOMB) Bomb orientation in general
  for(var i = 0; i<bomb.length; i++) {
  
    var b = bomb[i];
    b.noisePosition += 0.1;
    b.rotation += (noise(b.noisePosition)-0.5)*10;
    b.setSpeed(2, b.rotation);
    randomrelocation(b);

  }
  
  //(PUFF) When puff collides with bomb and heart
  for(var i = 0; i<puff.length; i++) {
      var p = puff[i]; 
      randomrelocation(p);

      for(var j = 0; j<bomb.length; j++) {
        var b = bomb[j]; 
        //Distance btw puff and bomb
        var dis = p5.Vector.dist(p.position, b.position); 
        
        //Puff and bomb does not attract
        if(dis < 70) {   
          var angle = degrees(atan2(b.position.y-p.position.y, 
            b.position.x-p.position.x));
          //repel
          var attraction = -30 / dis;
          p.addSpeed(attraction, angle);

        }
      }

      for(var z = 0; z<heart.length; z++) {
        var h = heart[z]; 
        //Distance btw heart and puff
        var dis2 = p5.Vector.dist(p.position, h.position); 
        
        //Puff and heart attract
        if(dis2 < 30) {   
          var angle2 = degrees(atan2(h.position.y-p.position.y, 
            h.position.x-p.position.x));
          var attraction2 = 100 / dis2;
          p.addSpeed(attraction2, angle2);

        }

      }
  }

  //(HEART) When heart collides with bomb and puff
  for(var i = 0; i<heart.length; i++) {

      var h = heart[i]; //save in a temp variable
      h.scale = map(h.life, 300, 0, 1, 0);
      h.overlap(bomb, bombeatsheart);
      h.overlap(puff, puffeatsheart); 

  }

  //(TOM) When Tom collides with bomb and heart
  for(var i = 0; i<tomcharacter.length; i++) {

    var t = tomcharacter[i]; //save in a temp variable
    t.overlap(bomb, bombmeetstom); 
    t.overlap(heart, heartmeetstom);
    t.overlap(puff, puffmeetstom);
   
  }

  //Scoreboard
  image(scoreboard, 580, 15); //Scoreboard image
  textSize(30);
  fill(0); 
  text(score,670,90); //Displays the score

  //Draws all sprites
  drawSprites();

}

//Makes Tom move up, down, left, right using the arrow keys
function keyPressed(){
  var tab = 20;
  var clickCount = 0;

  for (var i = 0; i<tomcharacter.length; i++ ){
    var t = tomcharacter[i];

    if (keyIsPressed === true){
      clickCount ++; //clickcount increases with movement
    }

    if (keyIsDown(LEFT_ARROW)){
      t.position.x -= tab; //left
    }

    if (keyIsDown(RIGHT_ARROW)){
      t.position.x += tab; //right
    }

    if (keyIsDown(UP_ARROW)){
      t.position.y -= tab; //up

    } else if (keyIsDown(DOWN_ARROW)){
      t.position.y += tab; //down
      }
    
  }
  
}

//The object dissapears outside the canvas and is randomly located again
function randomrelocation(w) {
   //wrap around the screen
    if (w.position.x > width)
      w.position.x = 0;
    if (w.position.x < 0)
      w.position.x = width;
    if (w.position.y > height)
      w.position.y = 0;
    if (w.position.y < 0)
      w.position.y = height;

}

//When puff eats the heart, they multiply x2 
function puffeatsheart(puff, heart) {
  puff.remove();
  createPuff(heart.position.x, heart.position.y);

}

//When Tom meets puff, score decreases by one
function puffmeetstom(puff, tomcharacter) {
  tomcharacter.remove();
  score--;

}

//Bomb eats/gets rid of the heart + explosion sign
function bombeatsheart(bomb, heart) {
  bomb.remove();
  createExplosion(heart.position.x, heart.position.y);
  createExplosion(heart.position.x, heart.position.y);
  createExplosion(heart.position.x, heart.position.y);
  createExplosion(heart.position.x, heart.position.y);
  createExplosion(heart.position.x, heart.position.y);
  createExplosion(heart.position.x, heart.position.y);
  createExplosion(heart.position.x, heart.position.y);
  

}

//Tom eats heart and +1 sign comes up
function heartmeetstom(heart, tomcharacter) {
  tomcharacter.remove();
  aftereatingheart(heart.position.x, heart.position.y);
  score++;
  
}

//When bomb meets Tom, Tom dies and game over sign comes up
function bombmeetstom(bomb, Tom) {
  bomb.remove();
  noLoop();
  push();
  scale(0.7);
  image(gameover, 175, 400);
  pop();

}

//Bomb is created
function createBomb(x, y) {

  var b = createSprite(x, y);
  b.addAnimation("bomb", "https://i.imgur.com/N4m1kty.png");
  b.setSpeed(2, random(0,360));
  b.noisePosition = random(0, 1000);
  b.maxSpeed = 2;
  bomb.add(b);

}

//When bomb eats heart, explosion is created to indicate that a heart was ate
function createExplosion(x, y) {

  var e = createSprite(x, y);
  e.addAnimation("bomb", "https://i.imgur.com/wzVAcbK.png");
  e.setSpeed(2, random(0,360));
  e.noisePosition = random(0, 1000);
  e.maxSpeed = 2;
  explosion.add(e);

}

//After Tom eats heart, +1 sign is created
function aftereatingheart(x, y) {

  var a = createSprite(x,y);
  a.addAnimation("eat", "https://i.imgur.com/b9C1Xyl.png");
  a.setSpeed(2, random(0,360));
  a.noisePosition = random(0, 1000);
  a.maxSpeed = 2;
  eating.add(a);

}

//Puff is created
function createPuff(x, y) {

  var p = createSprite(x, y);
  p.addAnimation("puff", "https://i.imgur.com/cs8Mkcr.png");
  p.setSpeed(-2, random(0,360));
  p.maxSpeed = 1;
  puff.add(p);

}

//Heart is created
function createHeart(x, y) {
  
  var h = createSprite(x, y);
  h.addAnimation("heart", "https://i.imgur.com/u2uRAYl.png");
  h.life = 300; 
  heart.add(h);

}

//Tom is created
function createTom(x, y) {

  var t = createSprite(x, y);
  t.addAnimation("tomcharacter", "https://i.imgur.com/Q8FnPtP.png", 
    "https://i.imgur.com/QzOR227.png");
  tomcharacter.add(t);

}