sketch

/*
Connor McGaffin
Section C
cmcgaffi@andrew.cmu.edu
Assignment 11-B
*/

var option = 3; //starting option
var w = 500; //width
var h = 300; //height
var w2 = w / 2; //width / 2
var h2 = h / 2; //height / 2
var x = []; //bubble starting pos
var y = [];
var dx = []; //bubble direction
var dy = [];
var col = []; //bubble color
var np = 50; // how many particles
var nb = 50; //how many bubbles
var title = ["Valerie","Hound Dog", "Step"];
var artist = ["Amy Winehouse", "Elvis Presley", "Vampire Weekend"];
var currentSpot = 200; //current spot and lengths are to be later configured with p5.js addons and Spotify connectivity
var songLength = 300; // placeholder
var tempo = [96,175,78,0]; //stored tempo of songs
var amp = new p5.Amplitude(); //get amplitude
var particles = [];

function preload() {
    img = loadImage("https://i.imgur.com/K3YQPRm.png"); //load spotify logo
    amy = loadSound("https://courses.ideate.cmu.edu/15-104/f2018/wp-content/uploads/2018/12/amy.mp3"); //"Valerie" by Amy Winehouse
    elvis = loadSound("https://courses.ideate.cmu.edu/15-104/f2018/wp-content/uploads/2018/12/elvis.mp3"); //"Hound Dog" by Elvis Presley
    vw = loadSound("https://courses.ideate.cmu.edu/15-104/f2018/wp-content/uploads/2018/12/vw.mp3"); //"Step" by Vampire Weekend 
}

function particleStep() {
    this.x += this.dx;
    this.y += this.dy / 5;
}

function particleDraw() { //draw particle
    strokeWeight(this.s);
    point(this.x, this.y);
}

function makeParticle(px, py, pdx, pdy, ps) { //create particle with starting pos and velocity
    p = {x: px, y: py,
         dx: pdx, dy: pdy, s: ps,
         step: particleStep,
         draw: particleDraw
        }
    return p;
}

function explode() {
    for (var i = 0; i < np; i++) {// make a particle
        fill(255);
        stroke(255);
        var p = makeParticle(w2, h2, random(-30,50), random(-30,50), random(5,10)); //initial location x/y quadrant velocity x/y, size
        particles.push(p); // push the particle onto particles array 
    }
    for (var i = 0; i < np; i++) { // for each particle
        var p = particles[i];
        p.step();
        p.draw();
    }
}

function record () {
    noStroke();
    fill(0);
    ellipse (w2, h2 , 200); //vinyl
    for (i = 0; i < 8; i++){ 
        push();
        noFill();
        stroke(90);
        strokeWeight(1);
        ellipse(w2, h2, 190 - i * 30);
        pop();  //texture
    }
    fill(200); // blue
    ellipse(w2, h2, 75); //label
    push();
    translate(w2,h2);
    rotate(frameCount * 5);
    fill(150);
    ellipse(15, 0, 5); //label spin
    pop();
    fill(255);
    ellipse (w2, h2, 10); // peg
}

function tonearm () {
    angleMode(DEGREES);
    translate(w2 + 100, h2 - 100);
    fill(255);
    ellipse(0, 0, 30); //swivel
    var tonearmPosition = map(currentSpot, 0, songLength, 0, 28);
    push();
    rotate(tonearmPosition);
    push();
    rectMode(CENTER);
    rect(0,125, 12, 25); //cartridge
    pop();
    strokeWeight(3);
    stroke(255);
    line(0, 0, 0, 125); //bar
    pop();
}

function header () {
    image(img,10,10,20,20);
    fill(255);
    textSize(12);
    text(title[option], 35, 20); //display title 12pt
    textSize(9);
    text(artist[option], 35, 30); //display artist 9pt
}


function setup() {
    createCanvas(w, h);
    for (i = 0; i < nb; i++) {  //generate bubbles 
        x[i] = random(width); //randomly 
        y[i] = height; //at the bottom
        dx[i] = random(-.5,.5); //that float sideways randomly 
        dy[i] = random(-2,-1); // and float virtically faster
        col[i] = color(random(255)); //randomly filled in greyscale
    }
}

function draw() {
    background(10,0,0);
    stroke(0);
    strokeWeight(10);
    explode();
    var vol = amp.getLevel();
    var bdiam = map(vol, 0, 1 , 5, 18);
	var vdiam = map(vol, 0, 1, 200, 250); //vinyl amp diameter
    
    for (i = 0; i < nb; i++) {  
        fill(col[i]);
        noStroke();
        ellipse(x[i], y[i], bdiam);
        x[i] += dx[i];
        y[i] += dy[i];
        
    	if (x[i] > width) { //bounce off right wall
        	x[i] = width - (x[i] - width);
        	dx[i] = -dx[i];

    	} else if (x[i] < 0) { //bounce off left wall
        	x[i] = -x[i];
        	dx[i] = -dx[i];

    	}
    	if (y[i] > height) { //bounce off bottom
        	y[i] = height - (y[i] - height);
        	dy[i] = -dy[i];
       
    	} else if (y[i] < 0) { //float off top, appear at bottom
    		y[i] = height;
    	}
	}
	push();
    noStroke();
    fill(180);
    ellipse(w2,h2,vdiam,vdiam);
    pop();
    header();
    record();
    tonearm();         
}

function mouseClicked() {
	particles = []; //clear particles array
	explode(); //create particle explosion
    option ++; //next option when clicked
    if(option > 3){ 
        option = 0;
    }
    if(option == 0){
    	for(i = 0; i < nb; i++){
    		col[i] = color(0, 0, random(255)); //random blue
    		dx[i] = tempo[option] * random(-.1,.1) / 10; //horiz to tempo
    	}
        vw.stop(); //stop "step"
        amy.play(); // play "valerie"
    }
    if(option == 1){
    	for(i = 0; i < nb; i++){
    		col[i] = color(random(255),0,0); //random red
    		dx[i] = tempo[option] * random(-.1,.1) / 10; // horiz to tempo
    	}
        amy.stop(); //stop "valerie"
        elvis.play(); //play "hound dog"
    }
    if(option == 2){
    	for(i = 0; i < nb; i++){
    		col[i] = color(random(255)); //random greyscale
    		dx[i] = tempo[option] * random(-.1,.1) / 10; //horiz to tempo
    	}
        elvis.stop(); //stop "hound dog"
        vw.play(); //play "step"
    }
    if(option == 3){
        vw.stop(); //stop "step"
    	for(i = 0; i < nb; i++){
    		col[i] = color(255); //bubbles fill white
    		dx[i] = random(-.1,.1); //horiz moment unrelated to tempo
    	}
    }
}

Redesigning Spotify Chromecast

This projects is a brief redesign of Spotify’s current Chromecast interface. Pictured below is the current state of the Spotify Chromecast player, which displays the current song, some controls, its information, its album cover, and the album covers of tracks adjacent to it in the queue.

With Spotify Chromecast so frequently being used while hosting a social event, I would argue that the current interface is distracting from the event, or redundant at the least. The current layout would easily enable guests to continuously check which song is coming next, and which songs have already played, freely guiding listeners out of the enjoying the music in the moment. In addition to this, much of the information on the screen is only necessary for the DJ to know, and it is already being provided on their phone’s interface.

With this being said, I looked to create a new interface for Spotify when used on Chromecast that would allow listeners to stay in the moment of the music, while still providing an complimentary atmosphere to that the of the social event.

As the user plays songs, the generated bubbles behind the record and one large bubble behind the record itself jump to the amplitude of the music. The small floating bubbles move horizontally on their upward waltz in speed relation to their tempo.

This project functions as a mock-up of my Chromecast Spotify player as a living environment. Unfortunately, I was not able to configure the p5.js with the Spotify API, as it was out of the scope of 15-104, and thus only these three preloaded songs may be queued. These tracks were chosen with the intent of providing a brief range of visual possibilities this program can generate.

Controls

  • select anywhere on the canvas to play the next song, thus changing the color, size and flow of bubbles

 

Vicky Zhou – Final Project

sketch

/*Vicky Zhou
vzhou@andrew.cmu.edu
Project 12 - Final
Section E*/

var particles; //number of particles in sphere system
var spacing; //spacing of particles
var waveRipple; //manipulates ripple in sphere system
var points; //array for compiling all sphere points
var speed; // speed variable for ripple sphere wave effect
var amplitude; //amplitude of wave

//variables for new toruses
var myx = [];
var myy = [];
var myz = [];

//variables for existing, old toruses
var total = 5;
var locations = new Array(total);
var bool = new Array(total);
var oldpart = [];
var oldpartsize = 12;

//to change displays
var changescene = true;


function setup() {
    createCanvas(600, 600, WEBGL);
    particles = 2000; 
    spacing = 200;
    points = [];
    speed = 0.01;
    amplitude = 0.8;
    wavecol = 0;
    
    if (changescene) {
    	//array of random points for wave of spheres
    	for (var i = 0; i < particles; i++) {
    		points[i] = createVector(random(-5, 5),
    								 random(-5, 5),
    								 random(-5, 5));
    	}
    } else {
    	//array for generated toruses
    	for (var i = 0; i < myx.length; i++) {
    		myx[i];
    		myy[i];
    		myz[i];
    	}

    	//array for already existing toruses
    	for (var i = 0; i < locations.length; i++) {
    		locations[i] = new Array(total);
    		bool[i] = new Array(total);
    		for (var j = 0; j < locations[i].length; j++) {
    			locations[i][j] = new Array(total);
    			bool[i][j] = new Array(total);
    			for (var k = 0; k < locations[i][j].length; k++) {
    				locations[i][j][k] = createVector(i * oldpartsize,
    												j * oldpartsize,
    												k * oldpartsize);
    				bool[i][j][k] = false;
    			}
    		}
    	}

    	var thres = 2; //threshold for state of Torus 
    	for (var i = 0; i < total; i++) {
    		for (var j = 0; j < total; j++) {
    			for (var k = 0; k < total; k++) {
    				stateofTorus = random(15); //random number for state 
    				if (stateofTorus <= thres) {
    					st = 1;
    					bool[i][j][k] = true;
    				} else {
    					st = 0;
    				}
    				oldpart.push(new myTorus(i * oldpartsize,
    										j * oldpartsize,
    										k * oldpartsize,
    										st));
    			}
    		}
    	}
    }
}


//create and push new coordinates for toruses when any key is pressed
function keyPressed() {
	myx.push(random(-10, 10));
	myy.push(random(-300, 300));
	myz.push(random(-300, 300));
}


