Virus Run
For my final project, I have created a side-scrolling game in which the player takes on the role of a coronavirus particle. As obstacles such as masks and hand sanitizer fly from right to left, the player must use the space bar to make the virus “jump” and avoid these obstacles. The longer the user holds down the key, the higher the virus floats, until it reaches the max height. If any of the obstacles are touched, the game stops. The score is then calculated based on the frame count. I was inspired by the dinosaur game which people can play when trying to use google with no internet connection, and if I had more time, I would have liked to make it so that the longer the game goes on, the more obstacles there are and the faster they move.
Some Screenshots of what the full canvas looks like:
var imgV; // image of virus
var imgM; // image of mask obstacle
var imgHS; // image of hand sanitizer
var imgStart; // image for starting screen
var imgGO; // image for game over
var mask = []; // array for masks on ground level
var mx = 800 // x coord of mask
var handSan = []; // array for hand sanitizer flying overhead
var hsx = 800 // x coord of hand sanitizer
var virusX = 100; // permanent x position of virus
var virusY = 270; // starting y position of virus
var virusR = 70; // radius of virus
var virusDy = 0; // change in height of virus when jumping
var score = 0; //counts score based on frames
var start = true; // checks to see if starting page is showing
var hills = []; // making moving hills as a background element
var noiseParam = 0;
var noiseStep = 0.02;
function preload(){
imgV = loadImage("https://i.imgur.com/k04oKtW.png");
imgM = loadImage("https://i.imgur.com/v2CRQLt.png");
imgHS = loadImage("https://i.imgur.com/iFAELeQ.png");
imgGO = loadImage("https://i.imgur.com/ORHbmPV.png");
imgStart = loadImage("https://i.imgur.com/OUL56Za.png");
}
function setup(){
createCanvas(800, 400);
//making hills
for(var i = 0; i <= width/5; i++){
var n = noise(noiseParam);
hills[i];
var value = map(n, 0, 1, 0, height/2);
hills.push(value);
noiseParam += 0.5*noiseStep;
}
}
function draw(){
if (start == true){
startingPage();
} else {
background(8, 6, 51); // dark blue background
drawHills();
fill(55, 0, 104); // purple floor
rect(0, 370, 800, 30);
image(imgV, virusX, virusY, 107, 100); // image of virus
if(keyIsDown(32)){
virusY -= 10;
// while the space bar is down, the virus moves upward
} else if (virusY < 270){
virusY +=10;
// when the space bar is let go while the virus is in the air,
//the virus moves down
} else { virusY = 270; }
// otherwise, the virus stays on the ground
if (virusY < 40)
{ virusY += 10}
createNewMask();
displayMask();
createNewHandSan();
displayHandSan();
checkIfGameOver();
score += 1;
//display score count in the top right corner during the game
textSize(15);
fill(190, 215, 62);
text(score, 745, 40);
}
}
function startingPage(){ // starting page with instructions
push();
fill(8, 6, 51);
rect(-1, -1, 801, 801);
fill(190, 215, 62);
textSize(25);
text('Press any key to begin >', 487, 365);
textSize(15);
text('Use the space bar to avoid obstacles!', 490, 230);
pop();
image(imgStart, 40, 80, 700, 100);
image(imgM, 540, 250, 60, 58);
image(imgHS, 620, 247, 62, 70);
image(imgV, virusX, virusY, 107, 100);
}
function keyPressed(){
if(start == true){ start = false; } // game begins
}
//drawing hills (background element)
function drawHills(){
push();
translate(0, 130)
hills.shift();
var n = noise(noiseParam);
var value = map(n, 0, 1, 0, height/2);
hills.push(value);
// draws shape for sand
beginShape();
vertex(0, height);
for(var i = 0; i <= width/5; i++){
//filling the hill color
fill(255, 255, 255, 10);
noStroke();
//vertex function to fill the hills
vertex((i * 5), hills[i]);
vertex((i + 1) * 5, hills[i + 1]);
}
vertex(width, height);
endShape(CLOSE);
pop();
}
function checkIfGameOver (){
for (i = 0;i < mask.length;i++)
{
if ((dist(mask[i].x, 400, virusX, 400) <= virusR)
// tests if the mask comes within a certain distance of the virus
& virusY == 270){
gameOver(); // the screen goes darker
noLoop(); // the game stops
}
}
for (i = 0;i < handSan.length;i++){
if ((dist(handSan[i].x, handSan[i].y, virusX, virusY) <= virusR)){
//tests if the hand sanitizer comes within a certain distance of the virus
gameOver(); // the screen goes darker
noLoop(); // the game stops
}
}
}
// BEGIN CODE FOR MASK OBSTACLES
function displayMask(){ // update mask positions and display them
for(var i = 0; i < mask.length; i++){
mask[i].move();
mask[i].draw();
}
}
function createNewMask(){
// makes new masks with some random probability
if(random(0, 1) < 0.008){
mask.push(makeMask(800));
}
}
function moveMask(){
this.x -= 10;
}
function drawMask(){
mask.push();
image(imgM, this.x, this.y, 60, 60);
}
// function to make mask obstacle object
function makeMask(){
var m = {x: mx,
y: 310,
move: moveMask,
draw: drawMask
}
return m;
}
// BEGIN CODE FOR HAND SANITIZER OBSTACLES
function displayHandSan(){ // update hand sanitizer positions and display them
for(var i = 0; i < handSan.length; i++){
handSan[i].move();
handSan[i].draw();
}
}
function createNewHandSan(){
// makes new masks with some random probability
if(random(0, 1) < 0.005){
handSan.push(makeHandSan(800));
}
}
function moveHandSan(){
this.x -= 10;
}
function drawHandSan(){
handSan.push();
image(imgHS, this.x, this.y, 80, 90);
}
// function to make mask obstacle object
function makeHandSan(){
var h = {x: hsx,
y: random(30, 100),
move: moveHandSan,
draw: drawHandSan
}
return h;
}
function gameOver(){
// screen displayed when the virus hits an obstacle
push();
fill(0, 0, 0, 80);
rect(0, 0, 800, 400);
pop();
image(imgGO, 50, 100, 700, 100);
push();
fill(190, 215, 62);
noStroke();
textSize(20);
text('SCORE:' + ' ' + score, 340, 250);
textSize(15);
text('Refresh to play again!', 340, 280)
pop();
}
Emily Gobeille is an accomplished artist and designer interested in the way that children learn and play. A co-founder of Design I/O, she works to create immersive, interactive installations and experiences for museums, galleries, and more. She also plays a significant role in developing video games like The Pack, a strategy and logic-based game that aims to give players experience with computational thinking concepts. Within the game, players must explore a fantasy world, befriending creatures that act as algorithms to change the world around them. The game’s visual design makes it accessible to younger and more diverse audiences, helping a wider range of players grasp complex concepts. Gobeille’s intrigue with children’s education is reflected in her own child-like creativity that shines through in the vibrance of her work.
For my generative landscape, I chose to make a desert with different types of cacti whose sizes and colors are randomly generated. For the sand and hills in the back, I referenced the lab from week 7. While the sand keeps moving, the hills in the back are still but change every time the page is refreshed.
var cacti = [];
var canyons = [];
var sand = [];
var ball = [];
var noiseParam = 0;
var noiseStep = 0.02;
function setup() {
createCanvas(640, 240);
background(0);
/*// create an initial collection of buildings
for (var i = 0; i < 10; i++){
var rx = random(width);
buildings[i] = makeBuilding(rx);
}*/
frameRate(20);
//canyon values
for(var i = 0; i <= width/5; i++){
var n = noise(noiseParam);
canyons[i];
var value = map(n, 0, 1, 0, height);
canyons.push(value);
noiseParam += noiseStep;
}
//sand values
for(var i = 0; i <= width/5; i++){
var n = noise(noiseParam);
sand[i];
var value = map(n, 0, 1, 0, height/2);
sand.push(value);
noiseParam += 0.5*noiseStep;
}
}
function draw() {
//sunset gradient
var gradRed = color(190, 60, 38); // darker red color
var gradYellow = color(250, 185, 21); // yellow
for (var i = 0; i < height; i++){
var gradient = map (i, 0, (height/3)*1.5, 0, 1);
var bgColor = lerpColor(gradRed, gradYellow, gradient);
stroke(bgColor);
line(0, i, width, i);
}
// draw a setting sun
push();
noStroke();
fill(255, 226, 161);
circle(500, 110, 180);
pop();
drawCanyons();
drawSand();
// taller cacti
updateAndDisplayCacti();
removeCactiThatHaveSlippedOutOfView();
addNewCactiWithSomeRandomProbability();
updateAndDisplayBalls();
removeBallsThatHaveSlippedOutOfView();
addNewBallsWithSomeRandomProbability();
}
//draws background canyons (still)
function drawCanyons(){
// canyon values
var n = noise(noiseParam);
var value = map(n, 0, 1, 0, height);
noiseParam += noiseStep;
// draws shape for canyons
beginShape();
vertex(0, height);
for(var i = 0; i <= width/5; i++){
//filling the canyons with translucent brown
fill(178, 108, 55, 180);
noStroke();
//vertex function to fill the canyon
vertex((i * 5), canyons[i]);
vertex((i + 1) * 5, canyons[i + 1]);
}
vertex(width, height);
endShape(CLOSE);
}
// draws sand (moving)
function drawSand(){
push();
translate(0, 130)
sand.shift();
var n = noise(noiseParam);
var value = map(n, 0, 1, 0, height/2);
sand.push(value);
// draws shape for sand
beginShape();
vertex(0, height);
for(var i = 0; i <= width/5; i++){
//filling the sand color
fill(237, 197, 154);
noStroke();
//vertex function to fill the sand
vertex((i * 5), sand[i]);
vertex((i + 1) * 5, sand[i + 1]);
}
vertex(width, height);
endShape(CLOSE);
pop();
}
function updateAndDisplayCacti(){
// Update the building's positions, and display them.
for (var i = 0; i < cacti.length; i++){
cacti[i].move();
cacti[i].display();
}
}
function removeCactiThatHaveSlippedOutOfView(){
var cactiToKeep = [];
for (var i = 0; i < cacti.length; i++){
if (cacti[i].x + cacti[i].breadth > 0) {
cactiToKeep.push(cacti[i]);
}
}
cacti = cactiToKeep; // remember the surviving buildings
}
function addNewCactiWithSomeRandomProbability() {
// With a very tiny probability, add a new building to the end.
var newCactusLikelihood = 0.02;
if (random(0,1) < newCactusLikelihood) {
cacti.push(makeCactus(width));
}
}
// method to update position of cactus every frame
function cactusMove() {
this.x += this.speed;
}
//draw the cactus
function cactusDisplay() {
cacti.push();
strokeWeight(25);
stroke(this.color);
//main stalk
var y = 220;
line(this.x, this.cBottom, this.x, this.cBottom-this.cHeight)
strokeWeight(17);
stroke(204, 209, 135, 60);
line(this.x, this.cBottom, this.x, this.cBottom-this.cHeight)
// two stalks on side
strokeWeight(13);
stroke(this.color);
line(this.x-20, this.cBottom-this.s1Bottom, this.x-20, this.cBottom-this.s1Height);
// left stalk has a random height
line(this.x+20, this.cBottom-this.s2Bottom, this.x+20, this.cBottom-this.s2Height);
// right stalk has a random height
}
// makes taller cacti
function makeCactus(birthLocationX) {
var ccts = {x: birthLocationX,
breadth: 50,
speed: -4.5,
cHeight: random(30, 60), // height of cactus
cBottom: random(200, 225), // bottom of cactus
s1Height: random(20, 60), // top of the left stalk
s1Bottom: random(5, 20), // bottom of left stalk
s2Height: random(20, 60), // top of right stalk
s2Bottom: random(5, 20), // bottom of right stalk
color: color(random(40, 100), random(80, 200), 72), // varies shades of green
move: cactusMove,
display: cactusDisplay}
return ccts;
}
function addNewBallsWithSomeRandomProbability() {
// With a very tiny probability, add a new building to the end.
var newBallLikelihood = 0.02;
if (random(0,1) < newBallLikelihood) {
ball.push(makeBall(width));
}
}
function updateAndDisplayBalls(){
// Update the building's positions, and display them.
for (var i = 0; i < ball.length; i++){
ball[i].move();
ball[i].display();
}
}
function removeBallsThatHaveSlippedOutOfView(){
var ballsToKeep = [];
for (var i = 0; i < ball.length; i++){
if (ball[i].x + ball[i].breadth > 0) {
ballsToKeep.push(ball[i]);
}
}
ball = ballsToKeep; // remember the surviving buildings
}
function addNewBallWithSomeRandomProbability() {
// With a very tiny probability, add a new ball cactus to the end.
var newBallLikelihood = 0.02;
if (random(0,1) < newBallLikelihood) {
ball.push(makeBall(width));
}
}
// method to update position of ball shaped cactus every frame
function ballMove() {
this.x += this.speed;
}
// displays ball-shaped cacti
function ballDisplay(){
push();
noStroke();
fill(this.color);
ellipse(this.x, this.ey, this.ew, this.eh); // main ball cactus
fill(204, 209, 135, 60)
ellipse(this.x, this.ey, this.ew-5, this.eh-5); // highlight
fill(this.color);
ellipse(this.x, this.ey, this.ew-10, this.eh-10);
fill(251, 203, 188);
stroke(this.color);
strokeWeight(1);
ellipse(this.x, this.ey-(this.eh/2), 8, 5) // lil flower guy on top
pop();
}
// makes ball-shaped cacti
function makeBall(birthLocationX) {
var b = {x: birthLocationX,
breadth: 50,
speed: -4.5,
eh: random(20, 40), // height of ellipse
ey: random(200, 225), // y coord of ellipse
ew: random(20, 40), // width of ellipse
color: color(random(50, 150), 100, random(100, 200)), // varies shades of blue
move: ballMove,
display: ballDisplay}
return b;
}
“EMMY“
Composer: David Cope
“EMMY” is a program developed by David Cope, a classical composer with a deep interest in the role which computers and AI can play in music. His experiments in music intelligence allowed him to create EMMY, a program which can be fed examples of work from specific musicians and composers and in turn produce new pieces based on the characteristics of the music it studies. I find it fascinating that EMMY is able to break down and learn aspects of what makes a specific composer’s work unique and recreate it in a way that is not necessarily copying, but familiar and new. Moreover, Cope theorized that “what made a composer properly understandable, properly “affecting”, was in part the fact of mortality.” Interestingly, this led him to unplug EMMY in 2004, after it had produced over 11,000 pieces.
For my sonic story, I made an animation of a series of animals chasing each other. I first started with a gif of a dog running, then split it into several frames to animate. I then decided to make it get chased by a bee (also a gif), then have the dog chase a cat who chases a mouse. The full frame is too long to display here, but this is a screenshot of what it looks like when all four characters are in frame:
// STORYLINE: A bee chasing a dog chasing a cat chasing a mouse.
var dogWalk = []; // an array to store the images of a dog walking
var beeFly = []; // array to store images of bee flying
var catRun = []; // images of cat running
var mouse;
var mx = 920; // x position of mouse
var fence;
var barking;
var buzz;
var meow;
var squeak;
function preload(){
// images of dog walking
var dogs = [];
dogs[0] = "https://i.imgur.com/Obg7kQd.gif";
dogs[1] = "https://i.imgur.com/vUkgpet.gif";
dogs[2] = "https://i.imgur.com/jvjCknz.gif";
dogs[3] = "https://i.imgur.com/yFxMHG1.gif";
dogs[4] = "https://i.imgur.com/oPxOfcq.gif";
dogs[5] = "https://i.imgur.com/i0WRXiO.gif";
dogs[6] = "https://i.imgur.com/338YDhl.gif";
dogs[7] = "https://i.imgur.com/W62WgdJ.gif";
dogs[8] = "https://i.imgur.com/2GhHX5W.gif";
dogs[9] = "https://i.imgur.com/VgfCha4.gif";
dogs[10] = "https://i.imgur.com/MbUowkT.gif";
dogs[11] = "https://i.imgur.com/CnQCi0T.gif";
dogs[12] = "https://i.imgur.com/rWgGUWm.gif";
dogs[13] = "https://i.imgur.com/WPaT5V0.gif";
dogs[14] = "https://i.imgur.com/y8qx8WE.gif";
dogs[15] = "https://i.imgur.com/Vp1xVPc.gif";
for (var i = 0; i < dogs.length; i++) {
dogWalk[i] = loadImage(dogs[i]);
}
//images of bee flying
var bees = [];
bees[0] = "https://i.imgur.com/4850hmR.gif";
bees[1] = "https://i.imgur.com/0FMGyos.gif";
bees[2] = "https://i.imgur.com/7CozqZ5.gif";
bees[3] = "https://i.imgur.com/J6e0HBK.gif";
bees[4] = "https://i.imgur.com/uexlFJE.gif";
bees[5] = "https://i.imgur.com/intd8Rx.gif";
bees[6] = "https://i.imgur.com/l3ZVHzT.gif";
bees[7] = "https://i.imgur.com/NNG3lnb.gif";
bees[8] = "https://i.imgur.com/wYl8pHt.gif";
bees[9] = "https://i.imgur.com/CM53Kch.gif";
bees[10] = "https://i.imgur.com/Tt90vYh.gif";
bees[11] = "https://i.imgur.com/MTQRrrG.gif";
bees[12] = "https://i.imgur.com/8S7d588.gif";
bees[13] = "https://i.imgur.com/tO9HTc5.gif";
bees[14] = "https://i.imgur.com/KtbttF1.gif";
for (var i = 0; i < bees.length; i++) {
beeFly[i] = loadImage(bees[i]);
}
//images of the cat
var cats = [];
cats[0] = "https://i.imgur.com/ZWzbhxI.gif";
cats[1] = "https://i.imgur.com/GaD7QU7.gif";
cats[2] = "https://i.imgur.com/i6pEZq8.gif";
cats[3] = "https://i.imgur.com/ljdWpB9.gif";
cats[4] = "https://i.imgur.com/3dpfves.gif";
cats[5] = "https://i.imgur.com/r0rgZRT.gif";
cats[6] = "https://i.imgur.com/810gnOd.gif";
cats[7] = "https://i.imgur.com/36qZ7mv.gif";
cats[8] = "https://i.imgur.com/DPO7GIs.gif";
cats[9] = "https://i.imgur.com/r9pxkAW.gif";
cats[10] = "https://i.imgur.com/BNjV3y7.gif";
cats[11] = "https://i.imgur.com/GOJmcoi.gif";
for (var i = 0; i < cats.length; i++) {
catRun[i] = loadImage(cats[i]);
}
mouse = loadImage("https://i.imgur.com/293UbtW.png");
fence = loadImage ("https://i.imgur.com/N9jB1Hh.png");
barking = loadSound ("https://courses.ideate.cmu.edu/15-104/f2020/wp-content/uploads/2020/11/barking.wav");
buzz = loadSound("https://courses.ideate.cmu.edu/15-104/f2020/wp-content/uploads/2020/11/buzz.wav");
meow = loadSound("https://courses.ideate.cmu.edu/15-104/f2020/wp-content/uploads/2020/11/meow-2.wav");
squeak = loadSound("https://courses.ideate.cmu.edu/15-104/f2020/wp-content/uploads/2020/11/squeak.wav");
}
function soundSetup(){
//barking.setVolume(0.75);
buzz.setVolume(15);
meow.setVolume(0.4);
//squeak.setVolume(1);
}
//updates walking dog for next frame
function stepDog(){
this.imageNumber++;
//cycles through the 16 images
if (this.imageNumber>15){
this.imageNumber=0;
}
}
//updates flying bee for next frame
function stepBee(){
this.imageNumber++;
// cycles through 15 images
if (this.imageNumber>14){
this.imageNumber = 0;
}
}
//updates cat for next frame
function stepCat(){
this.imageNumber++;
// cycles through 12 images
if (this.imageNumber>11){
this.imageNumber = 0;
}
}
//draws walking dog
function drawDog(){ image(dogWalk[this.imageNumber], this.x, this.y, 200, 110); }
// draws flying bee
function drawBee() {
push();
scale(-1, 1);
image(beeFly[this.imageNumber], this.x, this.y, 50, 50)
pop();
}
// draws cat
function drawCat() {
push();
scale(-1, 1);
image(catRun[this.imageNumber], this.x, this.y, 80, 80)
pop();
}
//constructor that creates and returns an object representing a dog
function makeDog(cx, cy){
c = {x: cx, y: cy,
dx: 8,
imageNumber:0,
stepFunction: stepDog,
drawFunction: drawDog}
return c;
}
//constructor that creates and returns an object for a bee
function makeBee(bx, by){
b = {x: bx, y: by,
dx: 7,
imageNumber: 0,
stepFunction: stepBee,
drawFunction: drawBee}
return b;
}
//constructor that creates and returns an object for a cat
function makeCat(ax, ay){
a = {x: ax, y: ay,
dx: 9,
imageNumber: 0,
stepFunction: stepCat,
drawFunction: drawCat}
return a;
}
//empty array for dog
var dog=[];
//empty array for bee
var bee = [];
//empty array for cat
var cat = [];
function setup(){
createCanvas(800, 100);
imageMode(CENTER);
for (var i = 0; i < 1; i++) {
// make a dog
var c = makeDog(1100, i+60);
// push the dog onto dog array
dog.push(c);
var b = makeBee(-1200, i+50);
bee.push(b);
var a = makeCat(-1000, i+70);
cat.push(a);
}
useSound();
frameRate(8);
}
function draw(){
background(201, 74, 88);
//barking sound
if (frameCount == 65 || frameCount == 75) {
barking.play();
}
// buzzing sound
if (frameCount == 80 || frameCount == 90){
buzz.play();
}
// meow
if (frameCount == 40 || frameCount == 50){
meow.play();
}
//squeak
if (frameCount == 20 || frameCount == 30){
squeak.play();
}
if (0 < frameCount < 100){
for (var i = 0; i < 1; i++) { // for the dog
var c = dog[i];
c.stepFunction();
c.drawFunction();
//moves dog to the left
dog[i].x-=dog[i].dx;
}
}
if (0 < frameCount < 100){
for (var i = 0; i < 1; i++) { // for the bee
var b = bee[i];
b.stepFunction();
b.drawFunction();
//moves bee to the left
bee[i].x+=bee[i].dx;
}
}
if (0 < frameCount < 100){
for (var i = 0; i < 1; i++) { // for the cat
var a = cat[i];
a.stepFunction();
a.drawFunction();
//moves cat to the left
cat[i].x += cat[i].dx;
}
}
image(mouse, mx, 75, 45, 30);
image(fence, 700, 100, 200, 70);
mx -= 10
}
function mousePressed (){
frameCount = 0;
}
Stephanie Dinkins is an interdisciplinary artist whose work focuses on the intersection of artificial intelligence and race, gender, and age, and how technology shapes history. She teaches at Stony Brook University, and was named an “AI Influencer” in the New York Times. Her work consists primarily of interactive installations that use AI algorithms to communicate issues of social justice and her own familial experiences while trying to make AI technology more accessible to underfunded communities. Specifically, her project “Not the Only One” centers around her multigenerational family history, helping to tell the story of her ancestors as a unique, deep-learning entity. I admire the way in which her work humanizes the world of AI, demonstrating how it can intersect with deeply personal topics. She also emphasizes the importance of using programming in a way that brings in audiences who might not otherwise be immersed in or have access to this technological setting, as well as people who might not recognize how it can relate to themselves.
var x;
var y;
var spread; // how far each bean is from one another
var a; // scales the size of individual beans
function setup() {
createCanvas(480, 480);
}
function draw() {
background(0, 30, 50);
translate(width/2, height/2);
push();
for(var i = 0; i <= 24; i ++){
beanWO();
rotate(radians(15));
}
pop();
// arrangement of 24 beanWO functions
push();
for(var j = 0; j <= 12; j++){
beanF();
rotate(radians(30));
}
pop();
// arrangement of 12 beanF functions
push();
for(var k = 0; k <= 36; k++){
beanScrib();
rotate(radians(mouseX));
}
pop();
// arrangement of 36 scribble-like shapes that rotate based on the X position of the mouse
push();
for(var m = 0; m <= 3; m ++){
beanVar();
rotate(radians(120));
}
pop();
// draws function beanVar in the middle of the canvas
}
function beanWO(){
// draws beans with white outlines that move toward and away from the origin based on mouseX
push();
a = 100
spread = mouseX/5;
stroke(255);
noFill();
beginShape();
for(var theta = 0; theta <= TWO_PI; theta += radians(1)){
x = a * cos(theta) * (pow(sin(theta), 3) + pow(cos(theta), 3)) + spread; //
y = a * sin(theta) * (pow(sin(theta), 3) + pow(cos(theta), 3)) + spread; // y parameter of bean curve
vertex(x, y)
}
endShape();
pop();
}
function beanF(){
// draws translucent beans whose colors change with mouseY
push();
a = 200;
spread = -mouseX/5;
var r = map(mouseY, 0, 480, 0, 255);
noStroke();
fill(r, 255, 231, 40);
beginShape();
for(var theta = 0; theta <= TWO_PI; theta += radians(1)){
x = a * cos(theta) * (pow(sin(theta), 3) + pow(cos(theta), 3)) + spread; // x paremeter of bean curve
y = a * sin(theta) * (pow(sin(theta), 3) + pow(cos(theta), 3)) + spread; // y parameter of bean curve
vertex(x, y)
}
endShape();
pop();
}
function beanScrib(){
// draws a variation of the bean function that looks like a scribble
push();
a = mouseY;
spread = mouseY/12
noFill();
stroke(214, 197, 255, 40);
strokeWeight(3);
beginShape();
for(var theta = 0; theta <= radians(180); theta += radians(1)){
x = a * cos(theta * .5) * (pow(sin(theta * .6), 3) + pow(cos(theta), 3)) + spread;
y = a * sin(theta * .5) * (pow(sin(theta * .6), 3) + pow(cos(theta), 3)) + spread;
vertex(x, y)
}
endShape();
pop();
}
function beanVar(){
// draws a variation of the bean function that changes with mouseY
push();
a = 50
var b = map(mouseY, 0, height, 0, 5) // modifies an angle in the function with mouseY
noFill();
stroke(146, 255, 205);
strokeWeight(5);
beginShape();
for(var theta = 0; theta <= radians(180); theta += radians(1)){
x = a * cos(theta) * (pow(sin(theta * b), 3) + pow(cos(theta), 3));
y = a * sin(theta) * (pow(sin(theta * b), 3) + pow(cos(theta), 3));
vertex(x, y)
}
endShape();
pop();
}
For this project, I chose to explore the properties of the “Bean Curve,” essentially a curve shaped like a bean. After figuring out how to use the parametric equations, I experimented with implementing a series of beans that were arranged in a concentric shape and moved further apart with mouseX. From there, I tried plugging in different variables into this function to see what kinds of compositions I could create with different parameters.
]]>Title: Herald/Harbinger
Artists: Jer Thorpe & Ben Rubin
In the heart of Calgary, Alberta, an installation piece titled “Herald/Harbinger” brings to life the motion of Bow Glacier, demonstrating the connection between human activity and that of the melting glacier. A series of LED arrays installed at the entrance of a building use colored light to indicate real-time movements in the glacier; meanwhile, several speakers project the glacier’s subtle noises into the traffic-filled area. The lights and speakers are connected to the glacier via a seismic observatory and relayed via satellite, then put through an algorithm to visualize the data collected for display. Thousands of passersby feel the glacier’s presence every day, a reflection of how the glacier is an integral part of the city’s water source and history. However, the installation has a shelf life; it is a reminder that this glacier is melting, and there will be one day where the city sounds no longer duet those of Bow Glacier.
“Mantel Rojo” by Manolo Gamboa Naon
Manolo Gamboa Naon, also known as “Manoloide,” is a digital artist based in Argentina whose work centers around generative algorithms that experiment with visual output. In his piece “Mantel Rojo,” or “Red Tablecloth,” he uses creative code to produce a swirling, mesmerizing composition filled with vivid colors and dynamic shapes. I am drawn to this piece because like many of his other works, there is no clear focal point; rather, viewers engage with the piece as a whole. Oftentimes, the longer one spends examining the work, the more little details and interesting coincidences seem to appear. In his piece specifically, the shapes and colors are generated pseudo-randomly, as the same hues reoccur and no other shapes are present besides circles and organic swirls.
function setup() {
createCanvas(480, 480);
background(46, 27, 61);
}
function draw() {
var sec = second();
var min = minute();
var hr = hour();
background(122, 49, 0);
noStroke();
fill(255, 236, 202, 30);
ellipse(240, 128+sec, 730, 505);
ellipse(240, 200+(2*min), 540, 330);
ellipse(240, 30+(13*hr), 368, 230);
// cast lighting
fill(0, 0, 0, 60);
ellipse(240, 425, 420, 75);
// plate shadow
fill(146, 138, 183);
ellipse(240, 415, 400, 75);
fill(177, 175, 232);
ellipse(240, 410, 320, 45);
// plate
fill(255, 242, 233);
rect(210, 100+(13*hr), 60, 312-(13*hr));
ellipse(240, 412, 60, 15);
stroke(255, 205, 171);
strokeWeight(0.5);
ellipse(240, 100+(13*hr), 60, 15);
// middle candle (takes the whole day to melt)
push();
fill(255, 210, 110);
circle(240, 70+(13*hr), 35);
triangle(225, 61+(13*hr), 255, 61+(13*hr), 240, 30+(13*hr));
fill(255, 237, 202);
circle(240, 73+(13*hr), 26);
pop();
// middle candle flame
var min = minute();
noStroke();
rect(160, 292+(2*min), 30, 120-(2*min));
rect(290, 292+(2*min), 30, 120-(2*min));
ellipse(175, 412, 30, 8);
ellipse(305, 412, 30, 8);
stroke(255, 205, 171);
strokeWeight(0.5);
ellipse(175, 292+(2*min), 30, 8);
ellipse(305, 292+(2*min), 30, 8);
// candles on either side of the middle candle (take an hour to melt)
push();
fill(255, 210, 110);
circle(175, 270+(2*min), 22);
circle(305, 270+(2*min), 22);
triangle(175, 245+(2*min), 166, 264+(2*min), 184, 264+(2*min));
triangle(305, 245+(2*min), 296, 264+(2*min), 314, 264+(2*min));
fill(255, 237, 202);
circle(175, 272+(2*min), 16);
circle(305, 272+(2*min), 16);
pop();
// flames for hour candles
noStroke();
rect(120, 352+sec, 20, 60-sec);
rect(340, 352+sec, 20, 60-sec);
ellipse(130, 412, 20, 5);
ellipse(350, 412, 20, 5);
stroke(255, 205, 171);
strokeWeight(0.5);
ellipse(130, 352+sec, 20, 5);
ellipse(350, 352+sec, 20, 5);
// outermost candles (take 1 min to melt)
push();
fill(255, 210, 110);
circle(130, 339+sec, 13);
circle(350, 339+sec, 13);
triangle(130, 325+sec, 124, 336+sec, 136, 336+sec);
triangle(350, 325+sec, 356, 336+sec, 344, 336+sec);
fill(255, 237, 202);
circle(130, 340+sec, 9);
circle(350, 340+sec, 9);
pop();
// flames for minute candles
}
For my abstract clock, I used a set of candles melting to represent the time. The biggest candle in the middle takes the entire day to “melt,” the ones on either side of it both take one hour to melt, and the smallest candles on the ends each take one minute to melt. The flames and cast lighting move accordingly, and the candles reset with the time.
I began with a preliminary sketch: