Use your mouse you glide the paddle to save the ball from dropping.
colorful cubes will be dropping at the consistent timed rate, make sure to hit the brick with the ball to increase count.
When you hit the ball, you get one point count.
For a challenge, click on the screen to add more balls, but you must balance all to save the game.
Im pretty happy with how this turned out, I definetly used almost everything I knew or saw in the course. It was really fun to make a remix of brick breaker and I learned even more along the way. I was definetly fun to recreate my child hood game.
The goal of my final project was to create some interactive project that is simple but playful. When working on this project I took it very incrementally, building on the previous versions and trying to add more elements or making the previous ones better. As I worked my way through the code, I got more comfortable and became more efficient with the way I wrote it. I enjoyed working this way because I found it a lot less intimidating. I really enjoyed making this project. I think if I wanted to I could continue to add on to this. I think a logical next step could be incorporating sound.
Note: I made the canvas size larger then what wordpress can handle because I liked the look of it being a larger area. Below is a zip file of the full size files. The one on wordpress is basically the same just with a few composition adjustments.
Instructions: just use the mouse to play around with the scene. Type into the input bar and hit enter to change the type displayed on the screen.
//Margot Gersing - Final project - mgersing@andrew.cmu.edu - Section E
//for blob shapes
//object one
var xOne;
var yOne;
var dOne;
var opaCityOne = 120;
var overOne = false;
//object two
var xTwo;
var yTwo;
var dTwo;
var opaCityTwo = 120;
var overTwo = false;
//object three
var xThree;
var yThree;
var dThree;
var opaCityThree = 110;
var overThree = false;
//object four
var xFour;
var yFour;
var dFour;
var opCityFour = 110;
var overFour = false;
//object five
var xFive;
var yFive;
var dFive;
var opCityFive = 110;
var overFive = false;
//snake -- code refrenced and modified from p5js examples
//https://p5js.org/examples/interaction-follow-3.html --- link to code refrenced
var snakeX = []; //arrays for snake
var snakeY = [];
var snakeJoint = 10; //amount of joints
var snakeLength = 10; //length between
for (let i = 0; i < snakeJoint; i++) { //loading the joints and lenght into to x and y arrays
snakeX[i] = 0;
snakeY[i] = 0;
}
//for "pong" letters on screen
var letterString = "hello!"; //starting string
var playArray = []; //store the objects (letters and their movements)
var d = [-1, 1]; //direction of array
var input; //for typing
function setup() {
createCanvas(800, 600);
noStroke();
//for type
textFont("Courier New"); //type style
textSize(60); //typesize
//for input bar
input = createInput(); //create the bar to type into
input.position(50, 20);
//for bouncing type, put string into array
for(var i = 0; i < letterString.length; i++){ //load array with objects
var x = map(i, 0, letterString.length, 100, width - 100); //map letters to begin
var vvx = random(1, 5) * d[round(random(0, 1))]; //random velocity in x direction
var vvy = random(1, 5) * d[round(random(0, 1))]; //random velocity in y direction
playArray[i] = createLetter(x, width/2, letterString.charAt(i), vvx, vvy) //call object
}
}
function draw() {
background(250, 120, 110);
//call blob shapes
thingOne();
thingTwo();
thingThree();
thingFour();
thingFive();
//for snake
stroke(87, 156, 128);
strokeWeight(10);
dragSnake(0, mouseX, mouseY); //make snake move with mouse
for (let i = 0; i < snakeX.length - 1; i++) { //create the moevment quality for the snake
dragSnake(i + 1, snakeX[i], snakeY[i]);
}
//get the letters to move around
noStroke();
fill("black");
for(var i = 0; i < playArray.length; i++){
var t = playArray[i];
if(dist(mouseX, mouseY, t.x, t.y) < 30){ //when mouse hovers over letter
t.vx = random(1, 5) * d[round(random(0, 1))]; //get new random velocity x
t.vy = random(1, 5) * d[round(random(0, 1))]; //get new random velocity x
}
if(t.x > width -30 || t.x < 30 || t.y > height - 30 || t.y < 30){ //when the letters hit the walls
t.vx = -t.vx; //reverse the direction
t.vy = -t.vy;
}
t.x = t.x + t.vx; //update the location of the letters
t.y = t.y + t.vy;
text(t.letter, t.x, t.y); //display it
}
}
function thingOne(){
//activation circle
xOne = 25; //location
yOne = 100;
dOne = 600; //diameter
// Test if the cursor is over the box
if(dist(mouseX, mouseY, xOne, yOne) < dOne / 2){ //if the mouse is over the activation ellipse -- var over one is true
overOne = true;
if (overOne == true) { //if true, then change value of opacity which chnages fill color of blob
opaCityOne = 200;
fill(250, opaCityOne, 110); //changing the fill
}
} else { //other wise go back to orignal color
if (opaCityOne >= 120) opaCityOne = opaCityOne - 3; //fades out
fill(250, opaCityOne, 110);
overOne = false;
}
drawOne();
}
function drawOne(){ //location and shape of blob in top left
noStroke();
ellipse(25, 100, 620, 700);
ellipse(50, 300, 450, 320);
}
function thingTwo(){ //same as function thingOne but for bottom right blob
xTwo = 650;
yTwo = 600;
dTwo = 500;
// Test if the cursor is over the box
if(dist(mouseX, mouseY, xTwo, yTwo) < dTwo / 2){
overTwo = true;
if (overTwo == true) {
opaCityTwo = 200;
fill(250, opaCityTwo, 110);
}
} else {
if (opaCityTwo >= 120) opaCityTwo = opaCityTwo - 5;
fill(250, opaCityTwo, 110);
overTwo = false;
}
drawTwo();
}
function drawTwo(){ //location and shape of blob in bottom right
noStroke();
ellipse(650, 600, 600, 500);
ellipse(800, 450, 400, 400);
}
function thingThree(){ //same as function thingOne but for top right blob
xThree = 700;
yThree = -20;
dThree = 500;
// Test if the cursor is over the box
if(dist(mouseX, mouseY, xThree, yThree) < dThree / 2){
overThree = true;
if (overThree == true) {
opaCityThree = 255;
fill(250, 120, opaCityThree);
}
} else {
if (opaCityThree >= 110) opaCityThree = opaCityThree - 3;
fill(250, 120, opaCityThree);
overThree = false;
}
drawThree();
}
function drawThree(){ //location and shape of blob in top right
noStroke();
ellipse(700, -20, 550, 300);
}
function thingFour(){ //same as function thingOne but for center blob
xFour = 475;
yFour = 225;
dFour = 200;
// Test if the cursor is over the box
if(dist(mouseX, mouseY, xFour, yFour) < dFour / 2){
overFour = true;
if (overFour == true) {
opCityFour = 255;
fill(250, 120, opCityFour);
}
} else {
if (opCityFour >= 110) opCityFour = opCityFour - 3;
fill(250, 120, opCityFour);
overFour = false;
}
drawFour();
}
function drawFour(){ //location and shape of blob in center
noStroke();
ellipse(475, 225, 225, 200);
}
function thingFive(){ //same as function thingOne but for bottom left
xFive = 150;
yFive = 600;
dFive = 200;
// Test if the cursor is over the box
if(dist(mouseX, mouseY, xFive, yFive) < dFive / 2){
overFive = true;
if (overFive == true) {
opCityFive = 255;
fill(250, 120, opCityFive);
}
} else {
if (opCityFive >= 110) opCityFive = opCityFive - 3;
fill(250, 120, opCityFive);
overFive = false;
}
drawFive();
}
function drawFive(){ //location and shape of blob in bottom left
noStroke();
ellipse(150, 600, 225, 200);
}
function dragSnake(i, xin, yin) { // for snake
const dx = xin - snakeX[i];
const dy = yin - snakeY[i];
const angle = atan2(dy, dx);
snakeX[i] = xin - cos(angle) * snakeLength;
snakeY[i] = yin - sin(angle) * snakeLength;
joint(snakeX[i], snakeY[i], angle);
}
function joint(x, y, a) { //for snake
push();
translate(x, y);
rotate(a);
line(0, 0, snakeLength, 0);
pop();
}
function createLetter(x, y, letter, vx, vy){ //object for each letter from the array, with loction, velocity
return{x: x, y: y, letter: letter, vx: vx, vy: vy}
}
function keyPressed(){ //when hit enter key call word function
if(keyCode == ENTER) word();
}
function word() { //for changing the letters on the screen
playArray = []; //empty array that holds letters from string
letterString = input.value(); //make the value of the input the new string (what you type)
//print(input.value());
//reload the newstring into the array
for(var i = 0; i < letterString.length; i++){ //load array with objects
var x = map(i, 0, letterString.length, 100, width - 100); //map letters to begin
var vvx = random(1, 5) * d[round(random(0, 1))]; //random velocity
var vvy = random(1, 5) * d[round(random(0, 1))];
playArray[i] = createLetter(x, width/2, letterString.charAt(i), vvx, vvy) //call object
}
}
For my final project, I created an ice-cream decoration game inspired by cooking games that I play as a child. The game allows users to drag around and place the decorations on top of the ice-cream, which changes flavors when being pressed. This project encourage me to create my own illustrations and incorporated them to my code to create a game. I had fun figuring out the interactions the players can explore on the canvas.
//Charmaine Qiu
//Section E
//charmaiq@andrew.cmu.edu
//Final project
//Array of links for icecream images
var iceLinks = [
"https://i.imgur.com/RQVGPb3.png",
"https://i.imgur.com/3NURceZ.png",
"https://i.imgur.com/gk7Jbfc.png",
"https://i.imgur.com/KzfjvaF.png",
"https://i.imgur.com/RyVDAlq.png",
"https://i.imgur.com/OoltZSF.png",
"https://i.imgur.com/DLpHcUh.png",
"https://i.imgur.com/Jd0Hx3a.png",
"https://i.imgur.com/G4zwiYV.png"]
//initialize index and empty array
var iceindex = 0;
var ice = [];
//set dragging for each decorationa as false
var bdragging = false;
var kdragging = false;
var sdragging = false;
var chdragging = false;
var fdragging = false;
var codragging = false;
var finishgame = false;
//set initial coordinate values for the decorations
var bx = 460;
var by = 60;
var kx = 540;
var ky = 100;
var sx = 490;
var sy = 140;
var chx = 160;
var chy = 50;
var fx = 160;
var fy = 125;
var cox = 80;
var coy = 100;
//initiallize offset values
var offsetX;
var offsetY;
var offsetX2;
var offsetY2;
var offsetX3;
var offsetY3;
var offsetX4;
var offsetY4;
var offsetX5;
var offsetY5;
var offsetX6;
var offsetY6;
function preload(){
//preload the icecream images and store them into the arrays
for (var i = 0; i < iceLinks.length; i++){
//store the index from the links to the new arrays
ice[i] = loadImage(iceLinks[i]);
}
//preload the images for decorations
blueberry = loadImage("https://i.imgur.com/BbD4FQd.png");
kiwi = loadImage("https://i.imgur.com/pwxFml9.png");
strawberry = loadImage("https://i.imgur.com/qwPbN2l.png");
cookie = loadImage("https://i.imgur.com/Eo3TG0F.png");
cherry = loadImage("https://i.imgur.com/FI8MUOH.png");
flower = loadImage("https://i.imgur.com/PDusCa3.png");
star = loadImage("https://i.imgur.com/09DBgK0.png");
}
function setup() {
createCanvas(600, 600);
}
function draw() {
background(255, 217, 214);
ellipse(width/2, height, 280, 250);
//draw table
drawCurtain();
//draw the text for instructions
fill(255);
text("Drag to decorate", 240, 90);
text("Click to change a flavor", 210, 120);
//draw the icream base
imageMode(CENTER);
image(ice[iceindex], width / 2, 375);
//draw image of decorations
image(blueberry, bx, by);
image(kiwi, kx, ky);
image(strawberry, sx, sy);
image(cookie, cox, coy);
image(cherry, chx, chy);
image(flower, fx, fy);
//when the decoration is being dragged, apply offset values
if (bdragging){
bx = mouseX + offsetX;
by = mouseY + offsetY;
}
if(kdragging){
kx = mouseX + offsetX2;
ky = mouseY + offsetY2;
}
if(sdragging){
sx = mouseX + offsetX3;
sy = mouseY + offsetY3;
}
if(chdragging){
chx = mouseX + offsetX4;
chy = mouseY + offsetY4;
}
if(fdragging){
fx = mouseX + offsetX5;
fy = mouseY + offsetY5;
}
if(codragging){
cox = mouseX + offsetX6;
coy = mouseY + offsetY6;
}
//draw the buttons for finish and reset
fill(255);
noStroke();
ellipse(80, 500, 100, 40);
ellipse(80, 550, 100, 40);
fill(217, 118, 115);
textSize(20);
text("Finish!", 52, 506);
text("Reset", 52, 556);
//when the game is finished, draw shape that displays the icecream
if(finishgame){
finishGame();
}
}
function mousePressed(){
//when the mouse is pressed on the icecream
if(mouseY > 250 & mouseX > 180 && mouseX < 420) {
//set the current ice equal to current index
var currentIce = iceindex;
while(currentIce === iceindex){
//randomnize the next image
iceindex = floor(random(0, 8));
}
}
//when the mouse is pressed on each decoration, dragging becomes true and
//offset values are applied
if(mouseX > bx - 21 & mouseX < bx + 21 && mouseY > by - 17 && mouseY < by + 17){
bdragging = true;
offsetX = bx - mouseX;
offsetY = by - mouseY;
}
if(mouseX > kx - 28 & mouseX < kx + 28 && mouseY > ky - 27 && mouseY < ky + 27){
kdragging = true;
offsetX2 = kx - mouseX;
offsetY2 = ky - mouseY;
}
if(mouseX > sx - 32 & mouseX < sx + 32 && mouseY > sy - 40 && mouseY < sy + 40){
sdragging = true;
offsetX3 = sx - mouseX;
offsetY3 = sy - mouseY;
}
if(mouseX > chx - 26 & mouseX < chx + 26 && mouseY > chy - 34 && mouseY < chy + 34){
chdragging = true;
offsetX4 = chx - mouseX;
offsetY4 = chy - mouseY;
}
if(mouseX > fx - 22 & mouseX < fx + 22 && mouseY > fy - 29 && mouseY < fy + 29){
fdragging = true;
offsetX5 = fx - mouseX;
offsetY5 = fy - mouseY;
}
if(mouseX > cox - 49 & mouseX < cox + 49 && mouseY > coy - 86 && mouseY < coy + 86){
codragging = true;
offsetX6 = cox - mouseX;
offsetY6 = coy - mouseY;
}
//when the finish button is pressed, the icecream is displayed
if(mouseX > 30 & mouseX < 130 && mouseY > 480 && mouseY < 520){
finishgame = true;
finishGame();
}
//reset the game when button is pressed
reset();
}
function mouseReleased(){
//when the mouse is released, dragging is nolonger happening
bdragging = false;
kdragging = false;
sdragging = false;
chdragging = false;
codragging = false;
fdragging = false;
}
function reset(){
//set cordinates to initial values when the reset button is pressed
if(mouseX > 30 & mouseX < 130 && mouseY > 530 && mouseY < 570){
bx = 460;
by = 60;
kx = 540;
ky = 100;
sx = 490;
sy = 140;
chx = 160;
chy = 50;
fx = 160;
fy = 125;
cox = 80;
coy = 100;
finishgame = false;
}
}
function drawCurtain(){
//when game is not finished, display the curtains
if (!finishgame){
ellipseMode(CENTER);
fill(217, 118, 115);
rect(0 ,0, width, 190);
for(var i = 25; i < width; i+= 50){
fill(217, 118, 115);
ellipse(i, 190, 50, 50);
}
}
}
function finishGame(){
//creates shape that covers the background to display ice
fill(255, 217, 214);
beginShape();
vertex(0, 0);
vertex(0, 525);
vertex(158, 525);
vertex(158, 150);
vertex(440, 150);
vertex(440, 600);
vertex(600, 600);
vertex(600, 0);
vertex(0, 0);
endShape();
//draws the text Enjoy
fill(217, 118, 115);
text("Enjoy~", 280, 140);
//displays stars
frameRate(8);
starX = random(width);
starY = random(height);
starX2 = random(width);
starY2 = random(height);
image(star, starX, starY);
image(star, starX2, starY2);
}
// William Su
// Section E
// wsu1@andrew.cmu.edu
// Final Project
var timer = 0;
let x = 0; //person position x
let x1 = 0;
let ender = 0; //variable used to end a scene.
let years; //calculate random years.
let miles; //calculate random miles.
function setup() {
createCanvas(windowWidth, 750); //Full window
textSize(14);
frameRate(60);
//preload images
Store = loadImage('https://i.imgur.com/L4qngWN.png');
walkin = loadImage('https://i.imgur.com/W2fK1A3.png');
walkout = loadImage('https://i.imgur.com/C5LA4S1.png');
broken = loadImage('https://i.imgur.com/aebY3Uk.png');
iphone = loadImage('https://i.imgur.com/iI6LMAU.png');
iphoneInside = loadImage('https://i.imgur.com/pL2oAbJ.png');
battery = loadImage('https://i.imgur.com/9oZjkzY.png');
numberGen(); //Generate random years and miles.
}
function draw() {
background(255);
//timer starts and 0 and increments every frame of draw.
if (timer > 0) {
background(255);
image(Store, 500, windowHeight/2 - 100, Store.width / 2, Store.height / 2);
image(walkin, 800 - x, windowHeight/2 - 8, 20, 30);
if(x <= 240) {
x += 2;
} else {
x = 590;
}
}
if (timer > 110) {
push();
strokeWeight(2);
fill(255);
beginShape();
vertex(558, windowHeight/2 - 8);
vertex(565, windowHeight/2 - 12);
vertex(565, windowHeight/2 + 24);
vertex(558, windowHeight/2 + 20);
endShape(CLOSE);
pop();
}
if (timer > 130) {
background(255);
image(Store, 500, windowHeight/2 - 100, Store.width / 2, Store.height / 2);
}
if (timer > 140) {
background(255);
image(Store, 500, windowHeight/2 - 100, Store.width / 2, Store.height / 2);
push();
fill(100);
text("Billions of smartphones are sold every year...", windowWidth/2 - 150, windowHeight/2 + 50);
pop();
}
if (timer > 340) {
background(255);
image(Store, 500, windowHeight/2 - 100, Store.width / 2, Store.height / 2);
push();
fill(100);
text("But how far can one get...", windowWidth/2 - 150, windowHeight/2 + 50);
pop();
}
if (timer > 480) {
background(255);
image(Store, 500, windowHeight/2 - 100, Store.width / 2, Store.height / 2);
push();
fill(100);
text("before needing to get a new one?", windowWidth/2 - 150, windowHeight/2 + 50);
pop();
}
if (timer > 620) {
background(255);
image(Store, 500, windowHeight/2 - 100, Store.width / 2, Store.height / 2);
push();
fill(100);
text("Lets find out >\n(Right Arrow Key)", windowWidth/2 - 150, windowHeight/2 + 50);
pop();
}
if (timer > 620 & x1 > 0) {
background(255);
image(Store, 500 - x1, windowHeight/2 - 100, Store.width / 2, Store.height / 2);
push();
fill(100);
text(x1.toString(), windowWidth/2 - 35, windowHeight/2 + 50);
pop();
image(walkout, 590, windowHeight/2 - 8, 20, 30);
if (keyIsDown(RIGHT_ARROW)) {
x1 += 10;
}
}
if (x1 >= totalMiles) {
background(255);
//image(Store, 500, windowHeight/2 - 100, Store.width / 2, Store.height / 2);
image(broken, 590, windowHeight/2 - 11, 20, 30);
push();
fill(100);
text("This person has traveled " + totalMiles.toString() + " miles.", windowWidth/2 - 150, windowHeight/2 + 50);
text("Their phone lasted " + years.toString() + " years.", windowWidth/2 - 150, windowHeight/2 + 65);
text("What makes them fail and\nwhy do companies design it that way?", windowWidth/2 - 150, windowHeight/2 + 95);
pop();
ender += 1;
}
//Landscape Line
push();
strokeWeight(3);
line(0, windowHeight/2 + 20, windowWidth, windowHeight/2 + 20);
pop();
//Borders
push();
noStroke();
fill(255);
rect(0,0, windowWidth/2 - 200, windowHeight);
pop();
push();
noStroke();
fill(255);
rect(windowWidth/2 + 200,0, windowWidth, windowHeight);
pop();
if (ender > 450) { //End scene and move to parts of a phone.
background(255);
//image(Store, 500, windowHeight/2 - 100, Store.width / 2, Store.height / 2);
image(iphone, windowWidth/2 - 150, windowHeight/2 - 200, 300,400);
image(iphoneInside, windowWidth/2 + 50, windowHeight/2 - 200, 300,400);
image(battery, windowWidth/4, windowHeight/2 - 200, 300,400);
push();
noStroke();
fill(255);
rect(windowWidth/2 + 100, windowHeight/2 - 60, 110,250);
pop();
if (mouseX >= windowWidth/4 + 70 & mouseX <= windowWidth/4 + 70 + 115) {
push();
noFill();
stroke(255,0,0);
strokeWeight(4);
rect(windowWidth/4 + 70, windowHeight/2 - 95, 115,270);
pop();
text("Lithium-Ion Battery | Lifespan: 3-5 years\nOne of the most common areas of failure.\nThese also contain many of toxic substances that make it difficult to recycle.", windowWidth/2 - 250, windowHeight/2 +200);
}
if (mouseX >= windowWidth/2 - 90 & mouseX <= windowWidth/2 + 90) {
push();
noFill();
stroke(0,255,0);
strokeWeight(4);
rect(windowWidth/2 - 90, windowHeight/2 - 180, 180, 360);
pop();
text("Screen / Outer Hardware | Lifespan: Varies\nThe most common failure point.\nNowadays, if a screen is cracked, its often cheaper to buy a new phone.", windowWidth/2 - 250, windowHeight/2 +200);
}
if (mouseX >= windowWidth/2 + 120 & mouseX <= windowWidth/2 + 160 + 115) {
push();
noFill();
stroke(0, 0, 255);
strokeWeight(4);
rect(windowWidth/2 + 120, windowHeight/2 - 180, 160, 360);
pop();
text("Circuitboard / Chips | Obsolete: 2 years\nNot common to fail, in fact chips are designed to work for 20-30 years.\nHowever, the technology quickly becomes obsolete due to newer, more powerful chipsets being developed.", windowWidth/2 - 250, windowHeight/2 +200);
}
}
timer += 1; //increment timer per call of draw.
}
function keyPressed() { //Detect right arrow key press.
if (keyCode === RIGHT_ARROW) {
x1 += 1;
}
}
function numberGen() { //Generate numbers.
let years1 = random(1, 4);
years = nf(years1, 1, 2);
miles1 = int(random(1300, 1500));
miles = nf(miles1, 4, 2);
totalMile1 = (miles * years);
totalMiles = nf(totalMile1, 4, 2);
}
function mousePressed() { //Detect mouse press.
return true;
}
For my project, I wanted to highlight the issue of planned obsolescence in our everyday electronics through a simple interactive “scene”. Basically I wanted to show the briefness with which our phones are usable relate it to approximately how many miles we could walk before they utterly fail or become obsolete. At the end, the scene changes and shows some of the places where a phone is designed to fail in 2-3 years.
This project was made to fit the full view width of a window so it might not be completely viewable on wp.
//Yoshi Torralva
//yrt@andrew.cmu.edu
//Final Project
//Section E
//storefront mockup
var mockup;
//input of camera variable
var cameraData;
//points for the hypotrochoid
var pixelPoints = 1000;
//location of hypotrochoid x position array
var placeCurvesX = [];
//location of hypotrochoid y position array
var placeCurvesY = [];
// array to store list of colors at 16 points
var getColors = [];
// array to store brightness of pixels at 16 points
var getBrightness =[];
// varible to move the 16 hypotrochoid's in indivudal rotation
var hypotrochoidsRotate = -1;
// scaled canvas for the interaction in the mockup
var SquareW = 154;
//lading the storefront mockup image
//vector image created by me with Adobe Illustrator
function preload() {
mockup = loadImage("https://i.imgur.com/pTMuI2T.png");
}
//connecting cameraData variable to createCapture
function setup() {
createCanvas(600, 480);
cameraData = createCapture(VIDEO);
cameraData.hide();
noStroke();
}
function draw() {
background(172, 34, 104);
//loading pixels into the canvas
cameraData.loadPixels();
//reflecting and scaling the camera data
//when visitors move to the right/left, it properly reflects movements
push();
translate(150, 0);
scale(-1, 1);
//sizing cameraData to the screen
image(cameraData, 0,0, 154, 154);
pop();
//calling the hypotrochoids function to draw 16 hypotrochoids
hypotrochoids();
//scaling the image from imgur
//placing it above hypotrochoids so that hypotrochoids dont go over the screen
push();
scale(0.24);
image(mockup, 0, 0);
pop();
}
//creating a function to draw a singular hypotrochoid
//setting four parameters for x position, y position, the curves color at one of the 16 points
//and the brightness of one of the 16 points
function drawCurve(x,y, curveColor, curveBrightness) {
push();
//translating with x and y for call parameters
translate(x, y);
//using the brightness in the map() of h and a to adjust size based on camera
var h = map(curveBrightness - 100, 0, height, 0, curveBrightness + 100);
var a = map(curveBrightness - 50, 0 , width, 0, curveBrightness + 50);
//adding curveBrightness with 50 for more detail
curveBrightness = curveBrightness + 50;
var b = a / curveBrightness;
//a small strokeWeight for clarity between curveBrightness
strokeWeight(0.05);
noFill();
//using the curveColor parameter
//adding red and blue to display the hypotrochoids as pink/red
stroke(curveColor + 200, 0, curveColor + 30);
//calling all the variables in a nested for loop to draw the hypotrochoid
beginShape();
// adding 5 extra points to close the hypotrochoid
for(var i = 0; i < pixelPoints + 5; i++) {
var t = map(i, 0, pixelPoints, 0, TWO_PI); {
x = (a-b) * cos(t) + h * cos(((a-b)/ b) * t)
y = (a-b) * sin(t) - h * sin(((a-b) / b) * t)
rotate(hypotrochoidsRotate);
//slight rotation implemented
hypotrochoidsRotate = hypotrochoidsRotate + 0.0001;
//scale of hypotrochoids set to normal
vertex(x, y);
}
}
endShape();
pop();
}
// using nested for loops to place replicate hypotrochoid into 16 hypotrochoids
function hypotrochoids(){
//translating hypotrochoids function to fit into the square screen
push();
translate(302, 185);
//4 in the width of the screen
for(var i = 0; i < 4; i++) {
//4 in the height of the screen
for(var t = 0; t < 4; t++) {
//locationofLoopi and LocationofLoopt space out the hypotrochoids
var locationofLoopi = i * SquareW/4 + 10;
var locationofLoopt = t * SquareW/4 + 10;
//getting the color of locationofLoopi and LocationofLoop 2
var colorLoop = get(locationofLoopi, locationofLoopt);
//getting the brightness of the colors
var sizeLoop = brightness(colorLoop);
rectMode(CENTER);
//implementing spacing into 16 hypotrochoids
drawCurve(locationofLoopi, locationofLoopt, sizeLoop + 20, sizeLoop + 20);
//pushing list into the arrays of location, color, and brightness of hypotrochoids
placeCurvesX.push(locationofLoopi);
placeCurvesY.push(locationofLoopt);
getColors.push(colorLoop);
getBrightness.push(sizeLoop);
}
}
//loop to call the length of the 16 points and implement the parameters from draw curve
for(var a = 0; a < locationofLoopi.length; a++) {
drawCurve(locationofLoopi[a], locationofLoopt[a], getColors[a], getBrightness[a]);
}
pop();
}
For my final project, I wanted to explore the opportunities in which I can apply computational practices into a physical space. I decided to focus on creating a reactive storefront using a camera to show the movement of visitors in a visually intriguing way. To inspire a creative direction for the storefront, I created an Athletic Clothing brand called ové. Ové derives from the french word of l’oeuvre meaning work. Additionally, Ové is the shared characters of Move and Woven. To make the hypotrochoid curves reactive to the camera’s data, I used 16 specific points of pixel brightness and color. Through this, I create an interactive storefront that reacts to traffic and ultimately would entice people to enter the store.
Our project’s canvas is too big for wordpress, so we are uploading the zip file and instructions to acess it.
Click the link to the google drive to find the zip file. Download the zip file to find the sketch.js file “finalproject.js” as well as the html file “finalproject.html” and the sound files. Because the project is outside of wordpress and there is a sound aspect, the project needs to be opened on the local host from your server, so the instructions must be followed bellow…
Open a Terminal in OS X or a command window (cmd) in Windows.
Change your current directory to the directory you want to serve: Type cd path-to-your-directory (ex. cd Desktop/104final )
Type in Terminal: python -m SimpleHTTPServerOr if you are using Python 3, type: python -m http.server
Visit the URL http://localhost:8000 in your browser to test your sketch.
How it works:
Using your mouse, scroll through the four different neighborhoods presented in illustrations.
Click on the music button to hear the song we chose to represent the ambience of the area and click on the pause button to stop the song.
Click on the different buttons for the cafes and restaurants shown to see a photo of the location, a description, and a link to the website.
For our final project, we wanted to portray the distinct atmospheres of different neighborhoods in Pittsburgh. As two students who weren’t familiar with Pittsburgh before Carnegie Mellon, we took the initiative to venture out and explore the different areas and what Pittsburgh has to offer. As people who are interested in immersing and inspiring ourselves through our surroundings, we wanted to introduce our favorite places to other students. Because music is also a very big part of our everyday lives, we wanted to complete the Pittsburgh ambiance by selecting specific songs that we felt captured in each area the best.
We really enjoyed the interactive visual aspect of this project. In the beginning, we brainstormed ways to depict our shared interests and goals through a project, and we felt that this achieved exactly what we envisioned. Making 2D objects into an interactive platform, we saw the potential to incorporate layers of our project into future uses. Our favorite part of the project was probably the music and design aspect of it. We had a lot of fun planning and collaborating together. Below, we have attached a video and screenshots of each different area included in our project.
Word Press doesn’t show you the camera screen so it is difficult to navigate the dots because of the file being so big. Please download this html to play the maze with the camera if you want to see where your tracker is: jinal2-final project. It works best on Chrome. Thank you!
What is it? I know that we did not have much experience with using our webcam into our code, but I really wanted to implement an interactive game that used it. For my project, I wanted to create a camera censored maze game that was moved through your pupils. Before creating the final result, I researched how to sensor your pupils so that the dots follow wherever you look when you are playing the game. However, that was way too ambitious as I struggled to understand how to implement that concept into my code. In the end, I made the camera sensor for any objects that are red so that it will create dots on them.
Due to me not being able to make the game restart when you hit the maze lines, I made the dots go back to the start position when the red tracker is out of the webcam frame. I know that this class is over now, but there are things that I would like to add to my project. Because it took so much longer to try to get the pupil sensors to work, I did not get to work more on other things that I wanted to get to. First, I want to make an “ERROR” sign when the dots hit the white lines of the maze. In addition, I would want to create an intro that would help people calibrate their red object with the screen so that it is easier to control. I had a lot of fun with this project while also pushing myself to try to create something that was way out of my comfort zone.
// Jina Lee
// jinal2@andrew.cmu.edu
// Section E
// 15-104 Final Project
// This codes uses the webcam to create red dots that will
// follow any red color on the camera.
var myCaptureDevice;
// This array stores the x value of the red positions
var xarray = [];
// This array stores the y value of the red positions
var yarray = [];
// Limits of x and y arrays
var sizeLimit = 10;
var redCircle = 1;
function setup() {
createCanvas(500, 500);
// Camera
myCaptureDevice = createCapture(VIDEO);
myCaptureDevice.size(500, 500);
}
function isColor(c) {
// Tests the colors
return (c instanceof Array);
}
function draw() {
// Background
fill(0);
rect(0, 0, 500, 500);
// Maze
stroke(255);
strokeWeight(10);
line(250, 250, 250, 320);
line(250, 250, 250, 320);
line(250, 320, 320, 320);
line(320, 320, 320, 190);
line(320, 320, 320, 180);
line(320, 180, 180, 180);
line(180, 180, 180, 370);
line(180, 370, 380, 370);
line(380, 370, 380, 130);
line(380, 130, 135, 130);
line(135, 130, 135, 420);
line(135, 420, 430, 420);
line(430, 420, 430, 70);
line(430, 70, 70, 70);
//boxes
fill(255);
noStroke();
rect(65, 65, 50, 50, 10);
rect(-5, 150, 100, 100, 10);
rect(60, 300, 80, 80, 10);
rect(-10, 430, 70, 70, 10);
// Starting Spot
fill(255);
rect(225, 230, 50, 50, 10);
textSize(10);
textFont('Helvetica');
fill('green');
text("START", 235, 260);
// Exit Spot
fill(255);
rect(440, 470, 50, 50, 10);
textSize(10);
textFont('Helvetica');
fill('red');
text("END", 455, 495);
// Red dots are created because of camera sensor
var xMin = 0;
var xMax = 500;
var yMin = 0;
var yMax = 500;
// For loop that draws the dots
for (var a = 0; a < xarray.length; a++){
// Ellipse
if (redCircle === 1){
noStroke();
fill('red');
ellipse(xarray[a], yarray[a], 5, 5);
}
}
for (var i = 0; i < width; i += 5){
for (var q = 0; q < height; q += 5) {
var currentColor = myCaptureDevice.get(i, q);
var r = red(currentColor) - 255;
var g = green(currentColor) - 0;
var b = blue(currentColor) - 0;
// If rbg matches criteria
// than get the center point of the red area
// and put it in an array
if (isColor(currentColor)){
var dist = sqrt(sq(r) + sq(g) + sq(b));
if (dist < 120) {
// Finds the location of the red areas on the web cam
// Finds the smallest x location
if (i > xMin){
xMin = i;
}
// Find the biggest x location
if (i < xMax){
xMax = i;
}
// Finds the smallest y loctaion
if (q > yMin){
yMin = q;
}
// Finds the biggest y location
if (q < yMax){
yMax = q;
}
}
}
}
}
// Push the new x of red from camera into the array
xarray.push((xMin + xMax) / 1.9);
// Push the new y of red from camera into the array
yarray.push((yMin + yMax) / 1.7);
// When array is full, it will pop something out from the beginning
while (xarray.length > sizeLimit) {
xarray.shift();
yarray.shift();
}
}
//Alice Cai
//Section E
//alcai@andrew.cmu.edu
//Final Project
//global variables for letter A
//starting point of angles for turning
var angleA = 0;
var angleA2 = 0;
//starting velocity of turning
var velA2 = -1;
//starting location
var y = 0;
//color variables
var r;
var g;
var b;
//global variables for letter B
var angleC = 70;
var angleC2 = 90;
var cVel1 = 0;
var cVel2 = 0;
//varying starting sizes of circles
var size1 = 1;
var size = 0;
var rC;
var gC;
var bC;
function setup() {
createCanvas(600, 300);
frameRate(10); //slow down framerate
}
function draw() {
//background is random color
background(g, r, r);
//constrain black circle to height of canvas
let cc = constrain(mouseX, 0, height);
//keep ellipse concentric to either center of A or the entire A
if (mouseX > width / 5) {
var cy = height / 2;
} else {
cy = height / 2 + 30;
}
fill(0);
ellipse(width /4, cy, cc,cc);
//draw A cap/serif. Made up of a varying amount of circles controlled by mouseX
for (var i = 0; i <= mouseX; i ++) {
noStroke();
push();
translate(width /4, height / 2 - 110 + 75);
rotate(radians(angleA2));
fill(r, b, g);
ellipse(50, y, 30, 30);
pop();
angleA2 = angleA2 + velA2;
//constrain rotating movment of ellipses to the shape of a
if (angleA2 < -135 ) {
velA2 = velA2 * -1;
}
//if surpasses "semicircle", stop rotating and move downwards.
if (angleA2 >= 0 & velA2 === 1) {
angleA2 = 0;
y = y + velA2;
}
//if hits bottom, come back up
if (y > 115) {
y = y - 115;
velA2 = velA2 * -1;
angleA2 = angleA2 + velA2;
}
}
//draw circle made up of more circles, quantity/length controled by mouseX
for (var i = 0; i <= mouseX / 2; i ++) {
noStroke();
push();
translate(width / 4, height / 2 + 30);
rotate(radians(angleA));
let xc = constrain(mouseX, 0, 50); //constrain diameter of circle
fill(r, b, g);
ellipse(xc, 0, 30, 30);
pop();
angleA = angleA - i * 0.8;
}
//draw C, made up of circles
for (var i = 0; i <= 45; i ++) {
noStroke();
push();
translate(width / 4 * 3, height / 2);
//C is constantly roatating
rotate(radians(angleC + i * 5));
//constrain size of C based on mouseX
let xc = constrain(mouseX / 4, 0 , width / 8);
fill(bC, rC, gC);
ellipse(xc, 0, 30, 30);
angleC = angleC - cVel1;
pop();
}
//constrain rotation of C to go back and forth and not become a circular motion
if (angleC2 < 90) {
cVel1 = cVel1 * -1;
}
if (angleC2 > 0) {
cVel1 = cVel1 * -1;
}
//create two rings of circles around and inside C
let numb = constrain(mouseX, 0, 50)
for (var a = 0; a <= numb / 5; a ++) {
noStroke();
push();
translate(width / 4 * 3, height / 2);
rotate(radians(a * 25 + angleC2 ));
fill(rC, bC , gC );
size1 = size1 + cVel1
let xc = constrain(size1, 0, width / 8)
var size = 5;
//ellipse size varies with sin and cosine to create explosive firework effect when clicked
ellipse(xc - 30, 0, size + xc / 3 * cos(xc) / 2, size + xc / 3 * cos(xc) / 2);
angleC2 = angleC2 + cVel1;
pop();
}
for (var a = 0; a <= 20; a ++) {
noStroke();
push();
translate(width / 4 * 3, height / 2);
rotate(radians(a * 20 + angleC2 - 30));
fill(bC, gC, rC);
size1 = size1 + cVel2
let xc = constrain(size1, 0, width / 5)
var size = 5;
ellipse(xc, 0, size + xc / 3 * sin(xc), size + xc / 3 * sin(xc));
angleC = angleC - cVel2 ;
pop();
}
}
function mousePressed() {
loop(); //starts loop
r = random(100, 255); // r is a random number between 100 - 255
g = random(100, 255); // g is a random number betwen 100-255
b = random(100, 255); //b is a random number betwen 100 - 255
//set velocity and size for movement
size1 = 1
cVel2 = 1
cVel1 = 0.5
rC = random(100, 255);
gC = random(30, 200);
bC = random(100, 255);
}
function mouseReleased() {
noLoop(); //freezes loop
//set velocity and sizes to 0
size1 = 0
cVel2 = 0
cVel1 = 0
angleC = 70 //set C angle to standard
}
This is a fun animation of my initials AC; I wanted to get more into creating motion graphics and decided it would be useful to begin brainstorming animations for a logo or graphic for my website. Interact by clicking and releasing, as well as dragging your mouse across the page. Colors change every click. The motif of circles runs throughout the construction of each letter; every line is drawn with ellipses. Thus, at any frame of the animation, the composition is somewhat unified.
Instructions: Please expand your browser window as much as possible since my canvas is at the width of 600! Thank you 🙂 To interact with my speculative comic panel, here are a couple of interactions you can try. To speed up the subway car, press the “f” key. To slow down the subway car, press the “s” key. (Note: you cannot make the subway go backwards. If you press this key too many times, it will reset to its original speed.) To reset the subway car speed, press the “r” key. In frames 1 & 2, click your mouse around to see some falling stars.
/*
* Angela Lee
* Section E
* ahl2@andrew.cmu.edu
* Final Project
*/
// FRAME 1 VARIABLES
var f1height = 270; // height of first frame
var f1width = 275; // width of first frame
var f1x = 22.5; // upper left x-pos of first frame
var f1y = 12.5; // upper left y-pos of first frame
var f1spacing = 9; // spacing to the right of the first frame
var accelerate = 0; // how much the subway car speeds up/slows down
var skyBlue; // top of the sky color
var skyYellow; // bottom of the sky color
var waterYellow; // top of the water color
var waterBlue; // bottom of the water color
var shootingStarArray = []; // array for shooting stars
var ripples = []; // array for the ripples
// FRAME 2 VARIABLES
var f2width = 270; // width of second frame
var f2height = 130; // height of second frame
var f2spacing = 11.5; // spacing below the second frame
var planetx = 400; // x position of the big planet
var subwayStarArray = []; // array for subway stars
function setup(){
createCanvas (600, 300);
background(27, 34, 45);
// gradient for the sky in frame 1
skyBlue = color(2, 76, 98); // top of the sky color
skyYellow = color(233, 255, 191); // bottom of the sky color
// gradient for the water in frame 1
waterYellow = color(252, 239, 194); // top of the water color
waterBlue = color(59, 98, 115); // bottom of the water color
sub = makeSubway(); // creates a subway object in frame 1
// RIPPLES IN FRAME 1
// boundaries for ripples in frame 1
var top = 212; // top boundary
var bottom = height; // bottom boundary
// first ripples that come into frame 1
for (var i = 0; i < 10; i++) {
var rippleX = random(width);
var rippleY = random(top, bottom);
ripples[i] = makeRipples(rippleX, rippleY);
}
}
function draw(){
// FRAME 1
f1scene(); // the sky
// moving the subway
sub.move();
sub.draw();
// moving the ripples
for (var i = 0; i < ripples.length; i++) {
ripples[i].move();
ripples[i].draw();
}
removeRipple();
addRipple();
// FRAME 2
f2scene(); // background in frame 2
// moving the shooting stars
for (var i = 0; i < shootingStarArray.length; i++) {
shootingStarArray[i].move();
shootingStarArray[i].draw();
}
removeShootingStar(); // keeps shooting stars that are in frame
addShootingStar(); // adds shooting stars when mouse is clicked
f2car(); // subway car features in frame 2
astroboy(); // astronaut boy in frame 2
// FRAME 3
f3scene(); // background in frame 3
// TEXT
// words in frame 1
var f1text = "every night i take the subway home..."
// words in frame 2
var f2text = "i wonder..."
// words in frame 3
var f3text = "if our worlds will ever collide again."
noStroke();
fill("white");
textSize(14);
textStyle(ITALIC);
text(f1text, f1x + 10, 279); // frame 1 text
text(f2text, 490, 87); // frame 2 text
text(f3text, f1x + f1width + 25, 279); // frame 3 text
// BORDERS AROUND THE FRAMES
noStroke();
fill(27, 34, 45);
rect(0, 0, width, f1y); // top border
rect(0, 0, f1x, height); // border left of frame 1
rect(f1x + f1width, 0, 17, height); // border right of frame 1
rect(0, 0, f1x + f1width, f1y); // border above frame 1
// border below frame 1
rect(0, f1y + f1height, width, width - f1y - f1height);
// border below frame 2
rect(f1x + f1width, f1y + f2height, width - f1x - f1width, f2spacing);
// border right of frame 2
rect(f1x + f1width + f1spacing + f2width, 0,
width - f1x + f1width + f1spacing + f2width, height);
}
// ---------------- FUNCTIONS & OBJECTS FOR FRAME 1 ------------------
// FRAME 1 BACKGROUND
function f1scene(){
// GRADIENT SKY
noFill();
for (var s = f1y; s < f1y + f1height; s++) {
var skyInter = map(s, f1y, f1y + f1height, 0, 1);
var skyStroke = lerpColor(skyBlue, skyYellow, skyInter);
stroke(skyStroke);
line(f1x, s, f1x + f1width, s);
}
planets(); // planets in the sky
surface(); // surface of the moon
clouds(); // clouds in the sky
constellations(); // constellations in the sky
// TWINKLING STARS
fill(242, 242, 158);
for (var st = 0; st < 8; st++) {
// x positions for the twinkling stars
starX = [32, 77, 129, 177, 190, 205, 242, 276];
// y positions for the twinkling stars
starY = [91, 66, 63, 27, 79, 53, 25, 90];
// sizes for the twinkling stars
starSize = [5, 4, 4, 5, 4, 5, 4, 5];
star(starX[st], starY[st],
starSize[st] - random(1, 3), starSize[st], 5);
}
// SUBWAY TRACK
stroke(79, 94, 94);
fill(130, 142, 134);
// pillars holding the track
for (var i = 0; i < 6; i++) {
rect(34 + i * 48, 148, 14, 67);
}
rect(0, 148, f1x + f1width, 13); // bridge above the water
// GRADIENT WATER
noFill();
for (var w = 212; w < f1y + f1height; w++) {
var waterInter = map(w, 212, f1y + f1height, 0, 1);
var waterStroke = lerpColor(waterYellow, waterBlue, waterInter);
stroke(waterStroke);
line(f1x - 1, w, f1x + f1width, w);
}
}
// CONSTELLATIONS iN FRAME 1
function constellations() {
stroke(131, 178, 177);
strokeWeight(1);
// LIBRA CONSTELLATION
// the following lines join together to create libra
line(98.5, 90.5, 98.5, 84.5);
line(98.5, 84.5, 89.5, 70.5);
line(89.5, 70.5, 111.5, 60.5);
line(111.5, 60.5, 112.5, 74.5);
line(89.5, 70.5, 96.5, 56.5);
line(96.5, 56.5, 111.5, 60.5);
// LEO CONSTELLATION
// the following lines join together to create leo
line(146, 84, 142, 75);
line(142, 75, 154, 79);
line(154, 79, 146, 84);
line(146, 84, 148, 97);
line(148, 97, 156, 95);
line(156, 95, 156, 89);
line(156, 89, 161, 88);
line(161, 88, 172, 95);
line(172, 95, 169, 100);
// AQUARIUS CONSTELLATION
// the following lines join together to create aquarius
line(258, 49, 241, 63);
line(241, 63, 246, 65);
line(246, 65, 254, 63);
line(241, 63, 241, 67);
line(241, 67, 239, 69);
line(239, 69, 247, 83);
line(247, 83, 249.5, 77.5);
line(249.5, 77.5, 258, 76);
line(258, 76, 268, 82);
}
// CLOUDS IN FRAME 1
function clouds() {
noStroke();
// YELLOW CLOUDS
fill(210, 216, 130, 70);
var ycloudx = [92, 67, 103, 117, 122, 159]; // x coordinates
var ycloudy = [92, 88, 53, 56, 52, 70]; // y coordinates
var ycloudw = [56, 48, 27, 35, 19, 25]; // cloud width
var ycloudh = [9, 8, 5, 4, 3.5, 3]; // cloud height
// for loop generates 6 yellow clouds
for (var y = 0; y < 6; y++) {
ellipse(ycloudx[y], ycloudy[y], ycloudw[y], ycloudh[y]);
}
// PINK CLOUDS
fill(242, 136, 158, 150);
var pcloudx = [251, 65, 48, 183, 196, 212]; // x coordinates
var pcloudy = [109, 58, 55, 43, 46, 41]; // y coordinates
var pcloudw = [80, 30, 25, 35, 31, 29]; // cloud width
var pcloudh = [12, 7, 3, 4, 7, 9]; // cloud height
// for loop generates 6 pink clouds
for (var p = 0; p < 6; p++) {
ellipse(pcloudx[p], pcloudy[p], pcloudw[p], pcloudh[p]);
}
}
// SURFACE OF THE MOON IN FRAME 1
function surface() {
strokeWeight(1);
stroke(31, 73, 72);
fill(38, 104, 119);
// x positions of the moon surface's shape
var moonx = [f1x, 61, 107, 152, 202, 248, f1x + f1width,
f1x + f1width, f1x];
// y positions of the moon surface's shape
var moony = [187, 185, 191, 180, 188, 190, 195, 237, 237];
// SURFACE OF THE MOON
beginShape();
vertex(moonx[0], moony[0]);
// for loop for curved parts of the shape
for (var m = 0; m < 7; m++) {
curveVertex(moonx[m], moony[m]);
}
// straight parts of the moon surface shape
// these points can't be seen because they are
// blocked by the water
vertex(moonx[7], moony[7]);
vertex(moonx[8], moony[8]);
vertex(moonx[0], moony[0]);
endShape();
// MOON CRATERS
noStroke();
fill(22, 81, 91);
ellipse(58, 192, 29, 6);
ellipse(96, 202, 19, 3);
ellipse(146, 188, 19, 4);
ellipse(201, 201, 20, 3);
ellipse(241, 200, 34, 5);
}
// PLANETS IN THE SKY OF FRAME 1
function planets () {
// LARGEST PLANET
strokeWeight(2);
stroke(30, 76, 114);
fill(32, 111, 142);
ellipse(217, 164, 142, 142);
// SECOND LARGEST PLANET
strokeWeight(1);
stroke(24, 76, 119);
fill(18, 114, 130, 95);
ellipse(95, 119, 75, 75);
// RINGED PLANET
noStroke();
fill(234, 106, 124);
ellipse(63.5, 45, 51, 12); // rings
strokeWeight(0.5);
stroke(140, 65, 83);
fill(242, 136, 158);
ellipse(64, 44, 25, 25); // planet itself
// CRESCENT MOON
strokeWeight(1);
stroke(234, 96, 119);
fill(242, 242, 158);
ellipse(144.8, 40, 30, 30); // crescent part
noStroke();
fill(31, 94, 104);
ellipse(143, 37, 24, 24); // shadow part
}
// MAKING THE STARS IN FRAME 1 + 3
// called in f1scene() and f3scene()
function star(x, y, radius1, radius2, npoints) {
var angle = TWO_PI / npoints;
var halfAngle = angle / 2.0;
beginShape();
for (let a = 0; a < TWO_PI; a += angle) {
var sx = x + cos(a) * radius2;
var sy = y + sin(a) * radius2;
vertex(sx, sy);
sx = x + cos(a + halfAngle) * radius1;
sy = y + sin(a + halfAngle) * radius1;
vertex(sx, sy);
}
endShape(CLOSE);
}
// ------------- FUNCTIONS & OBJECTS FOR FRAME 2 -------------------
// FRAME 2 BACKGROUND
function f2scene() {
// WINDOW BEHIND THE RIDER
strokeWeight(4);
stroke(255);
fill(9, 74, 89);
rect(302, 29, 291, 85);
// NIGHTTIME SCENERY
// stars that come into frame when the subway is "moving"
noStroke();
fill(242, 242, 158);
for (var i = 0; i < subwayStarArray.length; i++) {
subwayStarArray[i].move();
subwayStarArray[i].draw();
}
removeSubwayStar(); // keeps stars in the keepSubwayStar array
addSubwayStar(); // based on a small probability, adds new stars
// big planet
strokeWeight(2);
stroke(30, 76, 114);
fill(32, 111, 142);
ellipse(planetx, 100, 130, 130);
planetx += 0.05 // planet slowly moves right as time passes
// MORE WINDOW FEATURES
// glass dividers on the subway windows
strokeWeight(3);
stroke(255);
var divideh = 24; // height of vertical dividers
var dividex = 335; // initial x position of vertical dividers
var dividey = 32; // y position of all vertical dividers
var dividespace = 70; // horizontal spacing in between
// 4 vertical dividers are created through lines
for (var d = 0; d < 4; d++) {
line(dividex + dividespace * d, dividey,
dividex + dividespace * d, dividey + divideh);
}
// horizontal line beneath the vertical dividers
line(302, 32 + divideh, 302 + f2width, 32 + divideh);
// window reflections
noStroke();
fill(253, 255, 255, 40);
// first reflection shape
beginShape();
vertex(330, 112);
vertex(326, 95);
vertex(381, 31);
vertex(399, 31);
vertex(330, 112);
endShape();
// second reflection shape
beginShape();
vertex(355, 95);
vertex(409, 31);
vertex(418, 31);
vertex(364, 95);
vertex(355, 95);
endShape();
// third reflection shape
beginShape();
vertex(423, 98);
vertex(472, 30);
vertex(509, 30);
vertex(465, 98);
vertex(423, 98);
endShape();
// fourth reflection shpae
beginShape();
vertex(512, 102);
vertex(555, 31);
vertex(572, 31);
vertex(531, 102);
vertex(512, 102);
endShape();
}
// FEATURES OF THE SUBWAY CAR IN FRONT OF THE WINDOW
function f2car() {
// subway car wall above the window
noStroke();
fill(143, 169, 183);
rect(f1x + f1width + 10, 0, f2width, 29);
// subway car wall below the window
rect(f1x + f1width + 10, 114, f2width, 40);
// subway chairs
// back of the chair that the rider leans against
stroke(73, 122, 121);
strokeWeight(1);
fill(174, 199, 206);
rect(320, 92, 247, 77, 15);
// indents in the chair
noStroke();
fill(153, 181, 188);
var indentx = 333; // initial x positions of indents
var indenty = 103; // y positions of indents
var indentw = 66; // width of indents
var indenth = 65; // height of indents
var indentround = 15; // roundness of indents
var indentspacing = 79; // spacing between each indent
// for loop creates 3 evenly spaced indents in the chair
for (var c = 0; c < 3; c++) {
rect(indentx + indentspacing * c, indenty, indentw,
indenth, indentround);
}
// handles above the window
noFill();
strokeWeight(3);
stroke(200, 214, 219);
var handlespacing = 82; // horizontal spacing between handles
// for loop creates 3 evenly spaced handles
for (var h = 0; h < 3; h++) {
ellipse(360 + handlespacing * h, 20, 10, 40);
}
}
// ASTRONAUT BOY SITTING IN THE SUBWAY
function astroboy() {
// arms
noStroke();
fill(160, 116, 87);
rect(397, 137, 13, 15); // right arm
// shirt
fill(194, 160, 224);
ellipse(379, 118.5, 66, 19); // shoulders
rect(356, 117, 47, 35); // body
rect(346, 119, 15, 19); // left sleeve
ellipse(354, 137.5, 16, 5); // bottom of left sleeve
rect(400, 119, 12, 19); // right sleeve
ellipse(404, 137.5, 16, 5); // bottom of right sleeve
// pocket on shirt
fill(234, 132, 132);
rect(385, 130, 11, 13);
ellipse(390, 143, 11, 3);
// right arm
noStroke();
fill(160, 116, 87);
quad(346, 138, 360, 138, 366, 150, 352, 150); // left arm
// astronaut helmet
// neck piece
noStroke();
fill(100);
ellipse(377, 111, 27, 6);
rect(364, 100, 27, 11);
// helmet
strokeWeight(1);
stroke(184, 209, 208);
fill("white");
ellipse(377, 83, 48, 48);
// glass piece
noStroke();
fill(33, 51, 68);
ellipse(376, 95.5, 34, 11);
quad(357, 85, 395, 85, 393, 96, 359, 96);
ellipse(376, 85, 38, 11);
// highlights on glass piece
fill(100, 107, 109);
ellipse(371, 84, 22, 2); // top highlight
ellipse(376, 98, 18, 1); // bottom highlight
}
// ------------------ FUNCTIONS/OBJECTS IN FRAME 3 ---------------------
// FRAME 3 BACKGROUND
function f3scene() {
// the dark night sky
noStroke();
fill(18, 38, 76);
rect(307, 12 + f2height + f2spacing, f2width, f2height);
// clouds
// darkest clouds
fill(39, 49, 117, 170);
ellipse(382, 193, 69, 48);
ellipse(534, 201, 95, 47);
// purple clouds
fill(118, 86, 153, 120);
ellipse(348, 230, 107, 71);
ellipse(445, 220, 142, 77);
ellipse(531, 225, 98, 52);
// darker pink clouds
fill(219, 127, 154, 130);
ellipse(374, 247, 76, 50);
ellipse(520, 251, 135, 50);
// glow behind the star in figure's hand
noStroke();
var glowx = 491; // x position of the glow center
var glowy = 216; // y position of the glow center
// controls how the glowing portion of the star
// grows and shrinks, so it looks like it's flickering
var osc = 10 + sin(millis()) * 1;
// outer glow
fill(239, 125, 172, 100);
ellipse(glowx, glowy, 20 + osc, 20 + osc);
// middle glow
fill(249, 155, 189, 120);
ellipse(glowx, glowy, 10 + osc, 10 + osc);
// inner glow
fill(237, 102, 138);
ellipse(glowx, glowy, 4 + osc, 4 + osc);
starfigure(); // mysterious figure in the clouds
// light pink clouds
noStroke();
fill(242, 165, 165, 200);
ellipse(337, 273, 76, 50);
ellipse(535, 276, 94, 50);
fill(242, 165, 165); // cloud covering the figure is opaque
ellipse(430, 278, 135, 50);
// twinkling stars
fill(242, 242, 158);
for (var st = 0; st < 5; st++) {
// x positions for the twinkling stars
starX = [366, 413, 491, 493, 514];
// y positions for the twinkling stars
starY = [240, 213, 216, 245, 178];
// sizes for the twinkling stars
starSize = [8, 9, 6, 8, 6];
star(starX[st], starY[st],
starSize[st] - random(4, 5), starSize[st], 5);
}
}
function starfigure() {
// body
fill(224, 184, 140);
ellipse(466, 195.5, 2, 5); // right ear
ellipse(443, 194.5, 2, 5); // left ear
rect(450, 203, 8, 9); // neck
quad(470, 235, 485, 219, 490, 220, 475, 240); // arm
ellipse(488.5, 219.5, 7, 3); // palm
//forefinger
beginShape();
vertex(491, 220);
vertex(491, 218);
vertex(494.2, 217.6);
vertex(494.5, 218.5);
vertex(492, 220);
vertex(491, 220);
endShape();
ellipse(494.5, 216.5, 1, 4);// upper finger
//hair
fill(248, 252, 195);
ellipse(454.5, 194, 23, 24);
hairx = [443, 466, 464, 463, 462, 452, 450, 449, 445, 445,
444, 441, 443, 443]; // x positions of hair coordinates
hairy = [194, 194, 208, 205, 208, 208, 206, 208, 207, 205,
207, 207, 199, 194]; // y positions of hair coordinates
beginShape();
for (var h = 0; h < 14; h++) {
vertex(hairx[h], hairy[h]);
}
endShape();
// cape
fill(30, 30, 124);
ellipse(454, 217, 35, 13);
quad(426, 266, 437, 218, 472, 218, 482, 266);
// folds in the cape
fill(16, 16, 94);
triangle(438, 259, 443, 221, 443, 259);
triangle(467, 260, 465, 222, 472, 260);
// constellations of the cape
strokeWeight(1);
stroke(113, 108, 183);
// first constellation
line(443, 216, 448, 224);
line(448, 224, 452, 225);
line(452, 225, 445, 235);
// second constellation
line(455, 236, 453, 244);
line(453, 244, 459, 246);
line(459, 246, 461, 242);
line(461, 242, 465, 250);
// constellation on the edge of the cape
line(432, 243, 434, 245);
line(434, 245, 429, 251);
}
// ------------- FUNCTIONS & OBJECTS FOR THE MOVING SUBWAY IN FRAME 1 -------------
// PRESSING KEYS TO CONTROL SUBWAY CAR SPEED
function keyPressed() {
// everytime the "f" key is pressed, the subway car's
// speed will increase by 1
if (key == "f") {
accelerate += 1;
}
// likewise, everytime "s" is pressed, the subway car's
// speed will reduce by 1
if (key == "s") {
accelerate -= 1;
}
// upon pressing the r key, it resets the accelerate to 0
// helpful in case you make the train way too fast and don't
// want to click s multiple times to get back to the original speed
if (key === "r") {
accelerate = 0;
}
}
// SUBWAY OBJECT
function makeSubway() {
var subwaycar = {x: -200,
y: 119,
speed: 4,
move: moveSubway,
draw: drawSubway}
return subwaycar;
}
function drawSubway() {
// subway cars
var sublength = 75; // length of subway car
var subheight = 28; // height of subway car
var subx = -200; // x position of subway car
var suby = 119; // y position of subway car
var round = 7.5; // rounded edges of rect
// subway car windows
var windowY = 125; // y position of subway window
var windowW = sublength / 4 // length of subway window
var windowH = 10; // height of subway window
var wspace = 4.5; // spacing between subway windows
// creating the subway car
for (var s = 0; s < 5; s++) {
stroke(125, 121, 132);
fill(230, 230, 252);
rect(this.x + sublength * s, suby, sublength, subheight, round);
for (var i = 0; i < 3; i++) {
fill(21, 68, 76);
rect(this.x + wspace * (i + 1) + windowW * i + sublength * s,
windowY, windowW, windowH);
}
}
}
function moveSubway() {
this.x += (this.speed + accelerate);
// to prevent the subway car from moving backwards,
// if accelerate becomes too big that its absolute value
// is greater than the speed, it resets to 0, which means
// the subway train will pause right before and upon pressing
// the s key again, it will reset to the original speed
if (accelerate < this.speed * -1) {
accelerate = 0;
}
// reset the x position of the last subway car to -500
// if the last subway car exits the frame
if (this.x > f1x + f1width) {
this.x = -500;
}
}
// ----------- FUNCTIONS & OBJECTS FOR FALLING STARS IN FRAME 1 & 2 ----------
// FALLING STARS IN FRAME 1 & 2 WHEN MOUSE IS PRESSED
function mousePressed() {
// when mouse is pressed, the function will
// to add a shooting star will be called with
// the x and y inputs of the mouse position
if (mouseX < width && mouseX > f1x &&
mouseY < f1y + f1height && mouseY > f1y) {
addShootingStar(mouseX, mouseY);
}
}
// SHOOTING STAR OBJECT
function makeShootingStar(xPos, yPos) {
var shootingStar = {x: xPos,
y: 0,
size: random(7, 20),
speed: random(3, 8),
move: moveShootingStar,
draw: drawShootingStar}
return shootingStar;
}
// DRAWING THE SHOOTING STAR
function drawShootingStar() {
// controls how the glowing portion of the star
// grows and shrinks, so it looks like it's flickering
var osc = 10 + sin(millis()) * 1;
noStroke();
// glowing part of the star
fill(244, 242, 168, 75);
ellipse(this.x, this.y, this.size + osc, this.size + osc);
// inner part of the star
fill(239, 235, 91);
ellipse(this.x, this.y, this.size, this.size);
}
// MOVING THE SHOOTING STAR
function moveShootingStar() {
this.y += this.speed;
}
// ADDING SHOOTING STARS
function addShootingStar(xPos, yPos) {
shootingStarArray.push(makeShootingStar(xPos, yPos));
}
// REMOVING SHOOTING STARS
function removeShootingStar() {
// array for keeping the stars
var keepStar = [];
// as long as the shooting stars are in bound of frame 1
// they will be pushed into the array for keeping the stars
for (var i = 0; i < shootingStarArray.length; i++) {
if (shootingStarArray[i].y < f1y + f1height +
shootingStarArray[i].size) {
keepStar.push(shootingStarArray[i]);
}
}
shootingStarArray = keepStar;
}
// ------------- FUNCTIONS & OBJECTS FOR THE RIPPLES IN FRAME 1 --------------
// RIPPLE OBJECT
function makeRipples(xPos, yPos) {
var makeRipple = {x: xPos,
y: yPos,
// longer ripples are in the front, shorter ones in the back
length: map(yPos, 212, height, 5, 75),
// thinner ripples in the back, thicker ones in the front
weight: map(yPos, 212, height, 1, 4),
// faster ripples in the front, slower ripples in the back
speed: map(yPos, 212, height, 0.5, 1),
move: moveRipple,
draw: drawRipple}
return makeRipple;
}
// MOVING THE RIPPLE
function moveRipple() {
// x position changes by speed
this.x += this.speed;
// if the ripple leaves the frame, reset x position
// to the left side of the frame
if (this.x > width + this.length) {
this.x === -this.length;
}
}
// ADDING RIPPLES
// using a tiny probability, add ripples
function addRipple() {
if (random(0, 1) < 0.025) {
ripples.push(makeRipples(-75, random(212, height)));
}
}
// REMOVING RIPPLES
function removeRipple() {
// an array for ripples to keep
var keepRipples = [];
// as long as ripples are within the bounds of frame 1,
// keep them in the keepRipples array
for (var i = 0; i < ripples.length; i++) {
if (ripples[i].x < f1x + f1width) {
keepRipples.push(ripples[i]);
}
}
ripples = keepRipples;
}
// DRAWING THE RIPPLE
function drawRipple() {
strokeWeight(this.weight);
stroke(255, 255, 255, 75);
line(this.x, this.y, this.x + this.length, this.y);
}
// ------------- FUNCTIONS & OBJECTS FOR THE STARS IN FRAME 2 -------------
// SUBWAY STAR OBJECT
function makeStar(xPos, yPos) {
var subwayStar = {x: xPos,
y: yPos,
radius1: random(5, 10),
npoints: 5,
speed: random(0.5, 2),
move: moveSubwayStar,
draw: drawSubwayStar}
return subwayStar;
}
// MOVING THE SUBWAY STARS
function moveSubwayStar() {
// x position changes by speed
this.x += this.speed;
// if the star leaves the frame, reset x position
// to the left side of the frame
if (this.x > width) {
this.x === f1x + f1width + 10;
}
}
// ADDING SUBWAY STARS
// using a tiny probability, add subway stars
function addSubwayStar() {
if (random(0, 1) < 0.02) {
subwayStarArray.push(makeStar(f1x + f1width + 10,
random(30, 100)));
}
}
// REMOVING SUBWAY STARS
function removeSubwayStar() {
// an array for subway stars to keep
var keepSubwayStar = [];
// as long as the stars are within the width,
// keep them in a separate array
for (var i = 0; i < subwayStarArray.length; i++) {
if (subwayStarArray[i].x < width) {
keepSubwayStar.push(subwayStarArray[i]);
}
}
subwayStar = keepSubwayStar;
}
// DRAWING THE SUBWAY STARS
function drawSubwayStar() {
fill(242, 242, 158);
var angle = TWO_PI / this.npoints;
var halfAngle = angle / 2.0;
beginShape();
for (let a = 0; a < TWO_PI; a += angle) {
var sx = this.x + cos(a) * (this.radius1 - 5);
var sy = this.y + sin(a) * (this.radius1 - 5);
vertex(sx, sy);
sx = this.x + cos(a + halfAngle) * this.radius1;
sy = this.y + sin(a + halfAngle) * this.radius1;
vertex(sx, sy);
}
endShape(CLOSE);
}
For my final project, I created a speculative cartoon panel. It’s speculative in the sense that the it forces the viewer to use their imagination to weave a story of what might have happened–the possibilities are endless. In this project, I enjoyed exploring my interests in illustration, concept art, and storytelling. To convey this dreamy, mysterious, magical atmosphere, I worked within a range of cool/teal tones, with pink and yellow highlights.
To begin, I created a moodboard of images/animations on Pinterest. Then, I sketched out/digitized my idea using simple shapes on Illustrator. Not only did I enjoy creating the static images, I think programming the interactions made the comic scene much more fun and engaging.