Jisoo Geum Final Project

 

jgeum-final project

I am uploading a zip file instead of the sketch file since the program contains sound.

My final project is an educational program that teaches the user to learn (hopefully) about the Korean alphabet: Hangul. Hangul is very easy to learn because it does not require people to memorize the meaning of each character. Since Hangul is an alphabet system like English, anyone can read without knowing the meaning itself. I only focused on the Consonant letters of Hangul so the alphabets showing up on the program does not make a full word or a letter.

As I was writing the program, I realized how complicated it is to teach a language even though I initially thought Hangul was relatively simple. Thus, I am planning to expand on this project and hopefully make a program that covers not only consonants but also vowels and maybe some simple words.

(My original plan was to make the user draw on top of the text using turtle graphics, but somehow the code didn’t work. I canceled out the turtle graphics part. )

Final Project-Urban Wildlife-Veronica Wang

In this interactive game/animation:
(click on the canvas first)
Press left arrow to add birds, press right arrow to add chimneys. Bird nests and raccoons will show up according to the added elements. The position of mouseY changes the color of the sky. As more birds are added, bird sound gets louder.

sketch

//Veronica Wang
//Section B
//yiruiw@andrew.cmu.edu
//Final Project

var Y_AXIS = 1; //gradient sky axis
var c1, c2; //gradient sky colors
var PREFIX = "https://courses.ideate.cmu.edu/15-104/f2018/wp-content/uploads/2018/12/";
//var PREFIX = "";
var birdSound;
var trainSound;
var bV = 0.2;
var houseList = []; //array of houses
var allTrees = []; //trees
var birdList = []; //birds
var raccoonList = []; //raccoons
var flap = true;
var nestCount = 0;
var birdperNest = 3; //for every three birds draw a nest
var trainList = []; //trains
var rachouseCount = 0;

frameCount = 200;


function preload(){
    birdSound = loadSound(PREFIX + "birds.wav");
    trainSound = loadSound(PREFIX + "train.wav");
    trainSound.setVolume(0.5);
}

function setup() {
    createCanvas(500, 300);

    //sky colors
    c1 = color(244, 248, 255); //light blue
    c2 = color(94, 164, 186); //dark teal blue

    drawBackground();

    //draw houses
    for(var i = 0; i < 10; i++) {
        var newX = random(80, 420);
        var newY = random(120, 200);
        if (houseList.length < 1){
            houseList.push(makeHouse(newX, newY));
        } 
        else{
            while (checkifLess(newX, newY, houseList, 30)){
                newX = random(80, 420);
                newY = random(120, 200);
            }
            houseList.push(makeHouse(newX, newY));
        }
    }

    //draw trees
    for(var i = 0; i < 40; i++) {
        allTrees.push(makeTree());
    }
    sortTree(allTrees);
    sortTree(houseList);

    //draw creatures
    birdList.push(makeBird(width / 2,height / 2, 15));
    raccoonList.push(makeRac(width / 2, height / 2, 5));


}

function draw() {
    //play sound
    birdSound.setVolume(bV);
    if (frameCount % 100 == 0){
        birdSound.play();
    }
    //set volume to be proportional to number of birds
    bV = (map(birdList.length, 0, 20, 0, 1));
    

    drawBackground();
    drawStairs();

    //initial number of artificial nests
    nestCount = 0;
    rachouseCount = 0;

    //raccoon house counter
    for (var i = 0; i < houseList.length; i++) {
        if (houseList[i].rh){
            rachouseCount += 1;
        }   
    }

    //add raccoons
    if (raccoonList.length < rachouseCount * 2){
        raccoonList.push(makeRac(random(0, 500), random(150, 220)));
    }

    //drawing raccoons and removing them from array if out of canvas
    for (var i = 0; i < raccoonList.length; i++) {
        raccoonList[i].draw();
        raccoonList[i].rx += raccoonList[i].vel;
        if (racoutofBound(raccoonList[i])){
            raccoonList.splice(i, 1);
            i -= 1;
        }       
    }

    //draw houses
    for (var i = 0; i < houseList.length; i++) {
        houseList[i].draw();     
    }

    //draw trees
    for (var i = 0; i < allTrees.length; i++){
        if (i % 3 == 0){
            allTrees[i].draw1();
        } else if (i % 3 == 1){
            allTrees[i].draw2();
        } else {
            allTrees[i].draw3();
        }
        if (allTrees[i].nest){
            nestCount += 1;
        }
    }

    drawTracks();
    
    //bird wing flap
    if (frameCount % 40 == 0){
        flap = !(flap);
    }

    //if bird is out of canvas, take it out of the array
    for (var i = 0; i < birdList.length; i++) {
        if (birdoutofBound(birdList[i])){
            birdList.splice(i, 1);
            i -= 1;
        }
    }

    //draw birds
    for (var i = 0; i < birdList.length; i++) {
        if (flap){
            birdList[i].draw2();
        } else {
            birdList[i].draw();
        }
        birdList[i].bx += birdList[i].xvel * noise(1);
        birdList[i].by += birdList[i].yvel * noise(1);
    };

    //adding random movement and scale birds
    if (frameCount % 100 == 0){
        for (var i = 0; i < birdList.length; i++) {
            birdList[i].xvel += random(-3, 3) * (map(pow(2, birdList[i].by / 50), 0, pow(2, 300 / 50), 1, 100)) / 35;
            birdList[i].yvel += random(-1, 1) * (map(pow(2, birdList[i].by / 50), 0, pow(2, 300 / 50), 1, 100)) / 35;

        }
    }
    

    //night sky filter
    var darkness = map(mouseY, 0, height, 0, 200);
    fill(0, 0, 0, darkness);
    noStroke();
    rect(0, 0, width, height);

    //add nest to empty trees
    if (birdList.length > 0 & int(birdList.length / birdperNest) < 40){
        if (int(birdList.length / birdperNest) > nestCount){
            var seltree = int(random(0, allTrees.length));
            while (allTrees[seltree].nest){
                seltree = int(random(0, allTrees.length));
            }
            allTrees[seltree].nest = true;
        } else if (int(birdList.length / birdperNest) < nestCount){
            var seltree = int(random(0, allTrees.length));
            if (allTrees[seltree].nest == true){
                allTrees[seltree].nest = false;
            }
            
        }
    }

    //draw train
    if (frameCount % 400 == 0) {
        trainList.push(makeTrain());
        trainSound.play();
    }
    for (var i = 0; i < trainList.length; i++) {
        trainList[i].x -= trainList[i].vel;
        trainList[i].draw();
    };
    if (trainList.length > 0){
        if (trainList[0].x < -750){
            trainList.splice(0, 1);
        }
    }
}

