For my final project, I wanted to create a simple virus simulation where one infected particle infects a population of healthy particles. The particles move across the canvas at random speeds and directions and bounce off the edges of the canvas. I wanted to show how quickly disease can spread within a population if you don’t take preventative measures such as wearing a mask and social distancing.
//Maggie Ma
//Section D
//Final Project Virus Simulator
var dots = [];
var infectionRadius = 20; //light red infection radius
var rpc = 19000; //repelling constant
function setup() {
createCanvas(600, 600);
for (i = 0; i < 100; i++) { //create 100 healthy dots
var p = new dot();
dots.push(p);
}
frameRate(20);
}
function draw() {
background(255);
for (var i = 0; i< dots.length; i++) { //draw uninfected dots
dots[i].update();
dots[i].show();
}
if (frameCount ==100){
dots[9].infected = true; //after 100 frameCount, one dot becomes sick
}
//dots infect eachother if come in contact in the "infectionRadius"
for (var m = 0; m<dots.length; m++) {
for (var i = 0; i < dots.length; i++) {
if (dist(dots[m].x, dots[m].y,
dots[i].x, dots[i].y) <= infectionRadius) {
if (dots[m].infected == true) {
dots[i].infected = true;
}
}
}
}
//Social Distance dot that repels all infected dots
ellipseMode(CENTER);
noStroke();
fill(0,0,0,100);
ellipse(mouseX,mouseY,100,100);
fill(0);
ellipse(mouseX,mouseY,25,25);
var sickcounter = 0;
//var healthycounter = 100;
for (var i = 0; i < dots.length; i ++) {
if(dots[i].infected ==true) {
sickcounter++;
//healthycounter--;
}
}
noStroke();
fill(200);
textSize(12);
text("You are the social distancing particle.", 10, 25);
text("Press any key to add healthy particles.", 10, 50);
fill(200);
text("Press 'Return' key to reset.", 10, 75);
text("Sick Particles:", 10,100);
text(sickcounter, 130,100);
}
//add new uninfected dots by pressing key
function keyPressed() {
dots.push(new dot(mouseX, mouseY));
//healthycounter++;
//if click "Return key" simulation resets.
if (keyCode === RETURN) {
for (var m = 0; m<dots.length; m++) {
for (var i = 0; i < dots.length; i++) {
if(dots[i].infected == true) {
dots[i].infected = false;
dots[9].infected = true;
sickcounter = 0;
}
}
}
}
}
class dot {
constructor() {
this.x =random(3.5, width-3.5);
this.y =random(3.5,height-3.5);
this.dx =random(-10,7);
this.dy =random(-10,7);
this.infected = false
this.history = [];
}
update() {
//move black dots
this.x+= this.dx;
this.y+=this.dy;
//bounce off the edges of the canvas
if (this.x > width) { //bounce off right wall
this.x = width - (this.x - width);
this.dx = -this.dx
} else if (this.x < 0) { //bounce off left wall
this.x = -this.x;
this.dx = -this.dx
}
if (this.y > height) { // bounce off bottom
this.y = height - (this.y - height);
this.dy = -this.dy;
} else if (this.y < 0) { // bounce off top
this.y = -this.y;
this.dy = -this.dy;
}
//create the particle trail
var v = createVector(this.x, this.y);
this.history.push(v);
if (this.history.length>15) { //trail is 15 ellipses long
this.history.splice(0,1); //splice out the trail circle at index 0
}
//effect of the social distancing repeller
var dp = dist(this.x, this.y, mouseX, mouseY);
var f = rpc /(Math.pow(dp, 2)); //Math.pow is exponent w/ base dp
var dirx = (this.x - mouseX)/dp;
var diry = (this.y - mouseY)/dp;
this.x += f*dirx;
this.y += f*diry;
}
show() {
//if dot is infected, color red
if (this.infected ==true) {
noStroke();
//creating dot "tail"
for (var i = 0; i < this.history.length; i++) {
var pos = this.history[i];
fill('red');
ellipse(pos.x,pos.y, i*.15, i*.15)
}
//drawing red dot
fill(255,0,0,100);
ellipse(this.x, this.y, 30,30);
fill('red');
ellipse(this.x,this.y,10,10);
//else if dot is uninfected, color black dot
} else {
noStroke();
//creating dot "tail"
for (var i = 0; i < this.history.length; i++) {
var pos = this.history[i];
fill(0);
ellipse(pos.x,pos.y, i*.15, i*.15)
}
fill(0);
ellipse(this.x,this.y, 10,10);
}
}
}
My project is a simple landscape with hills, trees, buildings, birds, and the sun. The trees vary in size and location, the building varies in width, and the birds vary in location and speed.
//Maggie Ma
//Section D
//Generative Landscape
var buildings = [];
var hills = [];
var noiseParam = 0;
var noiseStep = 0.02;
var trees = [];
var birds = [];
function setup() {
createCanvas(400, 500);
strokeWeight(4);
// create an initial collection of buildings
for (var i = 0; i < 2; i++){
var rx = random(width);
buildings[i] = makeBuilding(rx);
}
//create an initial collection of birds
for (var i = 0; i < 3; i++){
var rx = random(width);
birds[i] = makeBird(rx);
}
// drawing hills in the back
for (var i = 0; i<width/5 +1; i++) {
var n = noise(noiseParam);
var value = map(n,0,1,0,height+50);
hills.push(value);
noiseParam+=noiseStep;
}
frameRate(15);
}
function draw() {
background(255,203,186); //light pink
//sun
fill(253,165,0); //yellow
circle(width/2, height/2-70, 120.438 );
//birds
updateandDisplayBird();
removeBirdThatHaveSlippedOutOfView();
addNewBirdWithSomeRandomProbability();
//hills
drawHills();
displayHorizon();
//building
updateAndDisplayBuilding();
removeBuildingsThatHaveSlippedOutOfView();
addNewBuildingsWithSomeRandomProbability();
//trees
updateAndDisplayTrees();
removeTreesThatHaveSlippedOutOfView();
addNewTreesWithSomeRandomProbability();
}
function drawHills() {
hills.shift();
var n = noise(noiseParam);
var value = map(n,0,1,0,height+50);
hills.push(value);
noiseParam += noiseStep;
beginShape();
vertex(0,height);
for(var i =0; i <= width/5; i++) {
fill(255,163,130); //pink color
strokeWeight(4);
vertex((i*5),hills[i]);
}
vertex(width,height);
endShape();
}
//creating trees
function makeTree(birthLocationX) {
var t = {x: birthLocationX,
breadth: round(random(10,30)),
speed: -5.0,
y: random(10, 40), //randomize tree size and location
w: random(30, 40),
h: random(40, 60),
move: treeMove,
display: treeDisplay}
return t;
}
function treeMove() {
this.x += this.speed;
}
function treeDisplay() {
push();
translate(this.x, height -125);
strokeWeight(4);
fill(80,202,119);
//leaves
ellipse(this.x,this.y, this.w, this.h);
//trunk
line(this.x, this.y+10, this.x, this.y+70);
pop();
}
function updateAndDisplayTrees(){
for (var i = 0; i < trees.length; i++){
trees[i].move();
trees[i].display();
}
}
function removeTreesThatHaveSlippedOutOfView(){
var treesToKeep = [];
for (var i = 0; i < trees.length; i++){
if (trees[i].x + trees[i].breadth > 0) {
treesToKeep.push(trees[i]);
}
}
trees = treesToKeep;
}
function addNewTreesWithSomeRandomProbability() {
var newTreeLikelihood = 0.07;
if (random(0,1) < newTreeLikelihood) {
trees.push(makeTree(width));
}
}
//creating building
function makeBuilding(birthLocationX) {
var bldg = {x: birthLocationX,
breadth: 70,
speed: -8.0,
w: random(70,110), //randomize width
move: buildingMove,
display: buildingDisplay}
return bldg;
}
function buildingMove() {
this.x += this.speed;
}
function buildingDisplay() {
push();
strokeWeight(4);
fill(253,165,0); //yellow block on right side
rect(this.x ,315.478 ,this.w ,137.574);
rect(this.x - 55.901, 364.514 ,15.016 ,88.538); //yellow pillars
rect(this.x +7.51, 364.514 ,15.016 ,88.538);
rect(this.x-28.083,180.775,23.237,26.131); //yellow top
fill(255,255,255);
rect(this.x-33.153,206.996,33.377,33.377); //middle section
fill(253,165,0);
rect(this.x-21.845,219.566,10.762,20.807);
fill(255,255,255);
rect(this.x-40.661, 240.463,48.108 ,212.59 ); //bottom base
fill(0,195,227);
rect(this.x-40.661,259.105,15.016 ,25.094); //turquoise designs
rect(this.x-40.661,302.931,15.016 ,25.094);
rect(this.x-7.508,259.105,15.016 ,25.094 );
rect(this.x-7.508, 302.931,15.016 ,25.094);
fill(225,43,0);
rect(this.x-40.885,346.758,15.016,106.295); //red designs
rect(this.x-7.508,346.758,15.016,106.295);
line(this.x-25.869,240.8 ,this.x-25.869,453.053);
line(this.x-7.508,240.8 ,this.x-7.508,453.053);
pop();
}
function updateAndDisplayBuilding(){
for (var i = 0; i < buildings.length; i++){
buildings[i].move();
buildings[i].display();
}
}
function removeBuildingsThatHaveSlippedOutOfView(){
var buildingToKeep = [];
for (var i = 0; i < buildings.length; i++){
if (buildings[i].x + buildings[i].breadth > 0) {
buildingToKeep.push(buildings[i]);
}
}
buildings = buildingToKeep;
}
function addNewBuildingsWithSomeRandomProbability() {
var newBuildingLikelihood = 0.015;
if (random(0,1) < newBuildingLikelihood) {
buildings.push(makeBuilding(width));
}
}
//creating birds
function updateandDisplayBird(){
for (var i = 0; i < birds.length; i ++){
birds[i].move();
birds[i].display();
}
}
function removeBirdThatHaveSlippedOutOfView(){
var keepBirds=[];
for (var i = 0; i < birds.length; i++) {
if (birds[i].x < width) {
keepBirds.push(birds[i]);
}
}
birds = keepBirds;
}
function addNewBirdWithSomeRandomProbability(){
var newBirdLikelihood = 0.01;
if (random(0,1) < newBirdLikelihood) {
birds.push(makeBird(15, 15));
}
}
function Birdmove(){
this.x += this.speed;
}
function birdDisplay(){
fill(0);
stroke(0);
push();
translate(this.x, this.y);
//wings
triangle(0, 0, 3, 0, -2, -3);
triangle(0, 0, 3, 0, 2, 3);
pop();
}
function makeBird(birthLocationX, birthLocationY){
var bird = {x: birthLocationX,
y:random(100,200),
speed:random(2, 9),
move: Birdmove,
display: birdDisplay};
return bird;
}
//creating grass horizon line
function displayHorizon(){
fill(106,207,124);
stroke(4);
rect(0, height-50, width, height-50);
}
The Dolls House (2016)
For my female artist I chose to learn about Claudia Hart, a computational fine artist of interactive imagery. Her work features topics such as issues of the body, perception, the real vs. unreal, and the relationship between nature and technology. Hart’s work is “symbolist and poetic” (from her website bio), and can be described as mesmerizing and hypnotic. She describes her work as “post photography” —her pieces are generated from computer models instead of captured with a camera.
I examined her piece The Dolls House—a special series of video, drawing and sculpture inspired by the media ballet The Dolls, based in the philosophical idea that history forever renews itself through “a process of decadence, decay and rebirth” (https://claudiahart.com/The-Dolls-House-2016). In this piece, Hart molded mathematical cycles into visual form: a figurative flicker film created with rhythmic animations and pulsing patterns. The passage of time is defined by a cyclical animation where a light moves around the virtual set, casting shadows to mimic sunrise and sunset.
]]>My story is a little western horse racing film. In the first scene, the gun goes off. Then the horses start galloping as the crowd cheers.
//Maggie Ma
//Section D
//Sonic Story
//STORYLINE: a little western horseracing film.
//In the first scene, the start gun goes off.
//Then, the horses start galloping.
//Finally, the crowd cheers.
var horseImage = []; // an array to store the images
var horse = []; //array to hold horse
//frame counter
var frameCount = 0;
//crowd image
var crowd;
//gun image
var gun;
//race gun x position
var gunX = -200;
//horse background of first scene
var horsebg;
//crowd scene zoom in and out
var zoomx = 0;
var zoomy = 0;
//sounds
var neigh;
var shot;
var gallop;
var crowdcheer;
function preload(){
neigh = loadSound("https://courses.ideate.cmu.edu/15-104/f2020/wp-content/uploads/2020/11/neigh.wav");
shot = loadSound("https://courses.ideate.cmu.edu/15-104/f2020/wp-content/uploads/2020/11/gunshot-1.wav");
gallop = loadSound("https://courses.ideate.cmu.edu/15-104/f2020/wp-content/uploads/2020/11/gallop-1.wav");
crowdcheer = loadSound("https://courses.ideate.cmu.edu/15-104/f2020/wp-content/uploads/2020/11/crowd.wav");
//horse running image files
var filenames = [];
filenames[11] = "https://i.imgur.com/Kpc8OBb.gif";
filenames[10] = "https://i.imgur.com/G6TaUvT.gifg";
filenames[9] = "https://i.imgur.com/lbhzzYr.gif";
filenames[8] = "https://i.imgur.com/wuAUUaw.gif";
filenames[7] = "https://i.imgur.com/083HFuW.gif";
filenames[6] = "https://i.imgur.com/VpQvDCP.gif";
filenames[5] = "https://i.imgur.com/4G68Ubf.gif";
filenames[4] = "https://i.imgur.com/hE8DQXc.gif";
filenames[3] = "https://i.imgur.com/6WcJ0gS.gif";
filenames[2] = "https://i.imgur.com/T1Xwkkj.gif";
filenames[1] = "https://i.imgur.com/2PDeXWh.gif";
filenames[0] = "https://i.imgur.com/shnBbse.gif";
crowd = loadImage('https://i.imgur.com/9tu2YoL.jpg');
gun = loadImage('https://i.imgur.com/lmEa4Kh.png');
horsebg = loadImage('https://i.imgur.com/Q9r6TGv.png');
for (var i = 0; i < filenames.length; i++) {
horseImage[i] = loadImage(filenames[i]);
}
}
function stepCharacter() { //update function
this.imageNumber+=1;
//reset image cycle when reach the end of array
if (this.imageNumber>11) {
this.imageNumber =0;
}
}
function drawCharacter() {
//making the dude walk
image(horseImage[this.imageNumber], this.x, this.y);
}
function makeCharacter(wx, wy, wdx, wdy) { //character constructer
w = {
x: wx,
y: wy,
dx: wdx,
dy: wdy,
imageNumber: 0,
stepFunction: stepCharacter,
drawFunction: drawCharacter
}
return w;
}
function setup() {
createCanvas(480, 350);
useSound();
imageMode(CENTER);
for (var i=0; i<1; i++) {
var w = makeCharacter(240,170,0,0);
horse.push(w);
}
frameRate(10);
}
function soundSetup () {
neigh.setVolume(0.8);
shot.setVolume(0.5);
}
function draw() {
background(255);
frameCount++;
if (frameCount >= 0& frameCount < 160) { //race gun goes off scene
image(horsebg, 240, 196, width, height); //background
image(gun, 200 + gunX, height-250, width, height); //gun image
if (frameCount >= 0 & frameCount < 70){
gunX += 7; //gun slides in
} else if (frameCount >= 90 & frameCount <160) {
gunX -=12; //gun slides out
}
}
else if (frameCount >=100 & frameCount <300) { //horse galloping scene
for (var i=0; i<1; i++) { //three people walking
var w = horse[i];
push();
w.stepFunction();
w.drawFunction();
pop();
}
} else if (frameCount >=300 & frameCount < 400) { //crowd cheering scene
image(crowd, 240, 196, width + zoomx, height + zoomy);
zoomx +=3; //zoom in x
zoomy += 3; //zoom in y
} else if (frameCount >= 400 & frameCount <450) { //end scene
background(0);
fill(255);
textFont('Courier New Italic')
textSize(16);
text('the great race.', width-200, height-75);
}else if (frameCount >= 450) {
background(0);
noLoop();
}
//sound play
if (frameCount == 88) {
shot.play();
} else if (frameCount == 130){
gallop.play();
} else if (frameCount == 200){
neigh.play();
} else if (frameCount == 270) {
crowdcheer.play();
} else if (frameCount > 400) {
crowdcheer.stop();
}
}
Endel by Oleg Stavitsky
Endel (CEO and co-founder Oleg Stavitsky) is a generative music app that creates sound based on the user’s environment. The app pulls data from the user’s phone including the weather, heart rate, physical activity rate, GPS location, and circadian rhythms to create personalized soundscapes (ex. Gentle music for sleeping, studying, and relaxing) by adjusting the sonic output to match user activity. It can siphon heart-rate from your smartwatch and create a beat to match your pulse and footsteps. I think this app is extremely powerful in that it has been used to aid in ADHD, insomnia, and tinnitus by curating relaxing and peaceful personalized music. It never creates two compositions that sound the same—I almost want to download it and try the app out myself!
In Bon’s Looking Outwards on Data Visualization, he examined All Streets Limited by Ben Fry. Fry used data from the U.S. Census Bureau to code the map in Javascript, which consists of 240 million individual road segments across the United States. He applied the Albers equal-area conic projection to obtain the longitude and latitude coordinates of the streets. I agree with Bon that the illustration provides a macro view of our national interconnectedness. After doing my own research, I also found it interesting that the map contains no other terrain (canyons, rivers, mountains, etc.), yet we can still see the shapes of these natural forms emerge as roads weave and navigate around, demonstrating the power dynamics between the natural and artificial/industrial.
For my portrait I chose to draw my little brother. I had him choose his favorite images from a set of keyboard symbols. The random symbols increase in size as the mouse moves to the right. Symbols also draw along with mouse movement. If you click the mouse, the canvas wipes white and resets.
//Maggie Ma
//Section D
//Self-Portrait
let img;
let symbols=["☻","☺","♬","☾","⍣","㋡","☀","♟"]; //array of symbols
function preload() {
img = loadImage('https://i.imgur.com/M8aUttC.jpg');
}
function setup() {
createCanvas(400, 533);
background(255);
img.loadPixels();
noStroke();
}
function draw() {
let x = floor(random(img.width)); //x position of symbols
let y = floor(random(img.height)); //y position of symbols
let pixels = img.get(x, y); //grab pixels from image
//randomly select symbols from array
var r = int(random(0,9));
var randompicker = symbols[r];
fill(pixels); //fill symbol with proper pixel color
//prevent symbols from getting too large
//symbols increase in size as mouse moves to right
textSize(mouseX/20);
text(randompicker, x,y);
//draw symbols as mouse moves
textSize(10);
text(randompicker, mouseX, mouseY);
//refresh to white canvas when mouse is pressed
if (mouseIsPressed) {
background(255);
}
}
Sarah Groff Hennigh- Palermo is an artist, programmer, and data designer who explores the relationship between technology and information by creating work focused on an “aesthetic exploration of experience” (Hennigh-Palermo) rather than an information-instrumental output of data. Her talk centered on undermining computers—how can make computers less “machine-like” and more accessible, visceral, and contextual? Her piece Oublié/trouvé, or: Towards a Theory of Invested Objects is an app and product that visualizes data based on your feelings and experiences (temperature, proximity from home, humidity, weather, etc) at a specific moment or location as an attempt to implement the personal into the machine. She then developed LiveCode, a communal computing experience of visuals, music, dance, and games with code written and evaluated in real time. She is also a part of an algorithmic band called Codie, which codes live music and visuals at each performance, which I thought was pretty cool.
I chose to use the Epitrochoid Curve for my project this week. My code draws two curves—a blue and red one, and it’s pretty cool to see how and where they overlap with each other.
*because of the lag it’s really slow on WordPress ;(*
//Maggie Ma
//Section D
//variables for equation on https://mathworld.wolfram.com/Epitrochoid.html
var a = 30; //radius of small circle
var b = 10; //radius of rotating perimeter circle
var h = 0; //height of curve from the center of b
var t; //time
var x;
var y;
var speed;
function setup() {
createCanvas(500,500);
background(255);
}
function draw() {
noStroke();
constrain(mouseX,0,width);
constrain(mouseY, 0, height);
//draws dots (without trail) when mouse is pressed
//background changes to black
//dots change in size with mouse position
if (mouseIsPressed) {
background(0);
for (t = 0; t <TWO_PI; t += 1) {
fill(0,0,255);
epCurve2(); }
for (t= 0; t<PI; t+= 1) {
fill(255,0,0);
epCurve2(); }
}
speed=map(mouseX,0,width,.5,-.5); //changes the speed and size of curve as mouseX moves
a+=speed;
h+=speed;
fill(255,0,0); //drawing red curve
for (t =-PI; t < TWO_PI; t += .08) {
epCurve();
}
fill(0,0,255); //drawing blue curve
for (t= -PI; t<PI; t+= .08) {
epCurve();
}
}
function epCurve() { //Epitrochoid Curve function
push();
translate(width/2, height/2);
//draw curve
x=(a+b)*cos(t)-h*cos(((a+b)/b)*t);
y=(a+b)*sin(t)-h*sin(((a+b)/b)*t);
ellipse(x,y,0.8,0.8); //draws dots on canvas
pop();
}
function epCurve2() { //Dotted curve when mouse is pressed
push();
translate(width/2, height/2);
//draw curve
x=(a+b)*cos(t)-h*cos(((a+b)/b)*t);
y=(a+b)*sin(t)-h*sin(((a+b)/b)*t);
ellipse(x,y,0.5+mouseX/100,0.5+mouseY/100); //draws dots on canvas that change size w/ mouse position
pop();
}
Martin Wattenberg and Fernanda Viegas
Martin Wattenberg and Fernanda Viegas’ Wind Map visualizes the tracery of wind flow over the United States in the most basic and familiar way—visual motion. On calm wind days, the artwork can be a “soothing meditation on the environment” (Wattenberg), whereas during hurricanes, the piece can become overwhelming and terrifying. Something that I found interesting was that bird watchers actually use the map to track migration patterns, and cyclists use it to prepare for trips. Even conspiracy theorists use the map to track mysterious air chemicals. Data for the wind map was collected from the National Digital Forecast Database, and Wattenberg took inspiration from Edmund Halley (1686), who developed the technique of using comet-like trails to visualize motion. Wattenberg used HTML and JavaScript.