function draw() {
	background(30, 30, 50);
	wavecol += 1;
	//light that is emiited
	directionalLight(wavecol/2, 200, 200, 50);
	ambientLight(80, wavecol/5, 170);
	//manipulates wave of spheres
	waveRipple = mouseX * speed;

	//center rotating cube
	push();
	rotateX(frameCount/100);
	rotateY(frameCount/100);
	noStroke();
	box(150);
	pop();

	if (changescene) {
		//array of random points to plot each sphere
		for (var i = 0; i < particles; i++) {
			var dot = points[i];
			doty = sin(dot.x + waveRipple) * amplitude;
			push();
			translate(dot.x * spacing, doty * spacing, dot.z * spacing);
			noStroke();
			sphere(10);
			pop();
		}
	} else {
		//array of existing toruses
		push();
		for (var i = 0; i < oldpart.length; i++) {
			oldpart[i].display();
		}
		pop();

		//create new toruses
		for (var i = 0; i < myx.length; i++) {
			rotateY(frameCount/500);
			push();
			if (keyIsPressed === true) {
				updatez = myz[i];
			} else {
				updatez = 100;
			}
			thenewy = cos(waveRipple) * amplitude + myy[i];
			thenewz = 0;
			directionalLight(wavecol/2, 200, 200, 50);
			translate(myx[i], thenewy, thenewz + updatez);
			var gradred = map(mouseX, 0, width, 0, 255);
			var gradgreen = map(mouseX, 0, width, 0, 200);
			fill(gradred, gradgreen, 200);
			strokeWeight(0.2);
			stroke("blue");
			torus(12, 5);
			pop();
		}
	}
}

//function to create existing toruses
function myTorus(x, y, z, st) {
	this.x = x;
	this.y = y;
	this.z = z;
	this.st = st;

	this.display = function() {
		if (this.st == 1) {
			rotateY(frameCount/350);
			push();
			directionalLight(wavecol/2, 200, 200, 250);
			translate(this.x, this.y, this.z * 3.5);
			strokeWeight(0.2);
			stroke("blue");
			torus(oldpartsize, 5);
			pop();
		}
	}
}

//click mouse to toggle between scenes
function mousePressed() {
	changescene =! (changescene);
	setup();
}

Instructions

Mouse Click: toggle between screens

Mouse move: manipulate wave; manipulate color of toruses

Press any key: add a new torus

Hold down any key: explode toruses

Statement

“Re:current” is a project utilizing 3D graphics from WEBGL in order to manipulate arrays and variables to create interactive cyclical experiences — 1. a “current” like nature of manipulating a wave of spheres using curves to mimic ocean-like wave that changes color over time and 2. a looping interaction of adding toruses which rotate around a 3. perennially rotating cube. My greatest struggle was working and adapting to the 3D landscape and nuances of WEBGL, and being able to translate the 2D elements we learned thus far onto my canvas. For my beginning prototypes, the biggest challenge I faced with creating an interactive project was being able to manipulate factors utilizing the mouse press and mouse pressed feature, because of how a mouse inherently creates an output of 2D, whereas my project would call for 3D. I resolved this issue to the best of my ability in this short term by opting to toggle between screens and making basic interactions using the mouse, and then for more detailed engagement I utilized the key pressed feature to generate random x, y, and z coordinates. For further exploration, I would love to explore with more dynamic ways of altering the canvas, ideally in ways to can imitate biomimicry, seeing that WEBGL is able to produce interestingly rendered outcomes with its different textures, lighting, and other attributes.

Screenshots
Some screenshots of my work!

wave manipulation (color change over time)
wave manipulation
initial torus scene
adding more toruses!

Kade Stewart – Final Project

Please download and unzip kades_final. Then just open the HTML file in your browser to play my game. It has instructions in the game itself to play.

My game strays a little bit form my proposal, but is ultimately pretty similar. You just have to reach the right side of the screen and avoid the red planes. Each level has controls that are manipulated in some way, which you’ll learn after you complete the level before. Most of the levels are controlled via the arrow keys, but levels 6 and 7 have special controls you’ll learn when you play. It is not the hardest game, but hopefully you can have some fun (and unlock hard mode when you complete the game)!

Christine Chen-Final Project

Christine Chen-Final-Project

/*
Christine Chen
Section E
cyc1@andrew.cmu.edu
Final Project
*/

var heartArray = [];
var heartCount = 0;

var carrotArray = [];
var carrotCount = 0;

var bombArray = [];

//location where the things that are collected disappear
var collect = 340;
    
//-------------------------------------

function setup() {
    createCanvas(600, 400);
}

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

function draw() {    
    background(255, 178, 220); //light pink background
    backgroundHeart();
    bunny();
    renderHeart();
    renderCarrot();
    renderBomb();
    pointDisplay();
    gameOver();
}

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

//***** HEART *****
function makeHeart(x, y) {
    return {
        x: x,
        y: y,
        draw: drawHeart,
    }
} 

function drawHeart() { //draw shape of heart 
    strokeWeight(0.3);
    noStroke();

    //color of heart changes to give glittering effect
    fill(255 - random(0, width), 116, 208); 
    ellipse(this.x - 5, this.y, 11, 11);
    ellipse(this.x + 5, this.y, 11, 11);
    triangle(this.x - 10.5, this.y + 2, 
        this.x + 10.5, this.y + 2, 
        this.x, this.y + 13);
}

function renderHeart() {
    //creates new heart every 30 frames
    if (frameCount % 30 == 0) {
        var newHeart = makeHeart(random(10, width - 10), 0);
        heartArray.push(newHeart);
    }

    //updates heart's y location(make them fall)
    for (var i = 0; i < heartArray.length; i ++) {
        var heart = heartArray[i];
        heart.y += 1.5;

        //remove hearts that are gone (off of canvas)
        //saves space to prevent codes from running slow
        if(heart.y > height) {
            heartArray.splice(i, 1);
        }

        //remove hearts that are collected
        if(dist(mouseX, collect, heart.x, heart.y) < 20) {
            heartArray.splice(i, 1);
            heartCount++; //keeps track of heart points
        }

        heart.draw();
    }
}

//***** CARROT *****
function makeCarrot(x, y) {
    return {
        x: x,
        y: y,
        draw: drawCarrot
    }
} 

//draws shapes of carrot
function drawCarrot() {
    //carrot stem
    fill(17, 147, 8); //green
    ellipse(this.x, this.y - 5, 5, 15);
    ellipse(this.x - 3, this.y - 5, 5, 15);
    ellipse(this.x - 7, this.y - 5, 5, 15);
    ellipse(this.x + 3, this.y - 5, 5, 15);
    ellipse(this.x + 7, this.y - 5, 5, 15);

    //carrot body 
    fill(255, 117, 25); //orange
    triangle(this.x - 10, this.y, 
        this.x + 10, this.y, 
        this.x, this.y + 30);
}

function renderCarrot() {
    //creates new carrot every 600 frames
    if (frameCount % 600 == 0) {
        var newCarrot = makeCarrot(random(10, width - 10), 0);
        carrotArray.push(newCarrot);
    }

    //updates carrot location
    for (var i = 0; i < carrotArray.length; i ++) {
        var carrot = carrotArray[i];
        carrot.y += 5; //carrot falls quite fast

        //removes carrots that are gone(off of canvas)
        if(carrot.y > height) {
            carrotArray.splice(i, 1);
        }

        //removes carrots that are eaten
        if(dist(mouseX, collect, carrot.x, carrot.y) < 20) {
            carrotArray.splice(i, 1);
            carrotCount++; //keeps track of number of carrots collected
        }

        carrot.draw();
    }
}

//***** BOMB *****
function makeBomb(x, y) {
    return {
        x: x,
        y: y,
        draw: drawBomb,
    }
} 

//draws shape of bomb
function drawBomb() {
    //spark
    fill(255, 0, 0); //red
    ellipse(this.x + 20, this.y - 23, 10, 10);

    //bomb line
    stroke(0); //black 
    strokeWeight(2);
    line(this.x, this.y, this.x + 18, this.y - 20);

    //bomb body
    noStroke();
    fill(48, 47, 47); //dark gray
    ellipse(this.x, this.y, 30, 30);
}


function renderBomb() {
    //creates bomb every 100 frames
    if (frameCount % 100 == 0) {
        var newBomb = makeBomb(random(10, width - 10), 0);
        bombArray.push(newBomb);
    }

    //updates bomb location
    for (var i = 0; i < bombArray.length; i ++) {
        var bomb = bombArray[i];
        bomb.y += 6;

        //removes bomb that is gone (off of canvas)
        if(bomb.y > height) {
            bombArray.splice(i, 1);
        }

        //removes bomb that is collected
        if(dist(mouseX, collect, bomb.x, bomb.y) < 30) {
            bombArray.splice(i, 1);
            //heart point deducted by 10 points if bomb collected
            heartCount -= 10; 
            //carrot point deducted by 1 if bomb collected 
            carrotCount -= 1;
        }

        bomb.draw();
    }
}

//***** BUNNY *****
function bunny() {
    //constrain bunny location to inside canvas
    var control = constrain (mouseX, 15, width - 15);

    noStroke();
    fill(255); //white

    //body
    fill(255); //white
    ellipse(control, 370, 55, 50);

    //feet
    ellipse(control - 15, height - 5, 20, 10); //left 
    ellipse(control + 15, height - 5, 20, 10); //right 

    //hands
    ellipse(control - 27, height - 45, 20, 10); //left 
    ellipse(control + 27, height - 45, 20, 10); //right 

    //ears
    ellipse(control - 8, 300, 10, 30); //left 
    ellipse(control + 8, 300, 10, 30); //right 

    //face
    ellipse(control, 330, 70, 50);

    //eyes
    fill(0);
    ellipse(control - 5, 325, 5, 5); //left 
    ellipse(control + 5, 325, 5, 5); //right 

    //mouth
    fill(255, 122, 169); //pink
    ellipse(control, 338, 12, 10);

    //nose
    fill(0);
    ellipse(control, 331, 5, 4);

    //blush
    fill(252, 204, 204); //light red
    ellipse(control - 10, 331, 10, 7); //left
    ellipse(control + 10, 331, 10, 7); //red
}

//***** GAMEOVER *****
function gameOver() {
    //if carrot point is less than 0,
    //or if heart point is or less than -100,
    //game ends
    if (carrotCount < 0 || heartCount <= -100) {
        noLoop();
    
    //gameover sign background
    fill(167, 0, 47); //red
    rectMode(CENTER);
    rect(width/2 - 10, height/2 - 10, 260, 70);

    //gameover sign text
    fill(255);
    textSize(22);
    textAlign(CENTER);
    text("   G A M E O V E R", width/2 , height/2);

    //skull
    fill(255); //white
    ellipse(width/2 - 100 , height/2 - 10, 30, 22);
    rect(width/2 - 100 , height/2, 16, 10);

    //skull eyes
    fill(50);//gray
    ellipse(width/2 - 105 , height/2 - 10, 7, 7);
    ellipse(width/2 - 95 , height/2 - 10, 7, 7);
    }
}