//if raccoon is out of canvas
function racoutofBound(bird){
    if (bird.rx < 0){
        return true;
    } else if (bird.rx > width){
        return true;
    } else if (bird.ry < 0){
        return true;
    } else if (bird.ry > height){
        return true;
    }
    return false;
}

//if bird is out of canvas
function birdoutofBound(bird){
    if (bird.bx < 0){
        return true;
    } else if (bird.bx > width){
        return true;
    } else if (bird.by < 0){
        return true;
    } else if (bird.by > height){
        return true;
    }
    return false;
}

//sort tree order by y position
function sortTree(treelist){
    n = treelist.length;
    for (var i = 0; i < n; i++){
        for (var j = 0; j < n - i - 1; j++){
            
            if (treelist[j].y > treelist[j+1].y){
                var temp = treelist[j];
                treelist[j] = treelist[j+1];
                treelist[j+1] = temp;
            }
        }
    }
}

//draw tree outside of houses
function ispointIn(x, y, house){

    var x1 = house.x;
    var x2 = house.x + house.w;
    var y1 = house.y - house.w / 3;
    var y2 = house.y + house.h * house.f;
    if (x < x1){
        return false;
    } else if (x > x2){
        return false;
    } else if (y < y1){
        return false;
    } else if (y > y2 + 13){
        return false;
    }
    return true;
}

//goes through list to check if point is outside the boundary
function hListcheck(x, y, list, n){
    if (list.length < 1){
        return false;
    }
    if (n < list.length - 2){
        return (ispointIn(x, y, list[n]) || hListcheck(x, y, list, n + 1));
    } else {
        return ispointIn(x, y, list[n]);
    }
}

//make sure houses are not overlapping
function checkifLess(x, y, list, mindist){
    var chck = false;
    for (var i = 0; i < list.length; i++) {
        var objdist = dist(x, y, list[i].x, list[i].y);
        if (objdist < mindist){
            chck = true;
        }
    }
    return chck;
}

//house object
function makeHouse(locX, locY){
    var num = 75;
    var house = { x: locX,
                  y: locY,
                  w: map(pow(2, locY / num), 0, pow(2, 300 / num), 1, 100),  //proportionally scale house in the distance
                  f: floor(random() * 2 + 1), //random number of floors from 1-3
                  rh: false, //raccoon houses
                  draw: drawHouse
                };
    return house;
}

function drawHouse(){
    noStroke();
    fill(173, 110, 110);
    var h = this.w / 2;
    rect(this.x, this.y, this.w, h * this.f); //body of house
    rect(this.x + this.w * 0.7, this.y - this.w / 3, 
        this.w / 5, this.w / 3); //chimney
    triangle(this.x, this.y, 
            this.x + this.w / 2, this.y - this.w / 3, 
            this.x + this.w, this.y); //roof
    if(this.f == 1){
        //gradient
        for (var i = 0; i < this.y / 10; i++) { 
            var op2 = map(i, 0, 50, 0, 200);
            stroke(201, 141, 141, op2); 
            line(this.x, i * this.y * 0.005 + this.y, 
                this.x + this.w, i * this.y * 0.005 + this.y);
        }
        drawWindow(this.x + this.w / 5, this.y + this.w / 6, 
                this.w * 0.3, this.w / 5);
        drawShrub(this.x, this.y + h, 
                this.w * 0.5, this.w * 0.3);

    }else{
        //gradient
        for (var i = 0; i < this.y / 5; i++) { 
            var op3 = map(i, 0, 50, 0, 200);
            stroke(201, 141, 141, op3); 
            line(this.x, i * this.y * 0.005 + this.y, 
                this.x + this.w, i * this.y * 0.005 + this.y);
        }
        drawWindow(this.x + this.w / 5, this.y + this.w / 6, 
                this.w * 0.3, this.w / 5);
        drawWindow(this.x + this.w / 5, this.y + this.w * 0.6, 
                this.w * 0.3, this.w / 5);
        drawShrub(this.x, this.y + this.w, 
                this.w * 0.4, this.w * 0.3);
    } 

    if(this.rh){
        fill(135, 81, 132);
        rect(this.x + this.w, 
             this.y + h / 5, this.w / 6, (h * this.f) * 0.75);
        triangle(this.x + this.w, this.y + h * this.f, 
                 this.x + this.w, this.y + (h * this.f) / 2, 
                 this.x + this.w + this.w / 3, this.y + h * this.f);
    }
}

function drawWindow(x, y, w, h){
    noStroke();
    fill(239, 233, 218);
    rect(x, y, w, h);
}

function drawShrub(x, y, w, h){
    noStroke();
    fill(143, 168, 104);
    ellipse(x, y, w, h);
    ellipse(x + 5, y, w * 0.8, h * 0.5);
    ellipse(x + 3, y - 2, w * 0.8, h * 0.8);

}

function makeTree(){
    var num1 = 75;

    var tempY = random(120, 200);
    var tempX = random(0, 450)
    var mult = map(pow(2, tempY / num1), 0, pow(2, 300 / num1), 1, 100) / 40;

    var tX = tempX + 3 * mult;
    var tY = tempY + 30 * mult;

    while (hListcheck(tX, tY, houseList, 0)){
        tempY = random(120, 200);
        tempX = random(0, 450)
        tX = tempX + 3 * mult;
        tY = tempY + 30 * mult;
    }
    
    var tree = { x: tempX,
                 y: tempY,
                 w: map(pow(2, tempY / num1), 0, pow(2, 300 / num1), 1, 100),  //proportionally scale house in the distance
                 m: map(pow(2, tempY / num1), 0, pow(2, 300 / num1), 1, 100) / 40,
                 draw1: tree1draw,
                 draw2: tree2draw,
                 draw3: tree3draw,
                 nest: false
                };
    return tree;

}


function tree1draw(){
    noStroke();
    push();
    
    translate(this.x, this.y);
    scale(this.m, this.m);
    fill(122, 98, 66);
    rect(3, 0, 3, 30);
    fill(95, 140, 99, 180);
    ellipse(0, -5, 30, 35);
    ellipse(15, -5, 20, 25);
    ellipse(5, 8, 50, 20);
    if(this.nest == true){
        fill(89, 68, 49);
        rect(0, 0, 10, 10);
        fill(193, 164, 137);
        ellipse(5, 5, 4, 4);
    }
    pop();
}

function tree2draw(){
    
    noStroke();
    push();
    translate(this.x, this.y);
    scale(this.m, this.m);
    fill(122, 98, 66);
    rect(5, 0, 3, 30);
    fill(95, 120, 96, 200);
    triangle(18, 18, -6, 18, 6, -30);
    if(this.nest == true){
        fill(89, 68, 49);
        rect(0, 0, 10, 10);
        fill(193, 164, 137);
        ellipse(5, 5, 4, 4);
    }
    pop();

}

function tree3draw(){
    noStroke();
    push();
    var mult = this.w / 40;
    translate(this.x, this.y);
    scale(mult, mult);
    fill(122, 98, 66);
    rect(3, 0, 3, 30);
    fill(108, 132, 102, 200);
    ellipse(4, -17, 20, 20);
    ellipse(4, -8, 30, 20);
    ellipse(4, 5, 40, 25);
    if(this.nest == true){
        fill(89, 68, 49);
        rect(0, 0, 10, 10);
        fill(193, 164, 137);
        ellipse(5, 5, 4, 4);
    }
    pop();

}

function drawBackground(){

    //draw background gradient
    setGradient(0, 0, width, height * 0.45, c1, c2, Y_AXIS);

    //mountain layer 1
    noStroke();
    fill(75, 137, 138);
    beginShape();
    curveVertex(0, height);
    curveVertex(0, height);
    curveVertex(0, 200);
    curveVertex(0, 180);
    curveVertex(190, 60);
    curveVertex(280, 80);
    curveVertex(350, 70);
    curveVertex(420, 100);
    curveVertex(520, 80);
    curveVertex(width, height);
    curveVertex(width, height);
    endShape();
    
    //gradient mask
    for (var i = 0; i < 500; i++) { 
        var op = map(i, 100, 500, 0, 255);
        stroke(255, 255, 255, op); 
        line(0, i * 0.6, width, i * 0.6);
    }

    //mountain layer 2
    noStroke();
    strokeWeight(1);
    fill(75, 147, 154);
    beginShape();
    curveVertex(0, height);
    curveVertex(0, height);
    curveVertex(0, 120);
    curveVertex(0, 110);
    curveVertex(100, 70);
    curveVertex(200, 130);
    curveVertex(300, 90);
    curveVertex(400, 130);
    curveVertex(500, 120);
    curveVertex(500, 130);
    curveVertex(width, height);
    curveVertex(width, height);
    endShape();

    //gradient mask
    for (var i = 0; i < 500; i++) { 
        var op = map(i, 100, 500, 0, 255);
        stroke(255, 183, 80, op); 
        line(0, i, width, i);
    }

}

