## jamieh-final-project

*Move mouse around canvas to move particles

*Click and/or drag to add more particles

``````/*
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.

``````//
// 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
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
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();

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

Producer splits into two after growing big enough:

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

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

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

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.

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.

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.

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

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.

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

``````//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();

//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;
}

//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,
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

``````//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;

var backgroundImageURL = "https://i.imgur.com/qDrD310.png";

var sunImageURL = "https://i.imgur.com/6SDAJjt.png";

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

var bluemothImageURL = "https://i.imgur.com/FR3gCsQ.png";

var beeImageURL = "https://i.imgur.com/oTcCy45.png";

//dog photos
for (var i = 0; i < dogPhotos.length; i++) {
}

//fish photos
for (var i = 0; i < fishPhotos.length; i++) {
}
}

function setup() {

createCanvas(700, 500);

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

//images of dog
//images of fish

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;
num++;
}

//iterates through fish photos when mouse clicks blanket
if (mouseX <= 700 & mouseX >= 0 && mouseY >= 130 && mouseY <= 250) {
var f = num % fishPhotos.length;
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

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

``````//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;

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

//items on the start page before game starts
if(started==false){
// 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,

//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.

``````//Robert Managad
//Section-E

//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();
}

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.
this.position = spinposition;
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);
}
}

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

this.position = dripposition;
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);
}
}

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

this.position = warpposition;
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);
}
}

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

this.position = plusposition;
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);
}
}``````

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

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.

## jennyzha – final project

Final_Project

``````// 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

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
//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

``````//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;

//load all the images

}

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
//and messing everything up
fill(0);
rect(0, 0, 480, 180);

revealCircles = [];
revealDots = [];

// iterate through pixels
image(revealImg, 0, 100);

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

//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);

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

``````//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
var snare;
var bass;
var hihat;
var hightom;
var lowtom;

snare.setVolume(.3);
bass.setVolume(1);
hihat.setVolume(.6);
hightom.setVolume(.5);
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

``````//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 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;

}
}

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;

}

}
}

//(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.setSpeed(2, random(0,360));
b.noisePosition = random(0, 1000);
b.maxSpeed = 2;

}

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

var e = createSprite(x, y);
e.setSpeed(2, random(0,360));
e.noisePosition = random(0, 1000);
e.maxSpeed = 2;

}

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

var a = createSprite(x,y);
a.setSpeed(2, random(0,360));
a.noisePosition = random(0, 1000);
a.maxSpeed = 2;

}

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

var p = createSprite(x, y);
p.setSpeed(-2, random(0,360));
p.maxSpeed = 1;

}

//Heart is created
function createHeart(x, y) {

var h = createSprite(x, y);
h.life = 300;

}

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

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

}

``````