//***** POINT DISPLAY *****
function pointDisplay() {
    stroke(0); //black
    strokeWeight(0.2); //light outline
    fill(250, 214, 226); //pink
    rect(10, 10, 180, 60);

    //heart
    noStroke();
    fill(108, 136, 255);
    ellipse(35, 35, 11, 11);
    ellipse(45, 35, 11, 11);
    triangle(29.5, 37, 
        50.5 , 37, 
        40, 48);

    //heart count text
    fill(30);
    textSize(20);
    text("= " + heartCount, 55, 45);

    //carrot
    fill(17, 147, 8); //green
    ellipse(120, 25 , 5, 15);
    ellipse(117, 25, 5, 15);
    ellipse(113, 25, 5, 15);
    ellipse(123, 25, 5, 15);
    ellipse(127, 25, 5, 15);

    fill(255, 117, 25); //orange
    triangle(110, 30, 
        130, 30, 
        120, 60);
    
    //carrot count text
    fill(30);
    textSize(20);
    text("= " + carrotCount, 140, 45);
}

//***** BACKGROUND HEART *****
function backgroundHeart() {
    noStroke();
    fill(253, 158, 209); //pink
    ellipse(180, 140, 260, 270);
    ellipse(420, 140, 260, 270);
    triangle(73, 220, 
        width - 73 , 220, 
        width/2, height - 5);
    ellipse(width/2, 220, 80, 80);
}

Heart
Carrot
Bomb

 

 

 

How to play:
– Move bunny left and right with mouse to control bunny’s position
– All things have to touch bunny’s mouth to be effective
– Collect hearts to gain points
– Collect carrots to store as lives
– Carrots save the bunny when bomb hits the bunny
– Bomb takes away bunny’s lives and deduct 10 heart points
– Bunny dies when heart point is -100 or when bomb hits bunny and there is no carrot to save bunny

Statement:
For my project, I decided to create a game that involves a bunny and heart rain. I wanted to create a simple, cute game that people can play with. Hearts are points and the carrots are lives. The bomb is what kills the bunny. The player should aim to gain as many heart points as possible. The stored carrots save the bunny from dying in the case that the bomb hits the bunny. I made the bomb fall fast on purpose so that it will be harder to escape the bomb, adding some excitement to the game. I also made the carrots, which rarely appears (to make the carrots more precious), fall fast. 10 heart points are deducted if the bunny hits the bomb. This is so that the player can’t keep using carrots to save the bunny while not collecting hearts. I made the background simple for this game as because of the “glittering effect” of the heart and the movement of the things falling, having more complicated background will confuse the player’s eyes too much. Initially, I made the hearts simply pink, but then I thought that was too boring and so I changed my codes so that they change colors randomly to create a glittering effect. The things have to touch the bunny’s mouth to be effective as my idea is that the bunny “eats” the things to collect them. For my codes, I used a lot of nested functions to make my codes clearer and easier to manipulate. I had a lot of fun creating this project as I have always wanted to create a cute game with movements of hearts that people can play with that cheers them up on bad days.

 

Collect hearts to gain points
Bomb kills the bunny

Justin Yook- Final Project

stageMaster

// Justin Yook
// jyook@andrew.cmu.edu
// Section C
// Final Project: Stage Master

var dancerArr = []; // Array of dancer objects 
var siz = 20; // Diameter of dancers (fixed)
var cFrame = 0; // Current frame = current count (index)
var tFrames = 8; // Total number of frames = eight count
var c; //Current canvas

function setup() {
    c = createCanvas(480, 360);
}

function draw() {
    background(255);
    // Create Grid 
    // Horizontal lines
    for (var i = 0; i < height; i+=30) {
        stroke(200);
        strokeWeight(1);
        line(0, i, width, i);
    }
    // Vertical lines
    for (var i = 0; i < width; i+=30) {
        stroke(200);
        strokeWeight(1);
        line(i, 0, i, height);
    }

    // Create bottom rectangle
    fill(65, 105, 225);
    rect(0, 300, width, height);

    // Create count circles 
    for (var i = 0; i < 8; i++) {  
        fill(255);
        ellipse(i * 50 + 66, 330, 2 * siz, 2 * siz);

        fill(0);
        textSize(14);
        text(i + 1, i * 50 + 62, 335);
    }

    // Create upper rectangle
    fill(65, 105, 225);
    rect(0, 0, width, height - 300);

    // Check and display dancer objects
    for (var i = 0; i < dancerArr.length; i++) {
        dancerArr[i].draw();
    }

    // Display title
    fill(255);
    textSize(20);
    text("STAGE MASTER", width / 3, height / 8);

    // Indicate which eight count you are currently on with green ellipse
    if (cFrame == 0) {
        fill(0, 255, 0);
        ellipse(66, 330, 40, 40);

        fill(0);
        textSize(14);
        text("1", 62, 335);
    }

    if (cFrame == 1) {
        fill(0, 255, 0);
        ellipse(116, 330, 40, 40);

        fill(0);
        textSize(14);
        text("2", 112, 335);
    }

    if (cFrame == 2) {
        fill(0, 255, 0);
        ellipse(166, 330, 40, 40);

        fill(0);
        textSize(14);
        text("3", 162, 335);
    }

    if (cFrame == 3) {
        fill(0, 255, 0);
        ellipse(216, 330, 40, 40);

        fill(0);
        textSize(14);
        text("4", 212, 335);
    }

    if (cFrame == 4) {
        fill(0, 255, 0);
        ellipse(266, 330, 40, 40);

        fill(0);
        textSize(14);
        text("5", 262, 335);
    }

    if (cFrame == 5) {
        fill(0, 255, 0);
        ellipse(316, 330, 40, 40);

        fill(0);
        textSize(14);
        text("6", 312, 335);
    }

    if (cFrame == 6) {
        fill(0, 255, 0);
        ellipse(366, 330, 40, 40);

        fill(0);
        textSize(14);
        text("7", 362, 335);
    }

    if (cFrame == 7) {
        fill(0, 255, 0);
        ellipse(416, 330, 40, 40);

        fill(0);
        textSize(14);
        text("8", 412, 335);
    }
}

function keyPressed() {
    // Add new dancer at (mouseX, mouseY) when pressing spacebar
    if (key == ' ') { 
        dancerArr.push(makeDancer(mouseX, mouseY, siz));
    }

    // Move to next eight count when pressing 'd'
    if (key == 'd') {
        cFrame += 1;
        cFrame = cFrame % tFrames;
        for (var i = 0; i < dancerArr.length; i++) {
            if (dancerArr[i].posArr.length < cFrame) {
                //x
                dancerArr[i].posArr[cFrame][0] = dancerArr[i].posArr[cFrame - 1][0];

                //y
                dancerArr[i].posArr[cFrame][1] = dancerArr[i].posArr[cFrame - 1][1];
            }
        }
    }

    // Move to previous eight count when pressing 'a'
    if (key == 'a') {
        cFrame -= 1;
        if (cFrame < 0) {
            cFrame += 8;
        }
    }

    // Download screenshot of current formation
    if (key == "s") {
        saveCanvas(c, 'formation', 'jpg');
    }
}

// Click and drag dancer object
function mousePressed() {
    for (var i = 0; i < dancerArr.length; i++) {
        if (dist(mouseX, mouseY, dancerArr[i].posArr[cFrame][0], dancerArr[i].posArr[cFrame][1]) <= (dancerArr[i].ps / 2)) {
            dancerArr[i].drag = true;
        }
    }
}

function mouseDragged() {
    for (var i = 0; i < dancerArr.length; i++) {
        if (dancerArr[i].drag == true) {
            for (var j = cFrame; j < tFrames; j++) {
                dancerArr[i].posArr[j][0] = mouseX; 
                dancerArr[i].posArr[j][1] = mouseY; 
            }
        }
    }
}

function mouseReleased() {
    for (var i = 0; i < dancerArr.length; i++) {
        dancerArr[i].drag = false;
    }
}

//----------------------------------------------------------------------
// DANCER OBJECT
function makeDancer(x, y, s) { 
    var dancer = {"px": x, "py": y, "ps": s, "drag": false};
    var posArr = [];
    for (var i = 0; i < tFrames; i++) {
        posArr.push([x, y]);
    }
    dancer.posArr = posArr;
    dancer.draw = dancerDisplay;
    return dancer;
}

function dancerDisplay() {
    fill(0);
    var cpos = this.posArr[cFrame];
    ellipse(cpos[0], cpos[1], this.ps, this.ps);
}

Instructions:

‘space’ key: Add a new dancer at (mouseX, mouseY)

‘d’ key: Move to the next count

‘a’ key: Move to the previous count

’s’ key: Save a screenshot of the current formation

Mouse: Click, hold, and drag inside dancer to move the dancer

First add a dancer on the canvas, at your mouse position; click and drag the dancer to move them around. If you are satisfied with the placement of your dancers, then move to the next count by pressing ‘d’. If you want to change any previous formations, press ‘a’ to move to the previous count. You can also save a screenshot of the current formation by pressing ’s’.

Statement:

“Stage Master” is a tool for choreographers and dancers to visualize dance formations. I really enjoyed creating this project, because it is something that can help me make formations easily, and in an organized manner. The most challenging part of the project was developing how dancer objects were updated for each count because I had to understand nested arrays and objects very well. The current version of the program only works for a single set of eight counts, but in the future, I would want to add a feature that allows the user to navigate through sets of eight counts. In addition, it would be better if I were to include a feature where the user can play and pause music. Overall, the visuals are not that great, and the amount of code is not a lot, but I believe that the function of the program is useful.

John Legelis – Final Project

sketch

// John Legelis
// Section D
// FINAL

// Board dimensons
var b
var boardW = 200
var boardH = 350
var boardX = 10
var boardY = 10

// Oscillator dimensions/variables
var oscSpacing = boardH/4.1
var numberboxS = 20

// Dial dimensions/variables
var dR = 17
var dialStroke = 4
var dialSpacing = boardW/5
var theta = Math.PI/2
var overDial = false
var moving = false
var iy
var oldTheta
var thetaMin = -1/4*Math.PI
var thetaMax = 5/4*Math.PI

var pressed = false
var oscOn = false
var c = 2**(1/12) // Frequency diffrence between half steps
var cf = 2 **(1/100/12) // Frequency diffrence between cents
var volKnob = 0 // Index of volume knob
var pitchKnob = 1 // Index of pitch knob
var fineKnob = 2 // Index of fine knob