function drawStairs(){
    noStroke();
    fill(99, 88, 77);
    for (var i = 0; i < 15; i++) {
        rect(i * 5 + 30, i * 3 + 190, 8, 1.5);
        rect(i * 5 + 40, -i * 2 + 190, 8, 1);
        rect(i * 5 + 30, i * 3 + 120, 1 + i * 0.5, 1)
    };

    rect(35, 190, 2, 45);
    rect(28, 190, 2, 45);
    rect(28, 210, 8, 2);

    rect(110, 162, 1, 35);
    rect(116, 162, 1, 35);
    rect(110, 170, 6, 1);

    strokeWeight(2);
    line(1, 1, 100, 100);

    for (var j = 0; j < 30; j++) {
        fill(117, 107, 98);
        rect(j * 2 + 440, j * 3 + 130, 1 + j * 0.7, 1);
    };

}

function drawTracks(){
    stroke(122, 102, 82);
    strokeWeight(2);
    line(0, 280, 500, 280);
    strokeWeight(1);
    line(0, 275, 500, 275);

    for (var i = 0; i < 15; i++) {
        rect(i * 40, 273, 1, 6);
    };
}

function makeTrain(){
    var train = { x: 500,
                  y: 277,
                  vel: random(5, 15),
                  draw: drawTrain
                };
    return train;
}

function drawTrain(){
    noStroke();
    fill(80);
    triangle(this.x, this.y - 5, this.x + 25, this.y - 5, this.x + 25, this.y - 20);
    rect(this.x + 12, this.y - 40, 10, 15);
    rect(this.x + 20, this.y - 40, 50, 35);
    rect(this.x + 22, this.y - 50, 15, 25);
    rect(this.x + 55, this.y - 59, 30, 4);
    rect(this.x + 70, this.y - 16, 16, 8);
    rect(this.x + 80, this.y - 16, 8, 8);
    rect(this.x + 88, this.y - 13, 18, 3);
    fill(140, 89, 88);
    rect(this.x + 16, this.y - 45, 40, 25);
    fill(140, 100, 88);
    rect(this.x + 60, this.y - 55, 20, 45);
    fill(201, 216, 215);
    rect(this.x + 64, this.y - 52, 12, 18);
    fill(96, 83, 58);
    ellipse(this.x + 30, this.y - 5, 15, 15);
    ellipse(this.x + 70, this.y - 5, 15, 15);

    for (var i = 1; i < 5; i++) {
        fill(80);
        rect(this.x + 100 * i, this.y - 55, 90, 3);
        rect(this.x + 100 * i, this.y - 13, 100, 3);
        fill(140, 120, 88);
        rect(this.x + 5 + 100 * i, this.y - 52, 80, 45);
        fill(140, 130, 98);
        rect(this.x + 5 + 100 * i, this.y - 22, 80, 15);
        fill(201, 216, 215);
        rect(this.x + 12 + 100 * i, this.y - 48, 30, 18);
        rect(this.x + 48 + 100 * i, this.y - 48, 30, 18);
        fill(96, 83, 58);
        ellipse(this.x + 20 + 100 * i, this.y - 5, 15, 15);
        ellipse(this.x + 70 + 100 * i, this.y - 5, 15, 15);
    };

    fill(0);
    drawSmoke(this.x, this.y);

}

function drawSmoke(x, y){
    fill(255, 255, 255, 100);
    ellipse(x + 30, y - 60, 20, 10);
    ellipse(x + 50, y - 70, 15, 8);
}


function makeBird(x, y) {
    var num = 50;
    var bird = {"bx": x, 
                "by": y, 
                "bsz": size, 
                "bsca": (map(pow(2, y / num), 0, pow(2, 300 / num), 1, 100)) / 35,
                "xvel": random(-10, 10) * (map(pow(2, y / num), 0, pow(2, 300 / num), 1, 100)) / 35,
                "yvel": random(-5, 5) * (map(pow(2, y / num), 0, pow(2, 300 / num), 1, 100)) / 35,
                };
    bird.draw = birdDraw;
    bird.draw2 = birdDraw2;
    return bird;
}

