Final Project – Shan Wang – “Slime’s Christmas”

slime's christmas

screenshot from my game

For the final project, I mainly followed my initial design of the game similar to doodle jump, but with different features and user interface.

var platforms = [];
var allSplashes = [];
var newSplash;
var mode = "START";

var bgColor1;
var bgColor2;

var dropSpeed = 1;
const PF_THINKNESS = 7;
const GRAVITY = 1.5;
const FALLING_INTERV = 50;

var character;
var topPlatform;
var charCurrentPF;

var fallDistance = 0;
var initJump = true;

var aX;
var aY;
var xFactor = 0.3;
var yFactor = 0.15;
var timeFactor = 10;
var distance;
var numberHitted = 0;
var totalScore = 0;

//sound variables
var bgMusic;
var bellSound;

//graphic elements
var snowflakeLinks = [

var frameFilenames = [];

var snowflakes = [];
var snowflake0;
var snowflake1;
var snowGraphs = [];
var allSnowflakesObjects = [];

var wRuffles = [];

var scaleX;
var scaleY;
var snowPosX;
var snowPosY;

function preload() {
    //load sounds
    bgMusic = loadSound("");
    bellSound = loadSound("");

    //load graphs for snowfaleks
    for (var i = 0; i < snowflakeLinks.length; i++){
        var snowLink = loadImage(snowflakeLinks[i]);

    var slime = "frames/slime.gif";
    gifSlime = loadGif(slime);


function gradient(x,y,wid,hei,c1,c2){
    for (var i = y; i<y+hei; i++){
        var inter = map(i,y,y+hei,0,1);
        var col = lerpColor(c1,c2,inter);

function makeWaterRuffle(gx, gy, gr, color, alpha){
    var ruffle = {x: gx, y:gy, r:gr, c:color,
                  a: alpha, draw: drawRuffle}
    return ruffle;

function drawRuffle(){
    if (this.a >= 0){
        fill(this.c, this.c, this.c, this.a);
        ellipse(this.x, this.y, this.r, this.r);

function makeCharac(cx, cy, color, speedX, speedY){
    var character = {x: cx, y: cy, col: color, vx: speedX, vy: speedY,
                     g: GRAVITY, draw: drawCharac};
    return character;

function drawCharac(){
    image(gifSlime, this.x, this.y);
    //ellipse(this.x, this.y, this.r, this.r);
    // idx = frameCount%3;
    // img = frames[idx];

function makePlatform(px, py, pw, pc, pt, type) {
    var platform = {x: px, y:py,
                    wid:pw, color:pc,
                    hei: pt, tp: type,
                    draw: drawPlatform};
    return platform;

function drawPlatform(){
    rect(this.x, this.y, this.wid, this.hei);

function isTooClose(y){
    for (var i=0; i<platforms.length; i++){
        if (Math.abs(platforms[i].y - y)<20 ){
            return true;
    return false;

// check for collision
function hitThePlatform(platform){
    if (collideRectRect(platform.x, platform.y, platform.wid, platform.hei,
        character.x+5, character.y+5, gifSlime.width-5, gifSlime.height-5)){
            return true;
    return false;

function makeSplashes(sx, sy, number, bRadius,indiviRadius, initColor, alpha){
    var splash = {x: sx, y:sy, num: number,bigR: bRadius, r: indiviRadius,
                    color: initColor, a: alpha, draw: drawSplashes};
    return splash;

function drawSplashes(){
    translate(this.x, this.y);
    fill(this.color, this.color, this.color, this.a);
    for (var i = 0; i<this.num+1; i++){
        ellipse(0,-this.bigR, this.r, this.r);

function makeSnowflakes(image, sx, sy, scaleX, scaleY){
    var snowflake = {img: image, x: sx, y: sy, sX:scaleX, sY: scaleY,
                     draw: drawSnowflakes};
    return snowflake;

function drawSnowflakes(){
    image(this.img,this.x, this.y,this.img.width*this.sX,this.img.height*this.sY);

function setup() {
    //loop through background music
    bgColor1 = color(190,0,16);
    bgColor2 = color(83,0,0);
    createCanvas(600, 800);
    //make water ruffle patter
    for (var i = 0; i < 8; i++){
        var ranX = random(width);
        var ranY = random(height);
        var rndRuffle = makeWaterRuffle(ranX, ranY, random(30,50),random(220,255), random(50, 150));

    //make 8 snowflakes with random selection for initial condition
    for (var i = 0; i < 15; i++){
        var rndSnowflake = random(snowGraphs);

    //render snowflakes; make them into objects;
    for (var i = 0; i < snowflakes.length; i++){
        snowPosX = random(10, width - 10);
        snowPosY = random(10, height - 10);
        scaleX = random(0.1, 0.4);
        scaleY = scaleX;
        var newSnow = makeSnowflakes(snowflakes[i], snowPosX, snowPosY, scaleX, scaleY);

    // make platforms
    var counter = 0;
    //make 8 platforms that do not collide with each other
    while (counter < 8){
        var ranPosX, ranPosY, ranWidth;
        do {
            ranPosX = random(80, width-80);
            ranPosY = random(0, height/2-40);
            ranWidth = random(20, 60);
        } while (isTooClose(ranPosY));
        var newPF = makePlatform(ranPosX, ranPosY,ranWidth, 255,PF_THINKNESS, counter);
        var newShadow = makePlatform(ranPosX + 10, ranPosX + 10, ranWidth, 255,PF_THINKNESS, counter);
        counter += 1;
    character = makeCharac(width/2, height - gifSlime.height, 155, 0,0);

function draw() {
    if (mode == "START"){
        textFont("Andale Mono");
        text("Slime's Christmas", width/2-30, height/4);
        line(width/2, height/2, width/2 + 100, height/2);
        text("Start Now!", width/2, height/2);

    else if (mode == "GAME" || mode == "christmasInit"){
        //image(gifBird, -150,0, gifBird.width*0.9, gifBird.height);
        for (var i = 0; i < wRuffles.length; i++){
            wRuffles[i].r += 1;
            wRuffles[i].a -= 1;
            if (wRuffles[i].a < 0){
        if (wRuffles.length < 10){
            var ranX = random(width);
            var ranY = random(height);
            var rndRuffle = makeWaterRuffle(ranX, ranY, random(30,50),random(220,255), random(50, 150));
        for (var i = 0; i < wRuffles.length; i++){

        for (var i = 0; i < allSplashes.length; i++){
            allSplashes[i].bigR += 3;
            allSplashes[i].a -= 5;

        for (var i = 0; i < allSnowflakesObjects.length; i++){
            allSnowflakesObjects[i].y += random(dropSpeed*2, dropSpeed*2.5);
            if (allSnowflakesObjects[i].y > height){
            if (character.y < height/2){
                allSnowflakesObjects[i].y += 1;

        // set limit of speed on y direction for the character
        fallDistance += dropSpeed;
        if (character.vy > 30){
            character.vy = 30;
        } else if (character.vy < -30){
            character.vy = -30;

        // move platforms & remove those that fall out of canvas
        for (var i = 0; i < platforms.length; i++) {
            platforms[i].y += dropSpeed;
            if (platforms[i].y > height){

        // add new platforms when the old ones drop FALLING_INTERV pixels;
        if (fallDistance % FALLING_INTERV == 0){
        // add new snowflakes as needed
        if (allSnowflakesObjects.length < 15){

        // after first jump, control the velocity on both x and y direction with GRAVITY
        // to simulate the jumping and falling condition
        if (initJump == false){
            distance = mouseX - character.x;
            aY = GRAVITY;
            character.vy += aY*yFactor;
            character.x += distance/timeFactor;
            character.y += character.vy;

        // check if the player hit the platform; keeping track of scores
        if (platforms.length != 0){
            for (var i = 0; i < platforms.length; i++){
                if (hitThePlatform(platforms[i])){
                    numberHitted += 1;
                    totalScore += numberHitted*10;
                    character.vy = -8;
                    // make splashes when the player hits the platform
                    newSplash = makeSplashes(platforms[i].x + platforms[i].wid/2,
                        platforms[i].y + platforms[i].hei, 10, 50, 6, 255, 100);
                    //remove platforms that have been hit by players
        // adjust the positions of the platforms when the player jumps
        // higher than half of the canvas
        for (var i = 0; i < platforms.length; i++){
            if (character.y < height/2){
                platforms[i].y +=1;

        // bounce the player back to the canvas if the player goes off the side
        if (character.x > width & 0 < character.y < height){
            character.x = character.r;
        } else if(character.x < 0 & 0 < character.y < height){
            character.x = width - character.r;
        } else if (character.y > height){
            mode = "GAMEOVER"

    else if (mode == "GAMEOVER"){
        textSize (30);
        textFont("Andale Mono");
        text("GAME OVER!", width/2, height/2);
        textSize (15);
        text("The Steps You Jumped: " + numberHitted, width/2, height*2/3);
        text ("Total Score: " + totalScore, width/2, height*3/4);
        textSize (10);
        text("Press R to restart the game", width/2, height*5/6);

function addNewPlatforms(){
    var newX = random(40, width-40);
    var ranWid = random(40, 65);
    var newPlatForm = makePlatform(newX, 0, ranWid, 255, PF_THINKNESS);

function addNewSnowFlakes(){
    var addedSnow = random(snowflakes);
    snowPosX = random(10, width - 10);
    scaleX = random(0.1, 0.4);
    scaleY = scaleX;
    var newSnow = makeSnowflakes(addedSnow, snowPosX, 0, scaleX, scaleY);

function mouseClicked(){
    if (mode == "START"){
        if (collidePointRect(mouseX, mouseY, width/2, height/2-10, 80, 30)){
            mode = "christmasInit";
    else if (mode == "christmasInit"){
        character.vy = -8;
        if (initJump == true){
            initJump = false;
            mode = "GAME";

function keyTyped(){
    // reset the game to the initial status
    if (key == "r"|| key == "R"){
        mode = "christmasInit";
        platforms = [];
        allSplashes = [];
        snowflakes = [];
        allSnowflakesObjects = [];
        wRuffles = [];
        numberHitted = 0;
        totalScore = 0;
        initJump = true;

Different from the existing game, the platforms in my “Slime’s Christmas” will disappear if hit by the character. And the “jumping” of the slime, which follows the mouse, is simulated with gravity to give a sense of physical reality.

With what we learned in object oriented programming, I also created effect of random water ruffle and splashes that are generated at the position of the hit platform.



All music downloaded from

The slime gif credited to google image.


Shan Wang-LookingOutwards12

I got my inspirations for the final project from the two games I have played on the phone. These two projects, Doodle Jump and Mega Jump

share the similarity in the sense of moving upwards. The main purpose of the games are the same, to move higher and higher up. The main differences between these projects lie in the fundamental driving force for moving upwards, and some special features.
For Doodle Jump, the alien is constantly jumping, whereas in the Mega jump, the gold coins are the incentives for jumping up. In both of these games, the platform stays the same unless the character moves, and for my final project I am thinking about having all the platforms dropping down.
The simple format of these two projects provides enormous possibilities in adding other features to platforms, character movement and etc, which I found particularly interesting.

Shan Wang – Project Proposal

For my final project, I’m thinking about creating an interactive game similar to the mechanism of the doodle jump based on the animation and objects that we learned this semester.

With the background and platforms being generated with some randomness, all platforms are falling off the screen. Players get to control the main character (currently drawn as a scale figure in the sketch) to jump up to the surrounding platforms to keep the character alive. By pressing left or right key, the user can jump on to the adjacent platform. The game will also keep track of the score (height) that the character has jumped up.

There are some features that I’m considering adding: sound effects for jumping, background music, special platforms that will crack once the character stays over a certain amount of time.
The main focus of this project will be the algorithm that generates the platforms that space out reasonably, providing both challenges and fun at the same time.


ShanWang-Project11-(click on the canvas)

For this project I’m interested in the dynamic shapes that the turtle graphic is able to produced. I selected a spiral base shape and users can click on the canvas to generate the geometry in different sizes.


//Shan Wang
//Section A

function setup(){


function mousePressed(){
    alpha += 50;
    //randomly select the number of rotates the basic geometry will go
    var numRotate = random(30,60);
    //randomly choose the initial strokeWeight for turtle
    var strokeWei = random(0.2,1);
    //base the size of the graph on the number of rotation
    var size = map(numRotate,30,60,80,200);
    //base the initial gradient of stroke on the position of x
    var color = round(map(mouseX, 0, width, 150,200));

    //create a turtle at the position where the mouse is pressed
    var turtle1 = makeTurtle(mouseX,mouseY,color,strokeWei);

    //set the decrement unit for color and stroke weight
    var intervStro = turtle1.weight/numRotate;
    var intervCol = turtle1.color/numRotate;

    //generate the spiral
    for (var counts = 0; counts < numRotate; counts++){
        turtle1.color -= intervCol;
        turtle1.weight -= intervStro;
        //draw square
        for (var j = 0; j<4; j++){

function turtleLeft(d) {
    this.angle -= d;

function turtleRight(d) {
    this.angle += d;

function turtleForward(p) {
    var rad = radians(this.angle);
    var newx = this.x + cos(rad) * p;
    var newy = this.y + sin(rad) * p;
    this.goto(newx, newy);

function turtleBack(p) {

function turtlePenDown() {
    this.penIsDown = true;

function turtlePenUp() {
    this.penIsDown = false;

function turtleGoTo(x, y) {
    if (this.penIsDown) {
      line(this.x, this.y, x, y);
    this.x = x;
    this.y = y;

function turtleDistTo(x, y) {
    return sqrt(sq(this.x - x) + sq(this.y - y));

function turtleAngleTo(x, y) {
    var absAngle = degrees(atan2(y - this.y, x - this.x));
    var angle = ((absAngle - this.angle) + 360) % 360.0;
    return angle;

function turtleTurnToward(x, y, d) {
    var angle = this.angleTo(x, y);
    if (angle < 180) {
        this.angle += d;
    } else {
        this.angle -= d;

function turtleSetColor(c) {
    this.color = c;

function turtleSetWeight(w) {
    this.weight = w;

function turtleFace(angle) {
    this.angle = angle;

function makeTurtle(tx, ty, tColor,tStroke) {
    var turtle = {x: tx, y: ty,
                  angle: 0.0,
                  penIsDown: true,
                  color: tColor,
                  weight: tStroke,
                  left: turtleLeft, right: turtleRight,
                  forward: turtleForward, back: turtleBack,
                  penDown: turtlePenDown, penUp: turtlePenUp,
                  goto: turtleGoTo, angleto: turtleAngleTo,
                  turnToward: turtleTurnToward,
                  distanceTo: turtleDistTo, angleTo: turtleAngleTo,
                  setColor: turtleSetColor, setWeight: turtleSetWeight,
                  face: turtleFace};
    return turtle;

This is one of the image I created.

Sample Image

ShanWang-LookingOutwards 11

Stanford Laptop Orchestra (SLOrk) performing

I found the TED talk of Ge Wang on computer music to be extremely inspiring. His projects are mainly on the implementation of of programming languages in interactive software design for computer, mobile and social music. Demonstrated in this video, one of his focus is building instruments for the Stanford Laptop Orchestra, in which he utilizes “Chuck”—the programming language he developed— to create ensembles of laptops, humans and special hemispherical speaker made from wood bowls. The instruments provide acoustic feeling to the computer generated music, and I saw great potential in the performances that the players can do as they adjust the gesture of playing.

The Smule app created by Ge Wang is also interesting in terms of user experiences such as actually blowing to the microphone of iPhones to play music. Both of these projects demonstrated possibilities in bringing music to everyone that has no access or cannot afford different kinds of instruments.



//Shan Wang
//Section A

var terrainSpeed = 0.001;
var terrainDetail = 0.05;

var birds = [];
var stars = [];

function setup() {

    //create initial set of birds
    for (var i=0; i < 10; i++){
        var bX = random(width);
        var bY = random(height/8,height/5);
        birds[i] = makeBird(bX, bY);

    //create initial set of stars
    for (var j=0; j < 30; j++){
        var x = random(width);
        var y = random(height*3/4);
        stars[j] = makeStars(x,y);


//draw gradient background
function gradient(x,y,wid,hei,c1,c2){
    for (var i = y; i < y+hei; i++){
        var inter = map(i,y,y+hei,0,1);
        var col = lerpColor(c1,c2,inter);


function gradientLine(x,y,len,c1,c2){
    for (var i = y; i < (y+len); i+=12){
        var interv = map(i,y,y+len,0,1);
        var colM = lerpColor(c1,c2,interv);

// move and draw the stars
function updateAndDisplayStars(){
    for (var s = 0; s < stars.length; s++){

function makeStars(sX,sY){
    var star = {x:sX,
                speed: random(0,1),
                move: moveStar,
                drawS: drawStars}
    return star;

//draw every star at its current position
function drawStars(){

//move the star at a low speed
function moveStar(){
    this.x += this.speed;

//only keep the stars that are still in the canvas, update the array
function removeStars(){
    var keepStars = [];
    for (var i=0; i < stars.length; i++){
        if ((0 < stars[i].x < width) & (0 < stars[i].y < height)){
    stars = keepStars;

//add stars if the number of array is less than 30
function addStars(){
    if (stars.length < 30){
        var stX = random(width);
        var stY = random(height/2);

function makeBird(birdX,birdY){
    var bird = {x:birdX,
                speedX: random(5,15),
                speedY: random(-10,5),
                spanX: random(5,10),
                spanY: random(2,5),
                stroke: 0,
                fly: birdFly,
                drawB: drawBird}
    return bird;

//move the bird on both x and y direction
function birdFly(){
    this.x += this.speedX;
    this.y += this.speedY;

//draw the bird, the stroke weight is based on the size of the wing
function drawBird(){
    this.stroke = map(this.spanX,5,10,1,3);
    line(this.x-3, this.y-5,this.x-3-this.spanX,this.y-5-this.spanY);
    line(this.x+3, this.y-5,this.x+3+this.spanX,this.y-5-this.spanY);

//call the functions that move and draw each bird
function updateAndDrawBirds(){
    for (var i = 0; i < birds.length; i++){

//only keep birds that are still on the canvas, update array accordingly
function removeBirds(){
    var keepBirds = [];
    for (var i=0; i < birds.length; i++){
        if ((0 < birds[i].x < width) & (0 < birds[i].y < height)){
    birds = keepBirds;

//randomly add birds at a low probability
function addBirds(){
    var prob = 0.3;
    if (prob>random(0,1)){

function draw() {
    //draw background gradient
    var bgColor2 = color(200, 219, 216);
    var bgColor1 = color(99,112,145);

    //display star groups

    //set mountain colors
    var mCol1 = color(250,238,222);
    var mCol2 = color(31,43,98);

    //set terrain colors
    var wCol1 = color(35,45,101);
    var wCol2 = color(97,126,180);

    //draw mountain and waves with different variation of Perlin noise
    for (var x = 0; x < width; x++) {
        var t1 = (x * terrainDetail/5) + (millis() * terrainSpeed/2);
        var t2 = (x * terrainDetail/10) + (millis() * terrainSpeed*4);
        var y1 = map(noise(t1), 0,1, 0, height*2/3);
        var y2 = map(noise(t2), 0,1, height*3/4, height*4/5);
        //draw gradient mountain
        gradientLine(x,y1,height-y1,mCol2, mCol1);
        //draw gradient water waves
        gradientLine(x,y2,height-y2,wCol1, wCol2);

    //display birds group

For this project I wanted to create the view of looking at the mountains and river passing by under the sky with stars.

I explored the use of gradient in rendering the mountain and water waves, but because iterating through every single pixel is slowing down the movement and making the animation extremely slow and incoherent. So I decided to render every 12 pixels of each vertical line with the same color to reduce the load on the algorithm.

I made the birds and stars into objects that can be easily controlled with different attributes.

[Note: this post was edited by RBD on Nov 5, 9:25am to work around some WordPress problems. Please do not take off any late points. I’ll post a reply on Piazza as well.]



example 3D printed watch band for Moto 360 Smartwatch


Tactum: the user interacting with the projection

I found the Tactum project created by MADLAB.CC to be extremely inspiring. Madeline Gannon is the head of the MADLAB.CC, which is a research studio specializing in integrating design and computational tools to develop interfaces for crafting wearables on and around the human body.

Madeline has background in architecture and computation design, and is currently PhD candidate at Carnegie Mellon University.

Tactum project specifically dealt with the manipulation of interactive digital geometry on user’s skin. The gestures used within Tactum are designed to be natural and intuitive for users. By poking, pinching or rubbing users can customize their own wearable within the embedded 3D printing fabrication constraints in the programmed geometry. The project was implemented with the combination of CAD backend for projection, existing 3D SCAN and 3D printer.

I’m particularly intrigued by the potential extended usage for this technology, and hope for the improvement on the precision.



//Shan Wang
//Section A

var distribution = 360;
var disArray = [];

//load image
function preload(){
    var imgURL = "";
    underlyingImage = loadImage(imgURL);

function setup() {
    createCanvas(600, 700);
    //resize the image to fit canvas

function drawSplash(x,y,color){
    //create the splash at the given position
    for (var i = 0; i < disArray.length; i++) {
        var dist = abs(disArray[i]);
        line(0, 0, dist, 0);


function draw() {
    var px = random(width);
    var py = random(height);
    var ix = constrain(floor(px), 0, width-1);
    var iy = constrain(floor(py), 0, height-1);
    var theColorAtLocationXY = underlyingImage.get(ix, iy);
    //reset the array to empty for every frame;
    disArray = [];
    //store in total 360 lines radiating from one center for splash;
    for (var i =0; i<distribution; i++){
        var size = random(2,4);
    drawSplash(ix, iy, theColorAtLocationXY);


function mouseDragged(){
    disArray = [];
    //store in total 360 lines radiating from one center for splash;
    for (var i =0; i<distribution; i++){
        var size = random(2,4);
    //get the color at the mouse position
    var theColorAtTheMouse = underlyingImage.get(floor(mouseX), floor(mouseY));
    //draw the splash


I was exploring the how I can achieve a splashing effect for each point that the image renders. I use the randomGaussian command and tested out the relatively reasonable range. The overall aesthetic sensibility of this computational portrait kind of resembles the view that one will see through the car window when it’s rainy, and I found the result pretty interesting.

I also added the mouseDragged function just so user can see the image faster if they don’t want to wait for the automatic render.


render example




LifeObject Installation

After browsing all the interesting post by my peers, I’m particularly drawn to the LifeObject project introduced by Yugy. This Israeli Pavilion is an architectural installation at  2016 Venice Biennale, that embodies the resilient properties of a bird’s nest. Curators of this project include Dr. Ido Bachelet, Bnaya Bauer, Arielle Blonder, Dr. Yael Eylat Van Essen, and Noy Lazarovich.

Specially, I’m fascinated by the spatial architectural forms generated through the scientific analysis, which materializes the abstract ideas into a architectural senses of thinness and volume.

I agree with my peer’s insight about this project bridging architecture and biology. The computational design of the fabrication process enables the realization of natural quality and randomness of biological characteristics. And the desired animation of the organic nest is successfully manifested through the dynamic yet inorganic material.


Israel Pavilion



Hyphae Crispata in Growing Objects


Kinematics Dress

Jessica Rosenkrantz and Jess Louis-Rosenberg are the co-founders of Nervous System in 2007, a design studio that applies cutting-edge technologies in design through scientific research and computational graphics. Jessica graduated from MIT with degrees in architecture and biology, while Jesse also attended MIT majoring in mathematics, and he previously worked as consultant in building modeling and design automation.

What’s fascinating about their works is the degree that they employ the computational design and digital fabrication in products that can be manufactured. A lot of their projects such as the Growing Objects draw inspirations from the forms and biological pattern of plants. During their presentation, they illustrated the process of abstracting essential forms and adding complexity and details through algorithm. Different experimentations and simulations had been done to produce the dynamic and interactive system that generates one of a kind for each product.

The 3D printed textile for the Kinematic project is also interesting because of the 2D flatness of each module in contrast to the degree of customization, variation in three dimensions that conform body curve, and the flexibility of movement in the final dress.

Eyeo 2015 – Jesse Louis-Rosenburg and Jessica Rosenkrantz from Eyeo Festival // INSTINT on Vimeo.