var wList = ["z", "x", "c", "v", "b", "n", "m", ",", ".", "/" ]
var bList = ["s", "d", false, "g", "h", "j", false, "l", ";"]



function setup() {
    createCanvas(600, 385);
    background(0);
    noFill()

    // Draw the board
    b = new ThreeWaveBoard(boardX, boardY, boardW, boardH)
    b.drawBoard()
}

function ThreeWaveBoard(X,Y,W,H) {
    // Board Dimensions
    this.bX = X
    this.bY = Y
    this.bW = W
    this.bH = H
    this.bStroke = 4
    this.oscillators = []

    // Draw board border ==> rectangle
    this.drawBoard = function() {
        stroke(255)
        strokeWeight(this.bStroke)
        rect(this.bX, this.bY, this.bW, this.bH)

        strokeWeight(2)
        rect(this.bW/2,this.bY + 50, 10,10)
        strokeWeight(1)
        textSize(15)
        text("FAT", this.bW/2 + 15, this.bY+60)
        textSize(29)
        text("3 Wave Oscillator", this.bX+8, this.bY+40)

        // Make 3 oscillators
        for (osc=0; osc<3; osc++){
            var oX = this.bX
            var oY = this.bY + oscSpacing*(osc+1)
            var oW = this.bW
            var oNum = osc + 1
            this.oscillators.push(new oscillator(oX, oY, oW, oNum))
            this.oscillators[osc].drawoscillator()
        }
    }

    // Update board
    this.updateBoard = function() {
        stroke(255)
        strokeWeight(this.bStroke)
        rect(this.bX, this.bY, this.bW, this.bH)

        strokeWeight(1)
        textSize(26)
        text("3 Wave Oscillator", this.bX+8, this.bY+40)

        // Update 3 oscillators
        for (osc=0; osc<3; osc++){
            this.oscillators[osc].updateOscillators()
        }
    }
}

function oscillator(X,Y,W,num) {
    // Oscillator Dimensions
    this.oX = X
    this.oY = Y
    this.oW = W
    this.oStroke = 4
    this.oTextStroke = 1
    this.oTextSize = 15
    this.number = num
    this.dials = []
    this.wave = new p5.Oscillator()
    this.f = false
    this.fine = 0
    this.volume = 0

    // Draw oscillator ==> line, number box, number
    this.drawoscillator = function() {
        //line and number box
        stroke(255)
        strokeWeight(this.oStroke)
        line(this.oX, this.oY, this.oX+this.oW, this.oY)
        rect(this.oX, this.oY, 20, 20)

        // Number text
        strokeWeight(this.oTextStroke)
        textSize(this.oTextSize)
        text(num, this.oX+5, this.oY+15)

        // Make volume dial
        var dX = this.oX + dR + this.oW/5 * (2)
        var dY = this.oY + oscSpacing/2
        this.dials.push(new Dial(dX, dY, dR, theta, -12, 12, "Volume"))
        this.dials[0].drawDials()

        // Make pitch dial
        var dX = this.oX + dR + this.oW/5 * (3)
        var dY = this.oY + oscSpacing/2
        this.dials.push(new Dial(dX, dY, dR, theta, -24, 24, "    Pitch"))
        this.dials[1].drawDials()

        // Make fine dial
        var dX = this.oX + dR + this.oW/5 * (4)
        var dY = this.oY + oscSpacing/2
        this.dials.push(new Dial(dX, dY, dR, theta, -12, 12, "    Fine"))
        this.dials[2].drawDials()
    }

    this.updateOscillators = function() {

        // Line and number box
        stroke(255)
        strokeWeight(this.oStroke)
        line(this.oX, this.oY, this.oX+this.oW, this.oY)
        rect(this.oX, this.oY, 20, 20)

        // Number text
        strokeWeight(this.oTextStroke)
        textSize(this.oTextSize)
        text(num, this.oX+5, this.oY+15)

        for (i=0; i<this.dials.length; i++){
            this.dials[i].drawDials()
        }
    }
}

function Dial(X, Y, R, theta, min, max, kind) {
    // Dial dimensions
    this.dialX = X
    this.dialY = Y
    this.dialR = R
    this.stroke = 4
    this.l1X = X
    this.l1Y = Y
    this.l2X = X + R*cos(theta)
    this.l2Y = Y - R*sin(theta)
    this.theta = theta
    this.over = false
    this.kind = kind
    // Ranges of each dials
    this.min = min
    this.max = max
    this.val = 0 // value of dial initialized to 0
    
    // Turns a decimal radian angle into a number rounded to a specific fration of the circle related to the ticks of the dial
    this.inc = function(dec) {
        this.range = (this.max - this.min)/2
        var increment = 3/4*Math.PI/(this.range)
        var d = dec/increment
        var r = round(d)
        var num = r * increment
        return num // Returns radian value along tick marker
    }

    // Draw dial shape and text
    this.drawDials = function() {
        this.l2X = X + R*cos(this.theta)
        this.l2Y = Y - R*sin(this.theta)
        strokeWeight(this.stroke)
        stroke(255)
        ellipse(this.dialX, this.dialY, this.dialR * 2, this.dialR * 2) // Dial circle
        line(this.l1X, this.l1Y, this.l2X, this.l2Y) // Dial line
        strokeWeight(1)
        text(this.val, this.dialX - 4, this.dialY + 32)
        text(this.kind, this.dialX - 25, this.dialY - 22)
        stroke(0)
        
    }
}

function draw() {

    stroke(255)
    noFill()
    background(0)
    b.updateBoard()
    drawPiano(230,20)

    // If a key is being pressed freq is its frequnncy
    if (keyIsPressed & !oscOn && typeof whichKey() == "string" && whichKey() == key) {
        oscOn = true

        for(waves = 0; waves < b.oscillators.length; waves++) {
            // Math for adjusting pitch and volume according to knobs
            b.oscillators[waves].f = frequencyfunc(whichKey()) * (c ** (b.oscillators[waves].dials[pitchKnob].val)) * (cf ** (b.oscillators[waves].dials[fineKnob].val))
            b.oscillators[waves].volume = 0.5 + map(b.oscillators[waves].dials[volKnob].val, -12, 12, -0.5 ,0.5)

            // Update oscilator parameters
            b.oscillators[waves].wave.amp(b.oscillators[waves].volume)
            b.oscillators[waves].wave.freq(b.oscillators[waves].f)
            b.oscillators[waves].wave.start()
        }
    }

    if (oscOn){
        // If another key is pressed while one is already down, priortize newest key
        if (whichKey != key){
            for(waves = 0; waves < b.oscillators.length; waves++) {
                b.oscillators[waves].f = frequencyfunc(whichKey()) * (c ** (b.oscillators[waves].dials[pitchKnob].val)) * (cf ** (b.oscillators[waves].dials[fineKnob].val))
                b.oscillators[waves].wave.freq(b.oscillators[waves].f)

                b.oscillators[waves].volume = 0.5 + map(b.oscillators[waves].dials[volKnob].val, -12, 12, -0.5 ,0.5)
                b.oscillators[waves].wave.amp(b.oscillators[waves].volume)

            }
        }

        if (!keyIsPressed){
            oscOn = false
        }
    }
    // Turn sounds off if oscillator is off --> no key is pressed
    if (!oscOn) {
        for(waves = 0; waves < b.oscillators.length; waves++) {
            b.oscillators[waves].wave.stop()
        }
    }

    // Check each dial in each oscillator
    for(osc=0; osc < b.oscillators.length; osc++){
        for(d=0; d < b.oscillators[osc].dials.length; d++){

            // If clicked / dragged on dial adjust the angle of specific dial respectively
            if ((dist(mouseX, mouseY, b.oscillators[osc].dials[d].dialX, b.oscillators[osc].dials[d].dialY)) <= (dR) & (mouseIsPressed) && !moving) {
                b.oscillators[osc].dials[d].over = true
                moving = true
                iy = mouseY
                oldTheta = b.oscillators[osc].dials[d].theta
            }
            if (!mouseIsPressed) {
                b.oscillators[osc].dials[d].over = false
                moving = false
            }
            if (b.oscillators[osc].dials[d].over == true) {
                var rawTheta = (oldTheta - map((iy - mouseY)*3, 0, height, 0, 2*Math.PI)) // Smooth theta from mouse
                var cTheta = constrain(rawTheta, thetaMin, thetaMax)    // Constrained theta based on min and max
                var iTheta = b.oscillators[osc].dials[d].inc(cTheta)    // Theta chopped to increment
                b.oscillators[osc].dials[d].val = round((map(iTheta, thetaMin, thetaMax, b.oscillators[osc].dials[d].max, b.oscillators[osc].dials[d].min)))
                b.oscillators[osc].dials[d].theta = iTheta
            }
        }
    }
}


// Return string value of a keyboard key, otherwise false
function whichKey(){
    if ( keyIsDown(90) || keyIsDown(83) || keyIsDown(88) || keyIsDown(68) || keyIsDown(67) || keyIsDown(86) || keyIsDown(71) || keyIsDown(66) || keyIsDown(72) || keyIsDown(78) || keyIsDown(74) || keyIsDown(77) || keyIsDown(188)|| keyIsDown(76) || keyIsDown(190)|| keyIsDown(186)|| keyIsDown(191) ) {
        k = key
    }
    if ((keyIsPressed) && (key == k)) {
        return str(k)
    }

    else{
        return false 
    }
}

// Function that draws the piano shapes and text
function drawPiano(x,y){
    var whiteW = 35
    var whiteH = 200
    var blackW = 20
    var blackH = 115

    // White Keys
    for (i=0; i<10; i++){
        push()
        translate(x,y)
        fill(255)
        rect(i*whiteW, 0, whiteW, whiteH)
        
        fill(0)
        textSize(25)
        text(wList[i],i*whiteW+ whiteW/2 - 6, whiteH - 30)
        pop()
    }

    // Black Keys
    for (j=0; j<9; j++){
        if ((j != 2) & (j != 6)) { // Exclude black key between E and F, and B and C
            push()
            translate(x,y)
            fill(0)
            rect((j+1) * whiteW - blackW/2, 0, blackW, blackH)

            fill(255)
            textSize(22)
            text(bList[j],(j+1) * whiteW - blackW/2 + 6 , blackH - 15)

            pop()
        }
    }
    fill(255)
    stroke(255)
    textSize(30)
    text("Click and Drag Dials!", 250,290)
    text("Play piano with keyboard!", 250, 340)
    noFill()
    strokeWeight(5)
    stroke(255)
    rect(240,250,340,110)
}