function birdDraw(){
    noFill();
    stroke(0);
    strokeWeight(2);
    push();
    translate(this.bx, this.by);
    scale(this.bsca);
    ellipseMode(CENTER); 
    arc(35, 35, 100, 100, PI * 1.25, PI * 1.5);
    arc(-35, 35, 100, 100, PI * 1.5, PI * 1.75);
    fill(0);
    ellipse(0, 0, 5, 5); 
    pop();
}

function birdDraw2(){
    noFill();
    stroke(0);
    strokeWeight(2);
    push();
    translate(this.bx, this.by);
    scale(this.bsca);
    rotate(PI/10);
    arc(35, 35, 100, 100, PI * 1.25, PI * 1.5);
    fill(0);
    ellipse(0, 0, 5, 5); 
    pop();

    push();
    translate(this.bx, this.by);
    scale(this.bsca);
    rotate(-PI / 10);
    arc(-35, 35, 100, 100, PI * 1.5, PI * 1.75);
    pop();
}

//make raccoon
function makeRac(x, y){
    var rac;
    var num = 50;
    rac = {
        "rx": x ,
        "ry": y, 
        "rsz": size,
        "bsca": (map(pow(2, y / num), 0, pow(2, 300 / num), 1, 100)) / 400,
        "vel": random(-2, 2) * (map(pow(2, y / num), 0, pow(2, 300 / num), 1, 50)) / 35
    };
    rac.draw = racDraw;
    return rac;
}

//draw raccoon
function racDraw(){
    var inv = -1;
    push();
    translate(this.rx, this.ry);
    fill(0);
    noStroke();
    beginShape();
    if (this.vel < 0){
        scale(this.bsca, this.bsca);
    } else{
        scale(this.bsca * -1, this.bsca);
    }
    vertex(0, 0);
    vertex(5, -10);
    vertex(3, -20);
    vertex(7, -30);
    vertex(5, -40);
    vertex(8, -45);
    vertex(20, -40);
    vertex(65, -55);
    vertex(85, -65);
    vertex(150, -60);
    vertex(190, -30);
    vertex(180, 20);
    vertex(190, 50);
    vertex(180, 80);
    vertex(170, 80);
    vertex(170, 75);
    vertex(175, 73);
    vertex(176, 55);
    vertex(140, 25);
    vertex(110, 25);
    vertex(80, 80);
    vertex(70, 80);
    vertex(70, 75);
    vertex(75, 75);
    vertex(80, 50);
    vertex(70, 10);
    vertex(50, 10);
    vertex(30, 5);
    vertex(10, 10);
    vertex(0, 0);
    endShape();

    beginShape();
    vertex(200, -25);
    vertex(192, 10);
    vertex(200, 18);
    vertex(210, -20);
    endShape();

    beginShape();
    vertex(220, -15);
    vertex(230, -8);
    vertex(220, 30);
    vertex(210, 22);
    endShape();

    beginShape();
    vertex(240, -3);
    vertex(250, 5);
    vertex(242, 20);
    vertex(232, 25);
    endShape();

    beginShape();
    fill(255);
    vertex(50, 10);
    vertex(30, -15);
    vertex(20, -13);
    vertex(15, -5);
    vertex(15, -5);
    vertex(20, 0);
    vertex(26, 0);
    vertex(35, 6);
    endShape();

    fill(0);
    ellipse(23, -8, 5, 5);
    rect(112, 50, 15, 7);

    rotate(PI / 4);
    rect(60, -40, 10, 40);
    rect(120, -80, 10, 30);
    pop();
}

//interactions
function keyPressed(){
    if(keyCode === RIGHT_ARROW){
        if (rachouseCount < houseList.length){
            var selhouse = int(random(0, houseList.length));
            while (houseList[selhouse].rh == true){
                selhouse = int(random(0, houseList.length));
            }
            houseList[selhouse].rh = true;
        }
    }
    if(keyCode === LEFT_ARROW){
        birdList.push(makeBird(random(0, 500), random(0, 200)));
    }
}

//Linear gradient code from p5js examples https://p5js.org/examples/color-linear-gradient.html
function setGradient(x, y, w, h, c1, c2, axis) {
    noFill();
    if (axis == Y_AXIS) {  // Top to bottom gradient
        for (var i = y; i <= y + h; i++) {
            var inter = map(i, y, y + h, 0, 1);
            var c = lerpColor(c1, c2, inter);
            stroke(c);
            line(x, i, x + w, i);
        }
    }  
}

In this project I want to create an interactive animation on cohabitation wiht urban wildlife in cities. As we are displacing natural habitats to make way for urban sprawl, effects of a loss of biodiversity and edge effects from habitat fragmentation are becoming more pronounced. In my studio I am currently working on a thesis project dealing with the environment and cohabitation/negotiation of boundaries between human and more-than-human species, and creating installations for endangered species as well as synanthropic species to share urban space. I am inspired by the works of Joyce Hwang and Sarah Gunawan , and I want to create an animation that documents the vision of such installation projects and their impacts on our environment. Especially the bird landing pads and composting chimneys for raccoons.