// Return the frequency of a keyboard key given string of key pressed
function frequencyfunc(str) {
    if (str == "z") {
        f = 523.25 // C5
    } else if (str == "s") {
        f = 554.37 // C#5
    } else if (str == "x") {
        f = 587.33 // D5
    } else if (str == "d") {
        f = 622.25 // Eb5
    } else if (str == "c") {
        f = 659.25 // E5
    } else if (str == "v") {
        f = 698.46 // F5
    } else if (str == "g") {
        f = 739.99 // F#5
    } else if (str == "b") {
        f = 783.99 // G5
    } else if (str == "h") {
        f = 830.61 // Ab5
    } else if (str == "n") {
        f = 880.00 // A5
    } else if (str == "j") {
        f = 932.33 // Bb5
    } else if (str == "m") {
        f = 987.77 // B5
    } else if (str == ",") {
        f = 1046.50 // C6
    } else if (str == "l") {
        f = 1108.73 // C#6
    } else if (str == ".") {
        f = 1174.66 // D6
    } else if (str == ";") {
        f = 1244.51 // Eb6
    } else if (str == "/") {
        f = 1318.51 // E6
    }
    
    else {
        f = false
    }

    return f
}

 

Click within the project to start!

My final project took a lot of effort on the back end side especially on the dial interaction. In order to make them clickable and draggable and easy to interface with, a lot of thinking and debugging had to be done in order for them to behave properly. Then the p5.Oscillators were used as a basis of the sound of my Oscillator. That package took some troubleshooting but eventually I worked out how to create this monophonic synthesizer. I am very proud of this instrument as it is highly functional and can be used as a real live instrument. It was lots of fun to work on and see it come together.

Shirley Chen – Final Project – Little Red Riding Hood

For the final project I create a game based on the story of “Little Red Riding Hood”. In this game, the user use mouse to control the position of Little Red to avoid a group of wolves running randomly. The Little Red should try to get more apples, and for each apple she gets the user can earn 10 points. There is another snake which wants to eat Little Red’s apple, so Little Red should get the apple before the snake reaches it. If the snake eats the apple before Little Red reaches it, the user will lose 10 points. The game would be over if the wolves hit Little Red. Little Red’s grandma is very worried about her granddaughter at the bottom right corner. This project is very fun and I enjoy the process of solving different problems that I met.
Have fun playing it! 🙂

sketch

// Shirley Chen
// Section B
// junfanc@andrew.cmu.edu
// FINAL PROJECT

var xWolf = [];
var yWolf = [];
var dxW = [];
var dyW = [];
var bushesX = [];
var bushesY = [];
var appleX = 300;
var appleY = 300;
var girl;
var wolf;
var bushes;
var apple;
var grandma;
var count = 0;
var snakeX = 590;
var snakeY = 0;
var textAppear = false;


function preload(){
  girl = loadImage("https://i.imgur.com/X6Tfuu7.png");
  wolf = loadImage("https://i.imgur.com/80Jv2F4.png");
  grass = loadImage("https://i.imgur.com/zTPmG8i.png");
  apple = loadImage("https://i.imgur.com/dgBmn61.png");
  snake = loadImage("https://i.imgur.com/QpLwiaZ.png");
  snakeflip = loadImage("https://i.imgur.com/aGK7Aa3.png");
  bushes = loadImage("https://i.imgur.com/mfdC2Ny.png");
  grandma = loadImage("https://i.imgur.com/3dTTlzG.png");
}


function setup(){
  createCanvas(600, 600);
  imageMode(CENTER);
// Randomly select bushes' location
  for (var i = 0; i < 10; i++){
    bushesX.push(random(20, 580));
    bushesY.push(random(20, 580));
  }
// Randomly select wolf's starting location and speed
  for (var i = 1; i < 15; i++) { 
    xWolf[i] = random(0, width);
    yWolf[i] = random(0, height);
    dxW[i] = random(-4, 4);
    dyW[i] = random(-4, 4);
  }
}


function draw(){
  background(229, 255, 229);
// Draw the bushes as background
  for (var i = 0; i < 10; i ++){
    image(bushes, bushesX[i], bushesY[i]);
  }
// Make the wolves move randomly in the canvas
  for (var i = 1; i < 15; i ++){
    image(wolf, xWolf[i], yWolf[i]);
    xWolf[i] += dxW[i];
    yWolf[i] += dyW[i];
// Make the wolves bounce back if they hit the boundary of the canvas 
    if (xWolf[i] + 10 > width || xWolf[i] - 10 < 0){
      dxW[i] =- dxW[i];
      }
    if (yWolf[i] + 15 > height || yWolf[i] - 15 < 0){
      dyW[i] =- dyW[i];
      }
// Create a girl controled by user's mouse
    image(girl, mouseX, mouseY);
// When the girl hits a wolf the game is over
    if (xWolf[i] > mouseX - 10  & xWolf[i] < mouseX + 10){
      if (yWolf[i] > mouseY - 10 && yWolf[i] < mouseY + 10){
        gameOver();
      }      
    }
  }
// Draw an apple with starting location (300, 300)
  image(apple, appleX, appleY);
// Create a snake that will move to eat apple
  var dx = appleX - snakeX;
  var dy = appleY - snakeY;
  snakeX += dx / 70;
  snakeY += dy / 70;
  image(snake, snakeX, snakeY);

// If the girl gets apple, adds 10 point to the score
// If the snake eats apple, minus 10 point to the score
  fill(255, 153, 175);
  textSize(15);
  text("SCORE: " + str(count), 20, 30);
  if (girlGetsApple()){
    pickAppleLocation(); 
    count +=10;
  }
  if (snakeGetsApple()){
    pickAppleLocation();
    count -=10;
  }
// Grandma is very worried about her granddaughter
  image(grandma, 540, 500);
}

// Randomly pick apple's location
function pickAppleLocation(){
  appleX = random(10, 290);
  appleY = random(10, 290);
}

// If the girl or snake gets apple,
// the apple will change to another location
function girlGetsApple(){
  var d = dist(mouseX, mouseY, appleX, appleY);
  if (d < 13){
    return true;
  }
  else{
    return false;
  }
}
function snakeGetsApple(){
  var s = dist(appleX, appleY, snakeX, snakeY);
  if (s < 13){
    return true;
  }
  else{
    return false;
  }
}

// When the gram is over, screen will appear "GAME OVER!"
// and the game will stop
function gameOver(){
  textAppear = true;
  fill(255, 153, 175);
  textSize(30);
  textStyle(BOLD);
  textFont('Arial');
  text("GAME OVER!", 200, 300);
  noLoop();
}


Robert Oh – Final Project- “QuadTask”

version2

//Robert Oh
//Section C
//rhoh@andrew.cmu.edu
//Final Project

//assigning variables

highScore = 0;
score = 0;
scoreTimer = 0;
list1 = [0, 0, 1, 0, 0];
arrList = [0, 0, 0, 0, 0];
circleX = [-1, -1, -1];
circleY = [0, 0, 0];
circleTime = [9, 9, 9];
operations = ["times", "plus", "minus"];
currTime = -1;
currTime2 = -1;
highlightInd = 2;
wallX = 1000;
wallY = 1000;
squareY = 660;
squaredy = 0;
gameState = "Menu";
cause = "";
int1 = 0;
int2 = 0;
int3 = 0;
quizTimer = 7; 
dir = "up";
realDir = "up";
replace = 0;
real = 0;
fake = 0;


//setting up the game
function setup() {
    createCanvas(600, 600);
    frameRate(300);
    arrList[int(random(5))] = makeArrow(0);
    circleTime[0] = 10;
    circleX[0] = random(width / 2 + 25, width - 25);
    circleY[0] = random(25, height / 2 - 25); 
    wallX = width / 2 - 15;
    wallY = int(random(height / 2, height - 100));
    quizTimer = 7;
    int1 = int(random(1, 10));
    int2 = int(random(1, 10));
    if (random(0, 1) > .5){
        replace = 2;
        if (random(0, 1) > .5){
            int3 = int2 + 1;
        }
        else {
            int3 = int2 - 1;
        }
    }
    else{
        replace = 1;
        if (random(0, 1) > .5){
            int3 = int1 + 1;
        }
        else {
            int3 = int1 - 1;
        }
    }
    word = random(operations);
    if (random(0, 1) > .5){
        realDir = "up";
    }
    else {
        realDir = "down";
    }
}

function draw() {
    background(67, 239, 236);
    //playing the game
    if (gameState == "Play"){
        noStroke();
        fill(155, 216, 255);
        rect(0, 0, width / 2, height / 2);
        fill(255, 214, 112);
        rect(width / 2, 0, width, height / 2);
        fill(121, 255, 112);
        rect(0, height / 2, width / 2, height);
        fill(255, 114, 135);
        rect(width / 2, height / 2, width, height);
        strokeWeight(3);
        stroke(89, 89, 89);
        line(0, height / 2, width, height / 2);
        line(width / 2, 0, width / 2, height);
        noStroke();
        fill(0);
        textSize(20);
        textStyle(BOLD);
        text(("Score: " + score), 0, height / 2 )
        drawTopLeft();
        drawTopRight();
        drawBotLeft();
        drawBotRight();
        temp2 = second();
        if (temp2 != scoreTimer){
            scoreTimer = temp2;
            score ++;
        }
    }
    //losing the game
    else if (gameState == "Done"){
        if (score > highScore){
            highScore = score;
        }
        background(255, 91, 151);
        fill(193, 193, 193);
        rect(width / 4, height / 4, width / 2, height / 2, 2);
        textSize(25);
        fill(0);
        noStroke();
        text("Death By: ", width / 2 - 110, height / 2 - 30);
        text(cause, width / 2 + 5, height / 2 - 30);
        fill(7, 246, 255);
        text("High Score: " + highScore, width / 2 - 90, height / 2 + 30);
        fill(101, 255, 73);
        text("RESTART? Press R", width / 2 - 120, height / 2 + 100);
        textStyle(BOLD);
        textSize(40);
        fill(255, 0, 0);
        text("GAME OVER", width / 2 - 125, height / 2 - 70);   
    }
    //main menu
    else if (gameState == "Menu"){
        textStyle(BOLD);
        textSize(70);
        fill(0);
        if (mouseX >= 185 & mouseX <= 400 && mouseY >= 120 && mouseY <= 175){
            fill(0, 255, 0);
        }
        text("START", width / 2 - 120, height / 2 - 130);
        fill(0);
        if (mouseX >= 40 & mouseX <= 570 && mouseY >= 310 && mouseY <= 355){
            fill(255, 0, 0);
        }
        text("INSTRUCTIONS", width / 2 - 250, height / 2 + 60);
        textStyle(NORMAL);
        textSize(20);
        fill(0);
        text("A multi-tasking game made by Robby Oh", width - 375, height - 45);
    }
    //Looking at rules
    else if (gameState == "Info"){
        fill(0);
        textStyle(BOLD);
        textSize(30);
        text("Survive as long as you can!", 100, 40);
        textSize(23);
        textStyle(NORMAL);
        fill(0, 212, 255);
        text("Press 'A' and 'D' to avoid the arrows!", 100, 140);
        fill(255, 214, 112);
        text("Click the circles before time runs out!", 100, 240);
        fill(19, 234, 0);
        text("Press 'Space Bar' to dodge the wall!", 100, 340);
        fill(255, 114, 135);
        text("Press 'W' and 'S' to answer the quiz correctly!", 100, 440);
        fill(0);
        text("Press 'M' to return to the menu!", 100, 540);
    }
}

//drawing the top-left box
function drawTopLeft(){ 
    strokeWeight(0);
    fill(0);
    rect((width / 4) - 127.5, height / 4 + 1.5, 255, 17);
    for (i = 0; i < list1.length; i ++){
        if (list1[i] == 1){
            fill(0, 27, 206);
        }
        else {
            fill(155, 216, 255);
        }
        rect((width / 4 - 123.5) + (i * 50), (height / 4) + 5, 47, 10);
    }
    for (i = 0; i < 5; i ++){
        if (arrList[i] != 0){
            fill(0);
            obj = arrList[i];
            triangle(52 + (i * 50), obj.y, 32 + (i * 50), obj.y - 30, 67 + (i * 50), obj.y - 30);
        }
    }
    updateArr();
}

//drawing the top-right box
function drawTopRight(){
    for (i = 0; i < 3; i ++){
        if (circleX[i] != -1){
            fill(255, 181, 0);
            ellipse(circleX[i], circleY[i], 50);
            fill (0);
            textSize(15);
            textStyle(BOLD);
            text(("" + circleTime[i]), circleX[i] - 4, circleY[i] + 5);
        }
    }    
    updateCircle();
}

//drawing the bottom-left box
function drawBotLeft(){
    fill(6, 211, 50);
    rect(30, squareY, 40, 40);
    fill(0);
    rect(wallX, wallY, 15, 100);
    updateWall();
}

//drawing the bottom-right box
function drawBotRight(){
    if (quizTimer >= 0){
        fill(0);
        textStyle(BOLD);
        textSize(25);
        text("WHAT IS: ", width / 2 + 20, height / 2 + 40);
        disString = ("" + int1) + " " + word + (" " + int2) + " ?"; 
        text(disString, width / 2 + 150, height / 2 + 40);
        if (word == "plus"){
            real = int1 + int2;
            if (replace == 1){
                fake = int3 + int2;
            }
            else {
                fake = int1 + int3;
            }
        }
        if (word == "times"){
            real = int1 * int2;
            if (replace == 1){
                fake = int3 * int2;
            }
            else {
                fake = int1 * int3;
            }
        }
        if (word == "minus"){
            real = int1 - int2;
            if (replace == 1){
                fake = int3 - int2;
            }
            else {
                fake = int1 - int3;
            }
        }
        if (realDir == "up"){
            text(("" + real), width / 2 + 180, height / 2 + 90);
            text(("" + fake), width / 2 + 180, height / 2 + 280);

        }
        else {
            text(("" + fake), width / 2 + 180, height / 2 + 90);
            text(("" + real), width / 2 + 180, height / 2 + 280);
        }
        fill(0);
        textStyle(NORMAL);
        text(("TIMER: " + quizTimer), 320, 475);
        fill(224, 0, 33);
        stroke(0);
        if (dir == "up"){
            rect(width / 2 + 181, height / 2 + 125, 15, 60);
            triangle(width / 2 + 188, height / 2 + 105, width / 2 + 168, height / 2 + 125, width / 2 + 208, height / 2 + 125);
        }
        else if (dir == "down") {
            rect(width / 2 + 181, height / 2 + 185, 15, 50);
            triangle(width / 2 + 188, height / 2 + 250, width / 2 + 168, height / 2 + 230, width / 2 + 208, height / 2 + 230);
        }
        var temp = second();
        if (temp != currTime2){
            quizTimer --;
            currTime2 = temp;
        }
    }
    else{
        if (dir == realDir){
            var temp = second();
            if (temp != currTime2){
                quizTimer --;
                currTime2 = temp;
            }
            if (quizTimer > -3){
                textSize(40);
                text("CORRECT!", 3 * width / 4 - 100, 3 * height / 4 + 10);
            }
            else {
                quizTimer = 7;
                int1 = int(random(1, 10));
                int2 = int(random(1, 10));
                if (random(0, 1) > .5){
                    replace = 2;
                    if (random(0, 1) > .5){
                        int3 = int2 + 1;
                    }
                    else {
                        int3 = int2 - 1;
                    }
                }
                else{
                    replace = 1;
                    if (random(0, 1) > .5){
                        int3 = int1 + 1;
                    }
                    else {
                        int3 = int1 - 1;
                    }
                }
                word = random(operations);
                if (random(0, 1) > .5){
                    realDir = "up";
                }
                else {
                    realDir = "down";
                }
            }
        }
        else {
            cause = " Quiz!";
            gameState = "Done";
        }
    }
    
}

//updating the bottom-left box
function updateWall(){
    if ((wallX <= 70 & wallX >= 30) && ((squareY >= wallY && squareY <= wallY + 100) || (squareY + 40 >= wallY && squareY + 40 <= wallY + 100))){
        cause = " Walls!";
        gameState = "Done";
    }
        
    if (wallX + 15 < 0){
        wallX = width / 2 - 15;
        if (squareY < 3 * height / 4){
            wallY = int(random(squareY, squareY + 20));
        }
        else {
            wallY = int(random(squareY - 80, squareY - 60));
        }
    }
    wallX -= 2;
    squareY -= squaredy;
    if (keyIsDown(32)){
        squaredy = 3;
    }
    else{
        squaredy = -3;
    } 
    if (squareY + 40 >= height){
        squareY = height - 40;
    }
    if (squareY <= height / 2){
        squareY = height / 2;
    }
}

//updating the top-right box
function updateCircle(){
    var count = 0;
    for (i = 0; i < 3; i ++){
        if (circleX[i] != -1){
            count ++;
        }
        if (circleTime[i] <= 0 & circleX[i] != -1){
            cause = " Circles!";
            gameState = "Done";
        }
    }
    if (count == 0){
        circleTime[0] = 9;
        circleX[0] = random(width / 2 + 25, width - 25);
        circleY[0] = random(25, height / 2 - 25); 
        count += 1;
    }
    for (i = 0; i < 3; i ++){
        if (circleX[i] != -1){
            times = second();
            if (times != currTime){
                for (j = 0; j < 3; j ++){
                    if (circleX[i] != -1){
                        circleTime[j] -= 1;
                        currTime = second();
                    }
                }
            }
        }
        else {
            if (count < 3 & max(circleTime) < 7){
                circleTime[i] = 9;
                circleX[i] = random(width / 2 + 25, width - 25);
                circleY[i] = random(25, height / 2 - 25); 
                count += 1;
            }
        }

    }
}

//updating the top-left box
function updateArr(){
    var count = 0;
    for (i = 0; i < 5; i ++){
        if (arrList[i] != 0){
            count ++;
        }
    }
    for (i = 0; i < 5; i ++){
        if (arrList[i] != 0){
            obj = arrList[i];
            if (highlightInd == i & height / 4 > obj.y - 30 && height / 4 + 10 < obj.y ){
                gameState = "Done";
                cause = " Arrows!";
            }

            if (obj.y > height / 2){
                arrList[i] = 0;
            }
            obj.y += obj.dy;
        }
        else {
            if (random(1) > .995 & count < 3){
                arrList[i] = makeArrow(0);
                count ++
            }
        }
    }

}

//key presses
function keyPressed(){
    if (gameState == "Play"){
        if (keyIsDown(65)){
            if (highlightInd > 0){
                list1[highlightInd] = 0;
                highlightInd --;
                list1[highlightInd] = 1;
            }
        }
        if (keyIsDown(68)){
            if (highlightInd < 4){
                list1[highlightInd] = 0;
                highlightInd ++;
                list1[highlightInd] = 1;
            }
        }
        if (keyIsDown(87)){
            dir = "up";
        }
        if (keyIsDown(83)){
            dir = "down";
        }
    }
    if (gameState == "Info"){
        if (keyIsDown(77)){
            gameState = "Menu";
        }
    }
    if (gameState == "Done" & keyIsDown(82)){
        list1 = [0, 0, 1, 0, 0];
        arrList = [0, 0, 0, 0, 0];
        highlightInd = 2;
        gameState = "Menu";
        cause = "";
        arrList[int(random(5))] = makeArrow(0);
        circleX = [-1, -1, -1];
        circleY = [0, 0, 0];
        circleTime = [9, 9, 9];
        currTime = -1;
        circleX[0] = random(width / 2 + 25, width - 25);
        circleY[0] = random(25, height / 2 - 25); 
        wallX = width / 2 - 15;
        wallY = int(random(height / 2, height - 100));
        quizTimer = 7;
        int1 = int(random(1, 10));
        int2 = int(random(1, 10));
        if (random(0, 1) > .5){
            replace = 2;
            if (random(0, 1) > .5){
                int3 = int2 + 1;
            }
            else {
                int3 = int2 - 1;
            }
        }
        else{
            replace = 1;
            if (random(0, 1) > .5){
                int3 = int1 + 1;
            }
            else {
                int3 = int1 - 1;
            }
        }
        word = random(operations);
        if (random(0, 1) > .5){
            realDir = "up";
        }
        else {
            realDir = "down";
        }
        score = 0;
    }
} 

//mouse clicks
function mouseClicked(){
    if (gameState == "Play"){
        for (i = 0; i < 3; i ++){
            if (dist(mouseX, mouseY, circleX[i], circleY[i]) < 25){
                circleX[i] = -1;
                circleTime[i] = 5;
            }
        }
    }
    else if (gameState == "Menu"){
        if (mouseX >= 185 & mouseX <= 400 && mouseY >= 120 && mouseY <= 175){
            gameState = "Play"; 
        }
        
        if (mouseX >= 40 & mouseX <= 570 && mouseY >= 310 && mouseY <= 355){
            gameState = "Info";
        }
    }
}

//object for arrows (top-left box)
function makeArrow(yValue) {
    var arr = {y : yValue,
                dy : random(1, 1.5)};
    return arr;
}

I had a TON of fun creating this final project, which I have named “QuadTask”. Essentially, it is a multi-tasking game where you must look at 4 different boxes and complete each task in order to survive as long as you can. To start, you must click on “Start”, or click on “Instructions” to read up on my rules. It was fun creating a different task for each box and making them all unique but equally challenging! If I had more time to spend, I probably would have made the difficulty increase as the time increases. Personally, my high score was 81, so if you beat that score, kudos to you! All in all, I hope you have fun with my game! – Robert Oh