In this animation, trains pass by at a certain interval of time, pressing left arrow adds a bird, and for every 3 birds added, an artificial bird nest attached to trees will pop up. (Nests will start disappearing when birds leave the screen). Pressing right arrow adds a compost chimney attached to houses, and every chimney attracts 2 raccoons.

Working on this project I spent a large chunk of my time illustrating the creatures and houses which in hindsight could have been done much easier if I just drew them and upload them to imgur. I also had trouble with overlapping objects and had to create functions that sort the order or the object array and also draws objects outside of the boundary of other objects. I feel like I reviewed everything we learned this semester through this project.

Sound Test

 

Yoo Jin Shin- Final Project

Final

// Yoo Jin Shin
// yoojins@andrew.cmu.edu
// Section D
// Final Project

// All scenes global variables
var yaxis = 1;
var xaxis = 2;
var frames = []; 
var frameCounts = 0;
var textSpeed = 3;
var slider;
var sliderValue;

// Yeosu global variables
var c1, c2, c3, c4;
var terrainSpeed1 = 0.0001; 
var terrainDetail1 = 0.003;
var terrainSpeed2 = 0.00012;
var terrainDetail2 = 0.0045;
var terrainSpeed3 = 0.00007;
var terrainDetail3 = 0.0006;
var clouds = []; 
var trees = [];
var textX = 1;
var sunMouse; 
var birdLoad;
var bird1;
var birds = [];

// Grand Canyon global variables
var terrainSpeed4 = 0.00003;
var terrainDetail4 = 0.0005;
var terrainSpeed5 = 0.00012;
var terrainDetail5 = 0.007;
var terrainSpeed6 = 0.00007;
var terrainDetail6 = 0.0065;
var textX3 = 1;
var c9;

// Las Vegas global variables
var c5, c6, c7, c8;
var buildings = [];
var snowflakes = [];
var drops = [];
var textX2 = 1;
var buildingMouse;
var carLoad;
var car1;
var cars = [];

function preload(){
    // Loading sprite sheet for girl character via direct links from imgur
    // Creds to https://www.gameart2d.com/freebies.html
    var filenames = [];
      filenames[0] = "https://i.imgur.com/oEJGbCm.png";
      filenames[1] = "https://i.imgur.com/QsZoWMJ.png";
      filenames[2] = "https://i.imgur.com/kyo4lhi.png";
      filenames[3] = "https://i.imgur.com/XmPYK4a.png";
      filenames[4] = "https://i.imgur.com/w80hQ6A.png";
      filenames[5] = "https://i.imgur.com/0BiNaBy.png";
      filenames[6] = "https://i.imgur.com/WL7kjhR.png";
      filenames[7] = "https://i.imgur.com/z6agkKq.png";
      filenames[8] = "https://i.imgur.com/Vxi5Eu6.png";
      filenames[9] = "https://i.imgur.com/mrjcctG.png";
      filenames[10] = "https://i.imgur.com/oDP8zsr.png";
      filenames[11] = "https://i.imgur.com/DsNFucK.png";
      filenames[12] = "https://i.imgur.com/8Y3e8BW.png";
      filenames[13] = "https://i.imgur.com/aQUjC1v.png";
      filenames[14] = "https://i.imgur.com/6yQMDLO.png";
      filenames[15] = "https://i.imgur.com/fJZPSul.png";
      filenames[16] = "https://i.imgur.com/vghvvNP.png";
      filenames[17] = "https://i.imgur.com/oh2WsXz.png";
      filenames[18] = "https://i.imgur.com/yPh4Upu.png";
      filenames[19] = "https://i.imgur.com/k0wd9HK.png";

    for (var i = 0; i < filenames.length; i++) {
        frames.push(loadImage(filenames[i]));
    }

    // Loading bird and car images created using illustrator
    birdLoad1 = "https://i.imgur.com/RQSUNmm.png";
    bird1 = loadImage(birdLoad1);

    carLoad1 = "https://i.imgur.com/yKCei98.png";
    car1 = loadImage(carLoad1);
}

function setup() {
  createCanvas(480, 480);
  drawSlider();

  // Yeosu: Define gradient colors
  c1 = color(225, 217, 199); // sky top color - grey
  c2 = color(254, 180, 144); // sky bottom color - orange
  c3 = color(50, 50, 2); // ground left - lighter green
  c4 = color(27, 20, 2); // ground right - dark green

  // Yeosu: Initialize clouds
  for (var i = 0; i < 3; i++) {
      var cloudX = random(width); 
      clouds[i] = drawClouds(cloudX);
  }

  // Yeosu: Initialize trees
  for (var i = 0; i < 7; i++) {
    var treeX = random(width);
    var t = drawTrees(treeX);
    trees[i] = t;
  }

  // Yeosu: Initialize birds
  for (var i = 0; i < 7; i++) {
    var birdX = random(width);
    birds[i] = drawBird(birdX);
  }

  // Las Vegas: Define gradient colors
  c5 = color(0, 0, 0); // sky top color - black
  c6 = color(61, 48, 52); // sky bottom color - purpley

  // Las Vegas: Initialize buildings
  for (var i = 0; i < 6; i++) {
      var rx = random(width);
      buildings[i] = drawBuilding(rx);
  }  

  // Las Vegas: Initialize cars
  for (var i = 0; i < 10; i++) {
    var carX = random(width);
    cars[i] = drawCar(carX);
  }
    imageMode(CENTER);
}