Jamie Dorst Final Project

sketch

/*
Jamie Dorst
jdorst@andrew.cmu.edu
Final Project
Section A
*/

// global variables
// weather
var weather;
var weatherUrl;
var temp;
var currentLocation;
var condition;
var city;
var rainConditions = ["Patchy light drizzle", "Light drizzle", "Freezing drizzle",
                      "Heavy freezing drizzle", "Patchy light rain", "Light rain",
                      "Moderate rain at times", "Moderate rain",
                      "Heavy rain at times", "Heavy rain", "Light freezing rain",
                      "Moderate or heavy freezing rain", "Light sleet",
                      "Moderate or heavy sleet", "Ice pellets", "Light rain shower",
                      "Torrential rain shower", "Light sleet showers",
                      "Moderate or heavy sleet showers", "Light showers of ice pellets",
                      "Moderate or heavy showers of ice pellets"];
var cloudyConditions = ["Cloudy", "Overcast", "Mist", "Patchy rain possible",
                        "Patchy snow possible", "Patchy sleet possible",
                        "Patchy freezing drizzle possible", "Thundery outbreaks possible",
                        "Fog", "Freezing fog"];
var snowyConditions = ["Blowing snow", "Blizzard", "Patchy light snow", "Light snow",
                       "Patchy moderate snow", "Moderate snow", "Patchy heavy snow", "Heavy snow"];
// custom variables
var button;
var cityInput;
var myFont;
// rain
var rainArray = [];
var rainAmount;
var rainSpeed;
var visibleR = false;
// snow
var snowflakeImg;
var snowArray = [];
var snowAmount;
var snowSpeed;
var visibleS = false;
// clouds
var cloudCover;
var cloudArray = [];
var cloudAmount;
var wind;
var rightWind = ["S", "SE", "E", "NE", "NNE", "ENE", "ESE", "SSE"];
var leftWind = ["N", "NW", "W", "SW", "SSW", "WSW", "WNW", "NNW"];


// preload weather API and snowflake image
function preload() {
    weatherUrl = 'https://api.apixu.com/v1/current.json?key=8f4b2cd0980d46aba2e201006182511&q=Pittsburgh';
    loadJSON(weatherUrl, getWeather);
    snowflakeImg = loadImage('https://i.imgur.com/VADyEQ9.png');
}

function setup() {
    createCanvas(480, 480);
    // case insensitive
    var lowCon = condition.toLowerCase();
    // make rain/snow relative to how heavy it is
    if (lowCon.indexOf('light') > -1) {
        rainAmount = 10;
        rainSpeed = 2;
        snowAmount = 10;
        snowSpeed = 1;
    } else if (lowCon.indexOf('moderate') > -1) {
        rainAmount = 15;
        rainSpeed = 3;
        snowAmount = 15;
        snowSpeed = 2;
    } else if (lowCon.indexOf('heavy') > -1) {
        rainAmount = 20;
        rainSpeed = 4;
        snowAmount = 20;
        snowSpeed = 3;
    } else {
        rainAmount = 30;
        rainSpeed = 5;
        snowAmount = 30;
        snowSpeed = 4;
    }
    // make amount of clouds relative to cloud cover
    cloudAmount = map(cloudCover, 0, 100, 0, 30);
    // prepare for rain/snow/clouds by filling array
    for (var i = 0; i < rainAmount; i++) {
        rainArray[i] = makeRain(random(170, 313), random(0, 315));
    }
    for (var i = 0; i < snowAmount; i++) {
        snowArray[i] = makeSnow(random(175, 305), random(0, 315));
    }
    for (var i = 0; i < cloudAmount; i++) {
        cloudArray[i] = makeCloud(random(-75, width + 75), random(320, height),
                                  random(100, 170), 175);
    }
    // let user name a city with input and button
    cityInput = createInput();
    cityInput.position(width - cityInput.width - 40, 20);
    button = createButton('GO');
    button.position(cityInput.x + cityInput.width + 5, 20);
    button.mouseClicked(changeCity);
}

// function to allow enter key to also submit input
async function keyPressed() {
    if (keyCode === ENTER) {
        changeCity();
    }
}

// function to change the weather to the user-inputted city
function changeCity() {
    // give variable a default
    city = 'Pittsburgh'
    // change city to inputted city
    city = cityInput.value();
    cityInput.value('');
    // reload weather data
    weatherUrl = 'https://api.apixu.com/v1/current.json?key=8f4b2cd0980d46aba2e201006182511&q=' + city;
    loadJSON(weatherUrl, getWeather);
}

async function cloudUpdate() {
    // make amount of clouds relative to cloud cover
        cloudAmount = map(cloudCover, 0, 100, 0, 30);
        // refill arrays so animations change
        for (var i = 0; i < rainAmount; i++) {
            rainArray[i] = makeRain(random(170, 313), random(0, 315));
        }
        for (var i = 0; i < snowAmount; i++) {
            snowArray[i] = makeSnow(random(175, 305), random(0, 315));
        }
        for (var i = 0; i < cloudAmount; i++) {
            cloudArray[i] = makeCloud(random(-75, width + 75), random(320, height),
                                      random(100, 170), 175);
        }
}

// get weather data
function getWeather(weather) {
    temp = Number(weather.current.temp_f);
    condition = weather.current.condition.text;
    currentLocation = weather.location.name;
    cloudCover = weather.current.cloud;
    wind = weather.current.wind_mph;
    windDir = weather.current.wind_dir;
    cloudUpdate();
}

async function draw() {
        // background is light during day, dark during night
        if (hour() > 8 & hour() < 18) {
            background("#0077CC");
        } else {
            background("#00487C");        
        }
        // font and size
        noStroke();
        textFont("Sans-serif");
        textAlign(CENTER);
        fill("EBF2FA");
        textSize(30);
        text(currentLocation, width / 2, 88);
        textSize(100);
        text(temp + "°", width / 2, 220);
        textSize(18);
        text(condition, width / 2, 120);
        textSize(12);
        text("Enter a city name or zip code to change location", 145, 28);
        // draw based on weather conditions
        if (condition === "Sunny") {
            sunny();
        } else if (condition === "Clear") {
            clearSky();
        } else if (condition === "Partly cloudy") {
            sunny();
            cloudy();
        } else if (rainConditions.some(weatherTest) == true) {
            rainy();
        } else if (cloudyConditions.some(weatherTest) == true) {
            cloudy();
        } else if (snowyConditions.some(weatherTest) == true) {
            snowy();
        } else if (condition === ("Patchy light rain with thunder") ||
                                 ("Moderate or heavy rain with thunder")) {
            rainy();
            thunder();
        } else if (condition === ("Patchy light snow with thunder") ||
                                 ("Moderate or heavy snow with thunder")) {
            snowy();
            thunder();
        }
}

// test weather conditions
function weatherTest(value) {
    return value == condition;
}
function directionTest(value) {
    return value == windDir;
}


//======================SUNNY=========================
function sunny() {
    // color of sun mapped to how hot it is
    // redder when hotter, yellower when cooler
    var sunColorG = map(temp, 0, 110, 230, 155);
    noStroke();
    fill(255, sunColorG, 0);
    // draw sun
    ellipse(width / 2, 350, 100, 100);
    // draw sun rays
    push();
    angleMode(DEGREES);
    translate(width / 2, 350);
    for (var i = 0; i < 10; i++) {
        fill ("orange");
        triangle(0, -80, -10, -60, 10, -60);
        rotate(36);
    }
    pop();
}

//======================CLEAR=========================
// function to draw moon
function clearSky() {
    noStroke();
    fill("#EBF2FA");
    ellipse(width / 2, 350, 150, 150);
    fill("#E3E9F2");
    ellipse(270, 330, 20, 20);
    ellipse(220, 360, 60, 60);
    ellipse(200, 300, 10, 10);
    ellipse(277, 400, 25, 25);
    ellipse(250, 291, 30, 30);
}


//=====================CLOUDY=========================
// function to make rain drop objects
function makeCloud(x, y, cloudColor) {
    var cloudObj = {
        x: x,
        y: y,
        cc: cloudColor,
        move: cloudMove,
        render: renderCloud
    };
    return cloudObj;
}

// function to draw clouds
function renderCloud() {
    noStroke();
    fill(this.cc);
    ellipse(this.x, this.y, 75, 75);
    ellipse(this.x + 60, this.y - 17, 100, 100);
    ellipse(this.x + 110, this.y + 5, 50, 50);
}

// function to make clouds move
// based on wind speed and direction
function cloudMove() {
    if (leftWind.some(directionTest) == true) {
        if (this.x > -160) {
            this.x -= map(wind, 0, 50, 0, 10);
        } else if (this.x <= -37.5) {
            this.x = width + 160;
        }
    } if (rightWind.some(directionTest) == true) {
        if (this.x < width + 37.5) {
            this.x += map(wind, 0, 50, 0, 10);
        } else if (this.x >= width + 37.5) {
            this.x = -160;
        }
    }
}

// weather condition function
function cloudy() {
    for (var i = 0; i < cloudAmount; i++) {
        cloudArray[i].render();
        cloudArray[i].move();
    }
}


//======================THUNDER=======================
// function to draw thunder
function thunder() {
    stroke("yellow");
    noFill();
    angleMode(DEGREES);
    arc(300, 320, 60, 60, 280, 0);
    arc(300, 320, 70, 70, 280, 0);
    arc(190, 315, 85, 85, 180, 285);
    arc(190, 315, 95, 95, 180, 285);
}


//======================RAIN==========================
// function to make rain drop objects
function makeRain(x, y) {
    var raindrop = {
        x: x,
        y: y,
        fall: rainFall,
        render: renderRain,
        visibleR: visibleR
    };
    return raindrop;
}

// function to draw rain
function renderRain() {
    noStroke();
    // only display raindrops when they are below the cloud
    if (this.visibleR == false) {
        ellipse(this.x, this.y, 0, 0);
    } else {
        ellipse(this.x, this.y, 7, 13);
    }
}

// function to make rain fall
function rainFall() {
    if (this.y < height) {
        this.y += rainSpeed
    } else if (this.y >= height) {
        this.y = random(300, 315);
    }
    if (this.y < 315) {
        this.visibleR = false;
    } else {
        this.visibleR = true;
    }
}