function drawSlider() {
  slider = createSlider(5, 60, 30);
  slider.style(200, '100px');
  slider.position(20, 450);
}

function draw() {
  // Set slider to adjust frameRate
  var frameRateSpeed = slider.value();
  frameRate(frameRateSpeed);
  
  // Change scene every 160 frames, then loop
  // Loop: Yeosu - GrandCanyon1 - LasVegas - GrandCanyon2
  if (frameCounts <= 160) { // 160
      drawYeosu();
  }
  else if (frameCounts >= 160 & frameCounts < 320) {
      drawGrandCanyon(); // transition: light to dark sky
  } 
  else if (frameCounts >= 320 & frameCounts < 480) {
    drawLasVegas();
  }
  else if (frameCounts >= 480 & frameCounts < 640) {
    drawGrandCanyon2(); // transition: dark to light sky
  }
  else if (frameCounts === 640) {
    frameCounts = 0;
  }  
  frameCounts++;
}

// =====================================================================
// ==============================YEOSU==================================
// =====================================================================

function drawYeosu() {
  setGradient(0, 0, width, height / 2 + 70, c1, c2, yaxis); // sky
  drawSun();
  drawMountain1();
  drawMountain2();
  drawOcean();
  drawSunReflection();
  updateClouds();
  removeClouds();
  addClouds();
  setGradient(0, 390, width, 480, c3, c4, xaxis); // ground
  updateTrees();
  removeTrees();
  addTrees();
  drawMe();
  drawFog();
  updateBird();
  removeBird();
  addBird();
  writeText();
}

function setGradient(x, y, w, h, c1, c2, axis) {
  // Top to bottom gradient
  if (axis == yaxis) { 
    for (var i = y; i <= y + h; i++) {
      var inter = map(i, y, y + h, 0, 1);
      var c = lerpColor(c1, c2, inter);
      stroke(c);
      line(x, i, x + w, i);
    }
  }
  // Left to right gradient
  else if (axis == xaxis) {
    for (var i = x; i <= x + w; i++) {
      var inter = map(i, x, x + w, 0, 1);
      var c = lerpColor(c1, c2, inter);
      stroke(c);
      line(i, y, i, y + h);
    }
  }
}

function drawMountain1(){
  noStroke();
  fill(204, 157, 135); 
  beginShape(); 
  for (var x = 0; x < width; x++) {
    var t = (x * terrainDetail1) + (millis() * terrainSpeed1);
    var y = map(noise(t), 0, 1.8, height / 4 - 10, height);
    vertex(x, y); 
  }
  vertex(width, height);
  vertex(0, height);
  endShape();
}

function drawFog() { 
  for (var y = 200; y < height + 5; y++) {
    var f = map(y, height / 2 - 40, 550, 0, 100);
    stroke(255, f);
    line(0, y, width, y);
  }
}

function drawMountain2(){
  fill(147, 126, 115); 
    noStroke();
    beginShape(); 
      for (var x = 0; x < width; x++) {
            var t = (x * terrainDetail2) + (millis() * terrainSpeed2);
            var y = map(noise(t), 0, 2, height / 4 + 60, height);
            vertex(x, y); 
      }
      vertex(width,height);
      vertex(0,height);
    endShape();
}

function drawOcean() {
    fill(170, 146, 121); 
    noStroke();
    beginShape(); 
      for (var x = 0; x < width; x++) {
            var t = (x * terrainDetail3) + (millis() * terrainSpeed3);
            var y = map(noise(t), 0, 2, height - 230, height);
            vertex(x, y); 
      }
      vertex(width, height);
      vertex(0, height);
    endShape();
}

function drawSun() {
	// All sun parts based on mouseY
  	sunMouse = constrain(mouseY, 0, height / 3 + 30);
    noStroke();
    // Sunglow
    // Color less intense and size smaller as sun goes farther up
    fill(253, 254 - sunMouse, 221 - sunMouse, 20 - sunMouse*0.02);
    ellipse(width / 3, sunMouse + 40, sunMouse - 10, sunMouse - 10);
    fill(253, 254 - sunMouse, 221 - sunMouse, 20 - sunMouse*0.03);
    ellipse(width / 3, sunMouse + 40, sunMouse*1.1, sunMouse*1.1);
    fill(253, 254 - sunMouse, 221 - sunMouse, 20 - sunMouse*0.05);
    ellipse(width / 3, sunMouse + 40, sunMouse*1.2 + 10, sunMouse*1.2 + 10);
    // Sunrays
    fill(253, 254, 221, 30);
    triangle(width / 3, sunMouse + 20, width / 3 - 40, height / 3 + 90, width / 3 - 10, height /3 + 80); // left big
    triangle(width / 3, sunMouse + 20, width / 3 + 40, height / 3 + 80, width / 3 + 10, height /3 + 70); // right big
    triangle(width / 3, sunMouse + 20, width / 3 - 50, height / 3 + 90, width / 3 - 30, height /3 + 80); // left small
    triangle(width / 3, sunMouse + 20, width / 3 + 50, height / 3 + 80, width / 3 + 30, height /3 + 70); // right small
    triangle(width / 3, sunMouse + 20, width / 3 - 70, height / 3 + 70, width / 3 - 80, height /3 + 70); // left small2
    triangle(width / 3, sunMouse + 20, width / 3 + 70, height / 3 + 90, width / 3 + 80, height /3 + 80); // right small2
    // Sun
    fill(253, 254, 221, sunMouse + 30);
    ellipse(width / 3, sunMouse + 40, sunMouse - 20, sunMouse - 20);
}