// weather condition function
function rainy() {
    // case insensitive
    var lowCon = condition.toLowerCase();
    // make rain relative to how heavy it is
    if (lowCon.indexOf('light') > -1) {
        rainAmount = 10;
        rainSpeed = 2;
    } else if (lowCon.indexOf('moderate') > -1) {
        rainAmount = 15;
        rainSpeed = 3;
    } else if (lowCon.indexOf('heavy') > -1) {
        rainAmount = 20;
        rainSpeed = 4;
    } else {
        rainAmount = 30;
        rainSpeed = 5;
    }
    // color of rain dependent upon temperature
    fill(0, map(temp, 32, 100, 255, 0), 255);
    for (var i = 0; i < rainAmount; i++) {
        rainArray[i].render();
        rainArray[i].fall();
    }
    var rainCloud = makeCloud(190, 315, 100);
    rainCloud.render();
}

//=======================SNOW=========================
// function to make snowflake objects
function makeSnow(x, y) {
    var snowflake = {
        x: x,
        y: y,
        fall: snowFall,
        render: renderSnow,
        visibleS: visibleS
    };
    return snowflake;
}

// function to draw snow
function renderSnow() {
    if (this.visibleS == false) {
        image(snowflakeImg, -5, -5, 1, 1);
    } else {
        image(snowflakeImg, this.x, this.y, 15, 15);
    }
}

// function to make snow fall
function snowFall() {
    if (this.y < height) {
        this.y += snowSpeed;
    } else if (this.y >= height) {
        this.y = 315;
    }
    if (this.y < 315) {
        this.visibleS = false;
    } else {
        this.visibleS = true;
    }
}

// weather condition function
function snowy() {
        // case insensitive
    var lowCon = condition.toLowerCase();
    // make snow relative to how heavy it is
    if (lowCon.indexOf('light') > -1) {
        snowAmount = 10;
        snowSpeed = 1;
    } else if (lowCon.indexOf('moderate') > -1) {
        snowAmount = 15;
        snowSpeed = 2;
    } else if (lowCon.indexOf('heavy') > -1) {
        snowAmount = 20;
        snowSpeed = 3;
    } else {
        snowAmount = 30;
        snowSpeed = 4;
    }
    fill(255);
    for (var i = 0; i < snowAmount; i++) {
        snowArray[i].render();
        snowArray[i].fall();
    }
    var snowCloud = makeCloud(190, 315, 100);
    snowCloud.render();
}

For my final project, I created a weather app. It defaults to Pittsburgh, but you can enter in any city or zip code you like and get the weather there. All of the animations are relative to some function of the weather such as the amount of rain, percent of cloud coverage, wind direction, and/or temperature. I learned a lot through making this project, from coming up with a plausible idea to figuring out how to get it to upload, and I’m happy with what I was able to produce.

Emily Zhou –– Final Project

Hi there, my final project is 900 x 450 px but WordPress cuts it off at 600 px width so here is the zip file to see the full thing: moon-exp.zip

Instructions: Click the stars to traverse the timeline of humanity’s exploration of the moon.

Details: Each click should reveal a key event in moon exploration in chronological order. The representation of the moon should slowly fill as time passes. After revealing all 26 events, the stars should be connected as one single constellation.

final project

var starX = [245, 260, 295, 325, 325, 365, 410, 400, 380, 390, 360, 355, 335, 
             265, 240, 205, 185, 170, 85, 95, 50, 60, 55, 65, 100, 120, 140];
var starY = [40, 32, 60, 85, 85, 100, 225, 275, 290, 305, 355, 370, 352, 385, 
             395, 382, 385, 380, 340, 325, 275, 250, 230, 140, 110, 100, 70];
var starD = [10, 6, 4, 10, 10, 6, 10, 4, 5, 8, 5, 4, 8, 6, 
             10, 4, 6, 5, 10, 5, 4, 6, 10, 5, 8, 4, 5];
var time = ["1609", "1610", "1610", "1645", "1647", "1651", "1753", "1824", "1920", "1959", 
            "1961", "1964", "1966", "1967", "1968", "1969", "1969", "1972", "1976", 
            "1990", "1994", "1998", "2007", "2007", "2008", "2009"];
var data = ["Hans Lippershey invented the telescope.",
            "Galileo Galilei made the first telescopic observation of the moon.",
            "Thomas Harriot and Galileo Galilei drew the first telescopic representation of the moon.",
            "Michael Florent van Langren made the first map of the moon.",
            "Johannes Hevelius published the first treatise devoted to the moon.",
            "Giovanni Battista Riccioli named craters after philosophers and astronomers.",
            "Roger Joseph Boscovich proved the moon has no atmosphere.",
            "Franz von Gruithuisen thought craters were formed by meteor strikes.",
            "Robert Goddard suggested sending rockets to the moon.",
            "Soviet spacecraft Luna 2 reached the moon, impacting near the crater Autolycus.",
            "President John F. Kennedy proposed a manned lunar program.",
            "NASA's Ranger 7 produced the first close-up TV pictures of the lunar surface.",
            "Soviet spacecraft Luna 9 made the first soft landing on the moon.",
            "NASA's Lunar Orbiter missions completed photographic mapping of the moon.",
            "NASA's Apollo 8 made the first manned flight to the moon, circling it 10 times before returning to Earth.",
            "Apollo 11 mission made the first landing on the moon and returned samples.",
            "Apollo 12 made first precision landing on the the moon.",
            "Apollo 17 made the last manned landing of the Apollo Program.",
            "Soviet Luna 24 returned the last sample to be returned from the moon (to date).",
            "NASA's Galileo spacecraft obtained multispectral images of the western limb and part of the far side of the moon.",
            "NASA's Clementine mission conducted multispectral mapping of the moon.",
            "NASA's Lunar Prospector mission launched.",
            "Japanese SELENE (Kaguya) spacecraft launched.",
            "Chinese Chang'e 1 lunar orbiter launched.",
            "Indian Chandrayaan 1 moon orbiter launched.",
            "NASA's Lunar Reconnaissance Orbiter launched"]
var newData = [];
var newTime = [];

var speed = 100; // typing speed
var maxCharacters = 65; // max characters per line
var spacing = 15; // spacing between lines
var clicks = 0; // click count
var next = 0; // to evaluate when next star is clicked

var starClickIndices = []; // index values of clicked stars
var clear1 = []; // stores handles for text either appearing or intercepting
var clear2 = []; // stores handles for text either appearing or intercepting
var toClearOne = true; // used to differentiate between clear1 and clear2

function setup() {
    createCanvas(900, 450);
    background(255, 255, 255);

    // to match time to new data array
    for (var i = 0; i < time.length; i++) {
    	newTime.push([]);
    	newTime[i].push(time[i]);
    }

    // code to split long text lines 
    for (var i = 0; i < data.length; i++) {
    	newData.push([]);
    	var stringStart = 0;
    	var stringEnd = 0;
    	
    	while (stringEnd < data[i].length) {

    		stringEnd += maxCharacters;

    		var trunc = stringEnd; // index of where to truncate
    		while (data[i].substring(trunc - 1, trunc) != " " & 
    				(stringEnd - trunc <= 10)) {
    			trunc--;
    		}

    		if (data[i].substring(trunc - 1, trunc) == " ") {
    			newData[i].push(data[i].substring(stringStart, trunc));
    			stringStart = trunc;
    			stringEnd = trunc;
    		}
    		else {
    			newData[i].push(data[i].substring(stringStart, stringEnd));
    			stringStart = stringEnd;
    		}
    	}
    }

    // frame
    noFill();
    stroke(220);
    rect(0, 0, width - 1, height - 1);
}

function draw() {
    // moon
    moonChord();

    // constellation lines
    for (var i = 1; i < starClickIndices.length; i++) {
        stroke(220);
        strokeWeight(0.5);
        line(starX[starClickIndices[i]], starY[starClickIndices[i]], 
        	starX[starClickIndices[i - 1]], starY[starClickIndices[i - 1]]);
    }

    if (next != clicks) {

        // erase text
        fill(255);
        noStroke();
        rect(450, 170, 425, 125);

        // code to type text
        if (toClearOne) {
        	for (var i = clear1.length - 1; i >= 0; i--) {
        		clearTimeout(clear1[i]);
        		clear1.pop(i);
        	}
        }
        else {
        	for (var i = clear2.length - 1; i >= 0; i--) {
        		clearTimeout(clear2[i]);
        		clear2.pop(i);
        	}
        }

        toClearOne = ! toClearOne;

	    fill(220);
	    noStroke();

	    if (toClearOne) {
	        typeWriter(newTime[next], 0, 0, 475, 215, clear1);
	        typeWriter(newData[next], 0, 0, 475, 235, clear1);
	    }
	    else {
	        typeWriter(newTime[next], 0, 0, 475, 215, clear2);
	        typeWriter(newData[next], 0, 0, 475, 235, clear2);

	    }
    }

    // stars
    stroke(150);
    fill(150);
    for (i = 0; i < 27; i++) {
        ellipse(starX[i], starY[i], starD[i], starD[i],)
    }

    next = clicks;
}

function mousePressed() {
    for (var i = 0; i < 27; i++) {
        var distance = dist(mouseX, mouseY, starX[i], starY[i]);
        if (distance <= starD[i] / 2 + 2) {

        	var unclicked = true;
        	for (var j = 0; j < starClickIndices.length; j++) {
        		if (starClickIndices[j] == i) unclicked = false;
        	}

        	if (unclicked) {
	            starClickIndices.push(i);
	            clicks++;
	            if (clicks >= data.length) clicks = 0;
	            curStarIndex = i;
	        }
        }
    }
}

// typewriter effect to print text
function typeWriter(info, r, c, x, y, clear) {
    if (r < (info.length)) {

    	c++;
    	if (c > info[r].length) {
    		c = 1;
    		r++;
    	}

        if (r < info.length) {
	        noStroke();
	        textSize(12);
	        text(info[r].substring(0, c), 
	            x, y + (r * spacing));

	        var handle = setTimeout(function() {
		          typeWriter(info, r, c, x, y, clear)
		        }, speed)
	        clear.push(handle);
	    }

    }
}

// continuously draw chords fill moon
function moonChord() {
    stroke(0, 0, 0, 15);

    var angle1 = random(0, 2 * PI);
    var px1 = 225 + 150 * cos(angle1);
    var py1 = 225 + 150 * sin(angle1);

    var angle2 = random(0, 2 * PI);
    var px2 = 225 + 150 * cos(angle2);
    var py2 = 225 + 150 * sin(angle2);

    line(px1, py1, px2, py2);
}

Progression:

Reflection: The hardest part was getting the typewriter text to print like I wanted and coordinating all of the different actions to the mouse click.

Sources:
The representation of the moon was inspired by this example on p5js.org:
https://p5js.org/examples/math-random-chords.html
I referenced this code as a starting point for developing the typewriter text:
https://gist.github.com/mjvo/2dce29799eb75b7ee1a571380f12ef1b