function drawSunReflection() { 
	// Size of reflection on ocean corresponds to sun size/distance away
    var x = 10;
    var y = 47;
    var w = sunMouse;
    var h = 10;
    noStroke();
    // Outer darker glow
    fill(248, 172, 137, 100);
    push();
    rectMode(CENTER);
    rect(width / 3, 2 * height / 3 + y, w, h, 10);
    rect(width / 3, 2 * height / 3 + y - 13, w * 0.8, h, 10);
    rect(width / 3, 2 * height / 3 + y - 26, w * 0.6, h, 10);
    // Inner lighter glow
    fill(253, 254, 221, 50);
    rect(width / 3, 2 * height / 3 + y - 4, w * 0.9, h * 0.8, 10);
    rect(width / 3, 2 * height / 3 + y - 17, w * 0.7, h * 0.8, 10);
    rect(width / 3, 2 * height / 3 + y - 30, w * 0.5, h * 0.8, 10);
    pop();
}

function updateClouds(){
  for (var i = 0; i < clouds.length; i++) {
    clouds[i].move();
    clouds[i].display();
  }
}

function removeClouds(){
  var keepClouds = [];
  for (var i = 0; i < clouds.length; i++) {
      if (clouds[i].x - this.width / 2 < width) {
        keepClouds.push(clouds[i]);
      }
  }
  clouds = keepClouds;
}

function addClouds(){
  var newCloud = 0.01;
  if (random(0, 1) < newCloud) {
    clouds.push(drawClouds(random(width)))
  }
}

function moveClouds(){
  this.x += this.speed;
}

function displayClouds(){
  var b = random(165, 180);
  fill(255, 240, 240, 30);
  ellipse(this.x, this.y, this.width, this.height);
  ellipse(this.x + 50, this.y + 10, this.width - 20, this.height - 70);
  ellipse(this.x - 50, this.y - 5, this.width - 70, this.height - 10);
}

function drawClouds(newCloudX) {
  var cloud = {
    x: newCloudX,
    y: random(30, height / 2),
    width: random(150, 250), 
    height: random(20, 50),
    speed: random(0.5, 1.5),
    move: moveClouds,
    display: displayClouds
  }
  return cloud;          
}

function updateTrees() {
  for (var i = 0; i < trees.length; i++) { 
    trees[i].move();
    trees[i].display();
  }
}

function removeTrees() {
  var keepTrees = [];
  for (var i = 0; i < trees.length; i++) {
    if (trees[i].x < width) {
      keepTrees.push(trees[i]);
    }
  }
  trees = keepTrees;
}

function addTrees() {
  var newTree = 0.02;
  if (random(0, 1) < newTree) {
    trees.push(drawTrees(random(width)));
  }
}

function moveTrees() {
  this.x += this.speed;
}

function displayTrees() {
  // Tree height based on mouseY, mapped to a reasonable extent
  var treeMouse = map(mouseY, 0, height, height / 3, 0);
  var treeMouseCon = constrain(treeMouse, 0, height)
  // Trunk
  stroke(c4);
  strokeWeight(4);
  line(this.x, this.y + 20 - treeMouseCon, this.x, this.y + 40);
  // Leaves
  fill(c3, 20);
  stroke(c3);
  strokeWeight(7);
  strokeJoin(ROUND);
  triangle(this.x, this.y - treeMouseCon, this.x + 20, this.y + 25, this.x - 20, this.y + 25);
  // Add snow if on Grand Canyon scene
  if (frameCounts >= 160 & frameCounts < 320 || frameCounts >= 480 && frameCounts < 640) {
    fill(250);

This project was inspired by my love of taking photos, especially of nature and cityscapes. I tried recreating the feel and weather of three shots below in a looping generative landscape. Follow me! You can speed up or slow my pace using the slider and control objects like the sun and buildings using the mouse.

Scene 1 – Sunrise at Yeosu (Javascript, my photo, sketch)
Scene 2 – Snowy Grand Canyon
Scene 3 – Rainy Las Vegas

Since this doesn’t load properly on WP, access zip file here!