shuann-Body

Link to app: https://detailed-feet.glitch.me/

GIF:

Sketch:

My original idea was pretty different from what I eventually finished. Originally I wanted to create a drawing tool that one can draw with his/her pupils. The line would trace where the eye had been and the entire canvas would erase itself if the person blinks. However, I was having trouble to distinguish different state of the eyes with the face tracking templet code and decided to go to a different direction.

Then the new idea I had was to use the cereal head movement to control a basic character. I first thought of the more conventional multiple level of floors that a character could be placed into by change the vertical position of the head. However I realized that did not make much sense since I want to use head turn to trigger "jump". The left&right motion does not naturally map to the up&down motion of the jump. Therefore I redesigned the floor to be vertical and have the character run up the screen to avoid obstacles. That been said, this not a game yet as it does not have any level design, no goals to achieve, etc. As it for now, it's more like an exploration on the kind of controllers that can be implemented. If there is more time, I would like to turn this into a game that two people can play against each other at the same time.

Still Image:

 Video:

var local;
var obs = [];
var dir = [1, -1];
var player1 = null;
var stuck = false;
var score1 = 0;
var vel = 8;
 
var from;
var to;
var bgcX = 0;
 
function setup() {
 
  createCanvas(640, 480);
  background(0);
 
  //this is where posenet initlizes and webcam stuff happens
  PoseZero.init();
 
  //this is you, check out agent.js for adding properties and functions
  local = new Agent();
 
  player1 = new player(width/2,height*2/3,random(dir));
  obs[0] = new obstacle(width/2, height/2, random(15,21), random(dir));
  obs[1] = new obstacle(width/2, height/3, random(15,21), random(dir));
 
}
 
function draw() {
  background(255);
 
	local.update(PoseZero.get());// update your skeleton
 
  push();
  if (local.data.pose != null){ 
    push();
    if (player1.dir === 1){
      bgcX = width/2;
      from = color(255,100,147);
      to = color(255,182,193);
    }
    if (player1.dir === -1){
      bgcX = 0;
      from = color(255,182,193);
      to = color(255,100,147);
    }
    noStroke();
    fill(150);
    var widthRect = width/40;
    noStroke();
    for (var i=0;i<20;i++){
      inter = lerpColor(from,to,(i+1)*(1/20));
      fill(inter);
      rect(bgcX + i*widthRect,0,widthRect,height);
    }
    pop();
 
    PoseZero.draw_pose(local.data.pose,{color:local.data.color})
 
    if (player1.dir === 1){
      text("shake your head RIGHT" , width/2 + 170, height - 20);
    }
    if (player1.dir === -1){
      text("shake your head LEFT ", 20, height - 20);
    }
 
    push();
    drawPlayground();
 
    palceObs();
    boundaryCheck();
    speedCheck();
 
    if (player1.stuck() === false){
      for (var elem = 0; elem < obs.length; elem ++){
        obs[elem].draw();
        obs[elem].move();
      }
    } else {
      for (var elem = 0; elem < obs.length; elem ++){ obs[elem].draw(); } } player1.draw(); player1.checkDir(); player1.headTurn(); pop(); } pop(); } function palceObs(){ if (frameCount % 15 == 0){ if (obs[obs.length - 1].y != -20){ obs.push(new obstacle(width/2, -20, random(15,21), random(dir))); } } } function boundaryCheck(){ if (obs[0].y >= height){
    obs.shift();
  }  
}
 
function speedCheck(){
  var closeness = PoseZero.estimate_scale(local.data.pose);
 
  if (closeness <= 70 && closeness > 40) {
    vel = 8;
  }
 
  if (closeness <= 100 && closeness > 70) {
    vel = 11;
  }
 
  if (closeness <= 120 && closeness > 100) {
    vel = 14;
  }
}
 
function drawPlayground(){
  var road = width/2;
  strokeWeight(4);
  line(road, 0, road, height);
 
}
 
function obstacle(x,y,s,dir){
  this.x = x;
  this.y = y;
  this.dir = dir;
  this.s = s;
  this.r = int(random(240,255));
	this.g = int(random(30,200));
	this.b = int(random(50,69));
 
  this.draw = function(){
    push();
    stroke(0);
		strokeWeight(4);
		fill(this.r,this.g,this.b);
    if (this.dir == 1){
      rect(this.x, this.y, this.s * 4, 20, 30);
      for (var a=0; a<4; a++){
        push();
        stroke(250);
			  line(this.x + 6 + 15*a, this.y + 3, this.x + 15*a + 15, this.y + 17);
        pop();
		  }
    } else {
      rect(this.x - this.s * 4, this.y, this.s * 4, 20, 30);
      for (var b=0; b<4; b++){
        push();
        stroke(250);
			  line(this.x + 6 + 15*b - this.s * 4, this.y + 3, this.x + 15*b + 15 - this.s * 4, this.y + 17);
        pop();
		  }
    }
    pop();
  }
 
  this.move = function(){
    this.y += vel;
  } 
}
 
function player(x,y,dir){
  this.x = x;
  this.y = y;
  this.dir = dir;
  this.h = 50;
  this.w = 30;
  this.leg = 15;
  this.vel = 10;
  var triX;
  var jumpR = false;
  var toppedR = false;
  var jumpL = false;
  var toppedL = false;
 
  this.draw = function(){
    // console.log(this.dir);
    if (this.dir == -1) {
      triX = this.x - this.leg;
    } else {
      triX = this.x + this.leg;
    }
    push();
    strokeJoin(ROUND);
    strokeWeight(4);
    stroke(255);
    fill(255, 209, 124);
    line(triX + this.h*3/4 * this.dir, this.y + this.w/2, this.x + this.dir * 5, this.y + this.w/2);
    triangle(triX, this.y, triX + this.h * this.dir, this.y + this.w/2, triX, this.y + this.w);
    ellipse(triX + this.h*3/4 * this.dir, this.y + this.w/2, this.leg*2 , this.leg*2);
    stroke(0);
    line(triX + (this.h*7/8 - 4) * this.dir, this.y + this.w/2 - 6, triX + this.h*6/8 * this.dir, this.y + this.w/2 - 6);
    line(triX + (this.h*7/8 - 4) * this.dir, this.y + this.w/2 + 6, triX + this.h*6/8 * this.dir, this.y + this.w/2 + 6);
    pop();
  }
 
  this.checkDir = function(){
    var playerPos = PoseZero.nosePosX(local.data.pose);
 
    if (playerPos < width/2) {
      this.dir = -1;
    } else {
      this.dir = 1;
    }
  }
 
  this.headTurn = function(){
    //check for jumps
    if (PoseZero.rightTurn(local.data.pose) && this.dir == 1){
      jumpR = true;
    }
    if (PoseZero.leftTurn(local.data.pose) && this.dir == -1){
      jumpL = true;
    }
 
    if (jumpR === true){
      this.jumpRight();
    } 
    if (jumpL === true){
      this.jumpLeft();
    }
  }
 
  //Right jump animation
  this.jumpRight = function(){
    if (toppedR === false && this.x < width/2 + 100){ this.x += this.vel; if (this.x >= width/2 + 100) {
        toppedR = true;
      }
    } 
    if (toppedR === true && this.x != width/2){
      this.x -= this.vel;
      if (this.x <= width/2) { this.x = width/2; jumpR = false; toppedR = false; } } } //Left jump animation this.jumpLeft = function(){ if (toppedL === false && this.x > width/2 - 100){
      this.x -= this.vel;
      if (this.x <= width/2 - 100) { toppedL = true; } } if (toppedL === true && this.x != width/2){ this.x += this.vel; if (this.x >= width/2) {
        this.x = width/2;
        jumpL = false;
        toppedL = false;
      }
    }
  }
 
  this.stuck = function(){
  //check for obstacles
    for (var elem = 0; elem < obs.length; elem ++){
      var obsTop = obs[elem].y;
      var obsBottom = obs[elem].y + 20;
      var obsOuter = obs[elem].x + obs[elem].s * obs[elem].dir * 4;
      var obsInner = obs[elem].x;
      if (obs[elem].dir == this.dir && obsTop < this.y && obsBottom >= this.y){
        if (this.dir === 1 && this.x <= obsOuter){ return true; } if (this.dir === -1 && this.x >= obsOuter){
          return true;
        }
      }
    }
    return false;
  }
}

 

shuann-LookingOutwards03

The project I selected is a kinematic maze called Landscape Abbreviated created by Nova Jiang. I think the interaction here is extremely interesting because, unlike many other interactive work, the initiative is held by the art work itself. The installation purposefully fills the room so that the visitor has to walk through the maze before going to the next room. Yet which specific path they will eventually end up taking is determined by the installation itself. The modular elements that consist the work can individually turn at different angles and different time thus forcing the visitors to his/her unique path. The simple yet effective unpredictability of this piece makes it an extremely intriguing work to interact with.

Link to work:http://www.novajiang.com/projects/landscape-abbreviated/

shuann-telematic

Some actions you can take:

  1. Type any key, and the program will randomly select a word that begins with the letter that you specified, from the list of 100 most used words, and place it at your mouse position.
  2. You can change the font size either by holding down the key, or by changing the slider located at the top left.
  3. You can also drag with your mouse to quickly place down a number of the same word that you just typed onto the canvas.

This project is about: Collectively create a text based collage that is based on realtime back-and-forth conversation.

Some screenshots:

    

Originally, I wanted to create something more complex. Allowing users to type a word/phrase and see a preview of that on screen, following their mouse. Then they can click to place the text onto the canvas. However I was not able to get that working thus I tweaked my idea on the fly.  Now with this simplified version, with every key press, a random word will be selected based on the 100 most frequently used word. Thus, it is a play on the conversation itself by experimenting with interrupted communication and what we can get out of those talks, if anything. Although it is possible to communicate with this tool, it takes some effort in deciphering or working around.

Idea sketch:

Although I am not really satisfied with my final result, the good thing is that I am still able to address a fair amount of my original idea. For instance, I wanted the conversation to be anonymous and thus focus people on the words but not dig into who said those and if there is any deeper meaning to those words. I wanted to see the users perceive words more as a pattern. I also had no idea how html, css or socket.io works so it took me a long time to understand what the code is doing and how are values been communicated back and forth. I think I began to grasp the central concepts of how these things work which I considered a plus from my own perspective.

I also tried to incorporate some physics using matter.js into this project so that when a user left the chat, all the word that he/she entered would free-fall towards the bottom of the screen. However I didn't have enough time to figure out how I can apply it onto texts. But I think adding that element in would defiantly make this piece more interesting.

shuann-LookingOutwards02

Sorry I just realize I did not publish this properly. 🙁

Link to the work: http://www.av-controls.com/#/lull/

 

This project is an immersive installation named "Lull". It creates the illusion of an immersive surreal environment for the visitors by cascading visuals on translucent fabric walls combined with the help of dense fog, which I think is a very cleaver decision that they made. Through this new way of projection, the artists are able to bring late, 2d visuals into the third dimension and allow visitors to literally immerse themselves into the art work.
I suppose the artist had to experiment a lot with the fabric walls to finally get the effect that they wanted. It also seems like the passion of the projectors and their own movements are are also something that needed a lot of tweaking.
To quote the artists " Simple rules shape this ever-evolving animation, giving rise to organic abstracted patterns with complex behavior that teeter between order and chaos". This point came across quiet nicely with the final installation as it was able to a dreamy space just using very simple, organic shapes.

shuann-viewing04

Spectacle: Use avant-garde technologies in a work merely for what it is able to achieve, both inters of complicity and novelty. Thus this kind of work often lacks content outside of where it has been applied.

Speculation: Use a range of technology to create works that are socially engaged. Speculation often engages itself into a conversations that commercial works could never talk about, because of it's impracticality.

Based on Warburton's argument, this work would be qualified as a spectacle. As a commercial advertisement that is trying to sell customers their need to own a new piece of avant-garde technology, it is produced only to showcase the capabilities of the device. Beyond that, there is really not much that can be said about it. In other words, the technology here is highly visible which perfectly compliments the central message of this video. However, the work lacks context otherwise and would never work as a stand-alone piece.

shuann-clock

I had a lot of trouble coming up with ideas, but I really liked one of the ancient time keeping tool that only tracked time to the hours. In addition I also wanted to crate a clock that can blends into the everyday routine that you shouldn't need to pay much attention to.  I was then inspired by "rainy mood (https://rainymood.com/)" which I listen to while working as the rain sound can keep me calm and concentrated. Thus I decided to make this clock that is all about that. It is designed so that you don't really need to pay much attention to the visuals. For every hour that passed by, there will be a thunder sound to remind you that it's now the next hour.

I am overall satisfied with the final product. I originally imagined this to be a full scale projection that blends into a wall to create the illusion of a rainy day. However, I had to scale it down due to 1). time constraints and 2). the style of computer graphics might not be the best to create that illusion(?).

Sketch:

var rain = [];
var offset = 30;
var s;
var m;
var h;
var preH=-1;
var sizeY = 10;
var sizeO = 9;
 
 
function preload() {
    rainSound = loadSound("rain.ogg");
    thunder = loadSound("thunder.ogg");
}
 
function setup() {
  createCanvas(500,500); 
  frameRate(30);
  rainSound.loop();
}
 
function draw() {
  drawBackground();
  s = second();
  m = minute();
  h = hour();
 
  if (h != preH){
    thunder.play();
    preH=h;
  }
 
  if (frameCount % 10 == 0){
    createNewDrop();
  }
 
  push();
  for (var elem=0; elem < rain.length; elem++){
    rain[elem].move();
    if (rain[elem].bound() && elem != rain.length-1){
      rain.splice(elem,1);
    }
    rain[elem].render();
  }
  pop();
 
  drawWall();
  drawCandle();
}
 
function drawCandle(){
  //plate
  push();
  translate(width*2/3-20, height*2/3-20);
  fill("lightpink");
  noStroke();
  rect(-80,0,160,7);
  ellipse(0,7,160,20);
  fill("pink");
  ellipse(0,0,160,20);
  pop();
 
  push();
  translate(width*2/3-20, height*2/3-20);
 
  noStroke();
  fill("offWhite");
  if(h < 12){// candle length mapped to miuntes && burn for 12h each
    //candle1
    var c1 = map(h*60+m, 0, 720, -60, -5);
    ellipse(-30,c1-3,50,10);
    rect(-55,c1-3,50,-c1);
    ellipse(-30,-3,50,10);//stationary
    stroke(0);
    strokeWeight(2);
    line(-30,c1-3,-30,c1-7);
    drawFire(-30,c1-7);
 
    //candle2
    noStroke();
    ellipse(25,-58,50,10);
    rect(0,-58,50,60);
    ellipse(25,2,50,10);//stationary
    stroke(0);
    strokeWeight(2);
    line(25,-58,25,-62);
 
 
  } else {
    ellipse(-30,-8,50,10);
    rect(-55,-8,50,5);
    ellipse(-30,-3,50,10);//stationary
    stroke(100);
    strokeWeight(2);
    line(-30,-8,-30,-12);
 
    var c2 = map(h%12*60+m, 0, 720, -60, -5);
    noStroke();
    ellipse(25,c2+2,50,10);
    rect(0,c2+2,50,-c2);
    ellipse(25,2,50,10);//stationary
    stroke(0);
    strokeWeight(2);
    line(25,c2+2,25,c2-2);
    drawFire(25,c2-2);
  }
  pop();
}
 
function drawFire(x,y){
  push();
  translate(x,y);
  noStroke();
  fill(225,243,94,100);
  if (s%2 == 0){
    sizeY += 0.5;
    sizeO += 0.1;
  } else{
    sizeY -= 0.5;
    sizeO -= 0.1;
  }
  ellipse(0,-4,8,sizeY);
  fill(231,122,19,200);
  ellipse(0,-3,7,sizeO);
  fill(241,60,47,240);
  ellipse(0,-2,4,7);
 
  pop();
}
 
function drawBackground(){
  background(150);
  var r0 = 0;
  var g0 = 76;
  var b0 = 153;
  var r1 = 70;
  var g1 = 130;
  var b1 = 180;
  for (var i=0; i < 30; i++){
    noStroke();
    var r = map(i,0,30,r0,r1);
    var g = map(i,0,30,g0,g1);
    var b = map(i,0,30,b0,b1);
    fill(r,g,b);
    rect(0,i*10,width/2+offset*2-5,10);
  }
}
 
function drawWall(){
  push();
  fill(	251, 241, 224);
  noStroke();
  rect(width/2+offset*2,0,width/2+30,height);
  rect(0,height*2/3-38,width,height/2);
 
  push();
  // fill(245,232,202);
  fill(220,210,180);
  translate(width/2+offset*2,0);
  quad(0,0,20,0,20,299,0,290);
  pop();
 
  push();
  fill(255,250,250);
  translate(0,height*2/3-45);
  quad(0,0,width/2+75,0,width/2+80,10,0,10);
  pop();
 
  pop();
  drawFrame();
}
 
function drawFrame(){
  push();
  fill(92,53,33);
  noStroke();
  quad(width/2+offset*2-5,0,width/2+offset*2+15,0,
      width/2+offset*2+15,height*2/3-45,width/2+offset*2-5,height*2/3-60);//right
  fill(97,62,38);
  quad(0,height/2+23,width/2+offset*2-5,height/2+23,
      width/2+offset*2+16,height/2+39,0,height/2+39);//bottom
  pop();
 
  drawStand();
  drawFlower();
  drawBucket();
}
 
function drawBucket(){
  push();
  translate(50,height*0.8);
  strokeWeight(5);
  stroke(80);
  noFill();
  ellipse(0,0,160,20);
 
  push();
  strokeWeight(10);
  stroke(161,122,99);
  strokeJoin(ROUND);
  beginShape();
  curveVertex(60,-60);
  curveVertex(60,-60);
  curveVertex(70,-90);
  curveVertex(78,-100);
  curveVertex(90,-103);
  curveVertex(100,-100);
  curveVertex(104,-94);
  curveVertex(105,-90);
  curveVertex(105,-85);
  curveVertex(103,-85);
  endShape();
  //umbrella
  noStroke();
  scale(1.5);
  translate(-25,0);
  fill(119,191,225);
  triangle(40,-50,70,-40,0,200);
  fill(107,172,229);
  triangle(45,-50,75,-40,0,200);
  fill(115,185,247);
  triangle(50,-50,80,-40,0,200);
  fill(117,199,252);
  triangle(60,-48,80,-42,0,200);
  fill(115,185,247);
  triangle(70,-44,90,-35,0,200);
  pop();
 
  line(80,0,70,100);
  line(80,0,70,5);
  line(70,5,60,8);
  line(60,8,52,100);
  line(60,8,50,9);
  line(50,9,40,9.5);
  line(40,10,35,100);
  line(40,9.5,20,9.5);
  line(20,10,18,100);
  line(20,9.5,10,9.5);
  line(0,10,0,100);
  line(-20,10,-18,100);
  line(-40,9,-35,100);
  pop();
}
 
function drawStand(){
  push();
  translate(width/3-20,height/2+offset*2);
  noStroke();
  fill(216,200,225);
  quad(20,0,380,0,400,30,0,30);
  fill(216,191,216);
  rect(0,30,400,15);
  fill(201,186,209);
  rect(10,45,380,250);
 
  //drawer1
  fill(220,204,209); 
  quad(50,60,430,60,420,70,55,70);
  quad(55,130,430,130,430,135,50,135);
  fill(242,224,230);
  quad(50,60,55,70,55,130,50,135);
 
  //drawer2
  fill(220,204,209);
  quad(50,160,430,160,420,170,55,170);
  fill(242,224,230);
  quad(50,160,55,170,55,230,50,240);
  pop();
}
 
function drawFlower(){
  push();
  //draw stem
  translate(width/2+170,height/3+25);
	stroke(52,131,34);
	strokeWeight(4);
	line(-20,-100,55,128);
	line(5,-30,10,-50);
	line(10,-50,50,-90);
	line(10,-50,12,-90);
  line(23,30,60,-40);
	line(10,-9,-70,-60);
	line(-33,-35,-60,-20);
	line(-20,-100,-50,-120);
	line(-20,-100,-20,-130);
 
	//draw buds 
	noStroke();
	fill(220,182,225);
	star(10,-50, 10, 20,7);
	star(50,-90, 13, 25,7);
	star(12,-90, 13, 25,7);
	star(60,-40, 13, 25,7);
	star(-70,-60, 13, 25,7);
	star(-33,-35, 10, 20,7);
	star(-50,-120, 10, 20,7);
	star(-25,-80, 13, 25,7);
	star(-15,-115, 13, 25,7);
	star(0,-15, 13, 25,7);
	star(-60,-20, 10, 20,7);
 
	fill("offwhite");
	ellipse(10,-50,7);
	ellipse(50,-90,7);
	ellipse(12,-90,7);
	ellipse(60,-40,7);
	ellipse(-70,-60,7);
	ellipse(-33,-35,7);
	ellipse(-50,-120,7);
	ellipse(-25,-80,7);
	ellipse(-15,-115,7);
	ellipse(0,-15,7);
	ellipse(-60,-20,7);
	pop();
 
	//draw vessel
	push();
	fill(198,228,255,200);
	noStroke();
	rect(width/2+170,height/3+25,60,130);
	ellipse(width/2+200,height/3+155,60,10);
	pop();
}
 
function createNewDrop(){
  for (var i = 0; i < 10; i++){
    var newDrop = new rainDrop(int(random(-20,width/2+offset*2)), int(random(-20,height/3)));
    rain.push(newDrop);
  }
}
 
function rainDrop(x, y){
  this.x = x;
  this.y = y;
  this.s = 1;
  this.pos = [[this.x, this.y]];
  this.velX = int(random(1,3));
  this.velY = 2;
 
  this.render = function(){
    stroke(204,229,255);
    if (this.pos.length == 1) {
      ellipse(this.x, this.y, this.s, this.s);
    } else {
      for (var i=1; i<this.pos.length; i++){
        strokeWeight(this.s);
        line(this.pos[i-1][0], this.pos[i-1][1], this.pos[i][0], this.pos[i][1]);
      }
    }
  }
 
  this.move = function() {
    this.x += this.velX;
    this.y += this.velY;
    this.pos.push([this.x, this.y]);
 
    if (random() < 0.8){ this.velX = int(random(2)); } else { this.velX = int(random(-2,3)); } this.checkLength(); } pop this.checkLength = function() { if (this.pos.length > 15){
      this.pos.shift(0);
    }
  }
 
  this.bound = function() {
    if (this.pos[0][0] > width/2+offset*2 || this.pos[0][1] > height*2/3-38){
      return true;
    } else {
      return false;
    }
  }
}
 
//taken from:https://p5js.org/examples/form-star.html
function star(x, y, radius1, radius2, npoints) {
  var angle = TWO_PI / npoints;
  var halfAngle = angle/2.0;
  beginShape();
  for (var a = 0; a < TWO_PI; a += angle) {
    var sx = x + cos(a) * radius2;
    var sy = y + sin(a) * radius2;
    vertex(sx, sy);
    sx = x + cos(a+halfAngle) * radius1;
    sy = y + sin(a+halfAngle) * radius1;
    vertex(sx, sy);
  }
  endShape(CLOSE);
}

 

shuann-Reading03

To me personally, I definitely value "First Word Art" more, not only because of it's originality but also because it's ability to shape what art will follow closely after. First of all, since there isn't really an expiration date for art, as more and more works are created, it only becomes harder for any artist to come up with something that is considered "new". Thus, even the attempt in creating First Word Art is in itself a accomplishment. More importantly, if a new First Word Art had been created, that means it will set the new standard for the works that come after. It will become the source of comparison that everything else is judged against. What this means is that, whether intended or not, First Word Art is empowered to shape the culture at least within the artistic realm. Therefore, in that sense I believe First Word Art is more valuable although both First Word Art and Last Word Art are important forms of art.

shuann-AnimatedLoop

Overall I am pretty satisfied with how this turned out, and it looks very close to what I had on my sketch. For the background switch I decided to use the EaseInOutElastic function after trying out a hand full of different easing functions because this one looks almost exactly like what I imagined the transition to be when I made my sketch. I believe the bounciness really suites the style of the visuals that I created here and defiantly adds to the overall quality of the piece. The hardest part about this assignment was defiantly the fact that I had to think and calculate everything based on the percentage frame rate. It was not so easy to wrap my head around that and when I ran into a glitch I have to really sit down and think about where the problem is. Did I lose count on the frame somewhere or was it something else. For example this one:

I also wish that I can spend more time to make the movement of the boat more natural. Maybe with some tilt at appropriate time.s

My Sketch:

The Code:

// INSTRUCTIONS FOR EXPORTING FRAMES (from which to make a GIF): 
// 1. Run a local server, using instructions from here:
//    https://github.com/processing/p5.js/wiki/Local-server
// 2. Set the bEnableExport variable to true.
// 3. Set the myNickname variable to your name.
// 4. Run the program from Chrome, press 'f'. 
//    Look in your 'Downloads' folder for the generated frames.
// 5. Note: Retina screens may export frames at twice the resolution.
 
 
//===================================================
// User-modifiable global variables. 
var myNickname = "shuann";
var nFramesInLoop = 120;
var bEnableExport = true;
var myScale = 0.006;
var myLoopingNoiseArray0 = [];
var myLoopingNoiseArray1 = [];
var myLoopingNoiseArray2 = [];
var myLoopingNoiseArray3 = [];
var myLoopingNoiseArray4 = [];
var myLoopingNoiseArray5 = [];
var myLoopingNoiseArray6 = [];
var myLoopingNoiseArray7 = [];
var myLoopingNoiseArray8 = [];
var myLoopingNoiseArray9 = [];
 
// Other global variables you don't need to touch.
var nElapsedFrames;
var bRecording;
var theCanvas;
 
//===================================================
function setup() {
  theCanvas = createCanvas(640, 640);
  bRecording = false;
  nElapsedFrames = 0;
 
  var radius = 60;
  noiseSeed(40);
  //waves at the bottom
  //sorry the style is horrible
  for (var i = 0; i < nFramesInLoop; i++){
    var rotatingArmAngle = map(i, 0 ,120, 0, TWO_PI);
    var px = width/2 + radius * cos(rotatingArmAngle);
    var py = height/2 + radius * sin(rotatingArmAngle);
    var noiseAtLoc0 = height - 50 * noise(myScale * px, myScale * py);
    var noiseAtLoc1 = height - 80 * noise(myScale * px, myScale * py);
    var noiseAtLoc2 = height - 150 * noise(myScale * px, myScale * py);
    var noiseAtLoc3 = height - 200 * noise(myScale * px, myScale * py);
    var noiseAtLoc4 = height - 300 * noise(myScale * px, myScale * py);
    myLoopingNoiseArray0[i] = round(noiseAtLoc0);
    myLoopingNoiseArray1[i] = round(noiseAtLoc1);
    myLoopingNoiseArray2[i] = round(noiseAtLoc2);
    myLoopingNoiseArray3[i] = round(noiseAtLoc3);
    myLoopingNoiseArray4[i] = round(noiseAtLoc4);
 
    var px1 = width/2 + radius * 0.7 * cos(rotatingArmAngle);
    var py1 = height/2 + radius * 0.7 * sin(rotatingArmAngle);
    var noiseAtLoc5 = height - 60 * noise(myScale * px1, myScale * py1);
    var noiseAtLoc6 = height - 130 * noise(myScale * px1, myScale * py1);
    var noiseAtLoc7 = height - 230 * noise(myScale * px1, myScale * py1);
    var noiseAtLoc8 = height - 330 * noise(myScale * px1, myScale * py1);
    var noiseAtLoc9 = height - 450 * noise(myScale * px1, myScale * py1);
    myLoopingNoiseArray5[i] = round(noiseAtLoc5);
    myLoopingNoiseArray6[i] = round(noiseAtLoc6);
    myLoopingNoiseArray7[i] = round(noiseAtLoc7);
    myLoopingNoiseArray8[i] = round(noiseAtLoc8);
    myLoopingNoiseArray9[i] = round(noiseAtLoc9);
  }
 
}
 
 
//===================================================
function keyTyped() {
  if (bEnableExport) {
    if ((key === 'f') || (key === 'F')) {
      bRecording = true;
      nElapsedFrames = 0;
    }
  }
}
 
//===================================================
function draw() {
 
  // Compute a percentage (0...1) representing where we are in the loop.
  var percentCompleteFraction = 0;
  if (bRecording) {
    percentCompleteFraction = float(nElapsedFrames) / float(nFramesInLoop);
  } else {
    percentCompleteFraction = float(frameCount % nFramesInLoop) / float(nFramesInLoop);
  }
 
  // Render the design, based on that percentage. 
  // This function renderMyDesign() is the one for you to change. 
  renderMyDesign (percentCompleteFraction);
 
  // If we're recording the output, save the frame to a file. 
  // Note that the output images may be 2x large if you have a Retina mac. 
  // You can compile these frames into an animated GIF using a tool like: 
  if (bRecording && bEnableExport) {
    var frameOutputFilename = myNickname + "_frame_" + nf(nElapsedFrames, 4) + ".png";
    print("Saving output image: " + frameOutputFilename);
    saveCanvas(theCanvas, frameOutputFilename, 'png');
    nElapsedFrames++;
 
    if (nElapsedFrames >= nFramesInLoop) {
      bRecording = false;
    }
  }
}
 
//===================================================
function renderMyDesign (percent) {
  //
  // THIS IS WHERE YOUR ART GOES. 
  // This is an example of a function that renders a temporally looping design. 
  // It takes a "percent", between 0 and 1, indicating where we are in the loop. 
  // Use, modify, or delete whatever you prefer from this example. 
  // This example uses several different graphical techniques. 
  // Remember to SKETCH FIRST!
 
  //----------------------
  smooth();
  var cx = 100;
  var cy = 100;
 
  push();
  translate(width/2, height/2);
 
  if (percent < 0.5){
    var ang = function_PennerEaseInOutElastic(percent*2)*180;
    rotate(radians(ang));
    drawbackdrop(percent);
  } else {
    var ang = function_PennerEaseInOutElastic(map(percent, 0.5, 1, 0, 1))*180;
    rotate(radians(ang) + TWO_PI/2);
    drawbackdrop(percent);
  }
 
  drawbackdrop(percent);
  pop();
 
 
  noStroke();
  push();
  translate(0, -100);
  drawboat(percent,-0.1);
  drawWave(percent, 88, 176, 230, myLoopingNoiseArray9, -0.1);
  drawWave(percent, 80, 160, 210, myLoopingNoiseArray8, 0);
  drawWave(percent, 22, 132, 200, myLoopingNoiseArray7, 0.6);
  drawWave(percent, 49, 80, 152, myLoopingNoiseArray6, 0.8);
  drawWave(percent, 54, 100, 160, myLoopingNoiseArray5, 0.8);
  pop();
  drawWave(percent, 45, 183, 212, myLoopingNoiseArray4, -0.2);
  drawWave(percent, 45, 167, 212, myLoopingNoiseArray3, -0.2);
  drawWave(percent, 65, 130, 190, myLoopingNoiseArray2, -0.2);
  drawWave(percent, 54, 100, 160, myLoopingNoiseArray1, 0.5);
  drawWave(percent, 49, 80, 152, myLoopingNoiseArray0, 0.5);
}
 
function drawbackdrop(p){
  fill(235,219,190);
  rect(-width, -height, width*3, height);
  fill(18,24,56);
  rect(-width, 0, width*3, height);
 
  //draw sun
  push();
  strokeWeight(3);
  stroke(235,172, 45);
  fill(235,172, 45);
  ellipse(200, -200, 50, 50);
  translate(200, -200);
  rotate(0.2 * p * TWO_PI);
  push();
  for (var a = 0; a < 10; a++){
    rotate(TWO_PI/10);
    line (50, 0, 70, 0);
  }
  pop();
  pop();
 
  //draw moon
  push();
  noStroke();
  fill(255);
  translate(-200, 200);
  ellipse(0, 0, 80, 80);
  fill(18,24,56);
  ellipse(-20, -10, 50, 50);
  pop();
 
  //draw star
  push();
  translate(-100, 110);
  noStroke();
  fill(255);
  star(0, 0, 5, 10, 5); 
  star(50, 110, 5, 10, 5); 
  star(80, 90, 3, 6, 5); 
  star(100, 105, 3, 6, 5); 
  pop();
 
}
 
function drawboat(p, tilt){
  currStep = round(p * nFramesInLoop);
  var nx = map(40, 0, nFramesInLoop, 0, width);
  var ny = myLoopingNoiseArray9[(currStep+40)%nFramesInLoop]
  // var newy = doubleExponentialSigmoid(map(y, 315, 370, 0, 1), 0.7);//does not look good
  // var y = map(newy, 0, 1, 315, 370);
  fill(255);
  beginShape();
  vertex(nx - 50, ny - 10);
  vertex(nx + 50, ny - 10);
  vertex(nx + 30, ny + 20);
  vertex(nx - 30, ny + 20);
  endShape();
  stroke(255);
  line(nx, ny, nx, ny - 50);
  triangle(nx, ny - 50, nx + 20, ny - 40, nx, ny - 30);
}
 
function drawWave(p, r, g, b, wave, tilt){
  currStep = round(p * nFramesInLoop);
  fill(r, g, b);
  noStroke();
  beginShape();
  vertex(0,height+50);
  for (var i = 0; i < wave.length; i++) {
    var nx = map(i, 0, nFramesInLoop - 1, 0, width);
    if (i + currStep >= nFramesInLoop) {
      var ny = wave[(i + currStep)-nFramesInLoop] - i*tilt;
    } else {
      var ny = wave[i + currStep] - i*tilt;
    }
    vertex(nx, ny);
  }
  vertex(width,height+50);
  endShape();
}
 
 
// symmetric double-element sigmoid function (a is slope)
// See https://github.com/IDMNYU/p5.js-func/blob/master/lib/p5.func.js
// From: https://idmnyu.github.io/p5.js-func/
//===================================================
function doubleExponentialSigmoid (_x, _a){
  if(!_a) _a = 0.75; // default
 
  var min_param_a = 0.0 + Number.EPSILON;
  var max_param_a = 1.0 - Number.EPSILON;
  _a = constrain(_a, min_param_a, max_param_a);
  _a = 1-_a;
 
  var _y = 0;
  if (_x<=0.5){
    _y = (pow(2.0*_x, 1.0/_a))/2.0;
  }
  else {
    _y = 1.0 - (pow(2.0*(1.0-_x), 1.0/_a))/2.0;
  }
  return(_y);
}
 
//From: https://github.com/golanlevin/Pattern_Master/blob/master/pattern_master/F00.pde
//------------------------------------------------------------------
function function_PennerEaseInOutElastic (t) {
 
  if (t===0) return 0; 
  if ((t/=0.5)==2) return 1;
  var p=(.3 * 1.5);
  var a=1;
  var s=p/4;
 
  if (t < 1) {
    var postFix = pow(2, 10*(t-=1)); // postIncrement is evil
    return -0.5 * (postFix* sin( (t-s)*(2*PI)/p ));
  } 
 
  var postFix = pow(2, -10*(t-=1)); // postIncrement is evil
  return postFix * sin( (t-s)*(2*PI)/p )*.5 + 1;
}
 
//From: https://p5js.org/examples/form-star.html
//------------------------------------------------------------------
function star(x, y, radius1, radius2, npoints) {
  var angle = TWO_PI / npoints;
  var halfAngle = angle/2.0;
  beginShape();
  for (var a = 0; a < TWO_PI; a += angle) {
    var sx = x + cos(a) * radius2;
    var sy = y + sin(a) * radius2;
    vertex(sx, sy);
    sx = x + cos(a+halfAngle) * radius1;
    sy = y + sin(a+halfAngle) * radius1;
    vertex(sx, sy);
  }
  endShape(CLOSE);
}

.
 

shuann-Scope

PDF file: shuann-praxinoscope-output

For this exercise I wanted to practice using sin/cos function to create wave patterns so I explored some aspects of that. I also played a little with rotation, transformation, push, pop, and scaling. One challenge I found that it is harder to design for a very limited space. Also, since this is my first time using processing I also tried to familiarized myself with the syntax and the available built-in functions. The good thing is that I did find that it is very similar to javascript which saved my a lot of time.

 

// Template for KidzLabs/4M/Toysmith Animation Praxinoscope
// https://www.amazon.com/4M-3474-Animation-Praxinoscope/dp/B000P02HYC
// https://www.walmart.com/ip/Animation-Praxinoscope-Science-Kits-by-Toysmith-3474/45681503
// Developed for Processing 3.3.6 * http://processing.org
// 23 January 2018 * Golan Levin 
 
// See information about Processing PDF export at: 
// https://processing.org/reference/libraries/pdf/index.html
// PDF generated by Processing can be opened in Adobe Illustrator.
import processing.pdf.*;
boolean bRecordingPDF = false;
 
float inch = 72; 
float diamArtInner = inch * 1.50; 
float diamArtOuter = inch * 4.80; 
float diamCutInner = inch * 1.41; 
float diamCutOuter = inch * 4.875; 
float holeDy = inch * 0.23;
float holeDx = inch * 0.20;
float holeD = inch * 0.1;
 
final int nFrames = 10; 
int myFrameCount = 0;
int exportFrameCount = 0; 
boolean bAnimate = true; 
boolean bExportFrameImages = false;
 
//-------------------------------------------------------
void setup() {
  size(792, 612); // 11x8.5" at 72DPI
  frameRate(15);
  smooth();
} 
 
//-------------------------------------------------------
void draw() {
  background(240); 
  if (bRecordingPDF) {
    beginRecord(PDF, "praxinoscope-output.pdf");
  }
 
  // Do all the drawing. 
  pushMatrix(); 
  translate(width/2, height/2);
  drawCutLines(); 
  drawGuides(); 
  drawAllFrames();
  popMatrix();
 
  if (bExportFrameImages) {
    // If activated, export .PNG frames 
    if (exportFrameCount < nFrames) {
      String filename = "frame_" + nf((exportFrameCount%nFrames), 3) + ".png";
      saveFrame("frames/" + filename);
      println("Saved: " + filename); 
      exportFrameCount++;
      if (exportFrameCount >= nFrames) {
        bExportFrameImages = false;
        exportFrameCount = 0;
      }
    }
  }
 
  if (bRecordingPDF) {
    endRecord();
    bRecordingPDF = false;
  }
}
 
 
//-------------------------------------------------------
void keyPressed() {
  switch (key) {
  case ' ': 
    // Press spacebar to pause/unpause the animation. 
    bAnimate = !bAnimate;
    break;
 
  case 'p': 
  case 'P':
    // Press 'p' to export a PDF for the Praxinoscope.
    bRecordingPDF = true; 
    break;
 
  case 'f': 
  case 'F': 
    // Press 'f' to export .png Frames (to make an animated .GIF)
    myFrameCount = 0; 
    exportFrameCount = 0; 
    bExportFrameImages = true;
    bAnimate = true; 
    break;
  }
}
 
//-------------------------------------------------------
void drawCutLines() {
  fill(0); 
  textAlign(CENTER, BOTTOM); 
  text("Praxinoscope Template", 0, 0-diamCutOuter/2-6); 
 
  stroke(0); 
  strokeWeight(1.0);
 
  noFill(); 
  if (!bRecordingPDF) {
    fill(255); 
  }
  ellipse(0, 0, diamCutOuter, diamCutOuter);
 
  noFill(); 
  if (!bRecordingPDF) {
    fill(240); 
  }
  ellipse(0, 0, diamCutInner, diamCutInner);
 
  noFill(); 
  ellipse(diamCutOuter/2 - holeDx, 0-holeDy, holeD, holeD); 
 
  line (diamCutInner/2, 0, diamCutOuter/2, 0);
}
 
//-------------------------------------------------------
void drawGuides() {
  // This function draws the guidelines. 
  // Don't draw these when we're exporting the PDF. 
  if (!bRecordingPDF) {
 
    noFill(); 
    stroke(128); 
    strokeWeight(0.2); 
    ellipse(0, 0, diamArtInner, diamArtInner); 
    ellipse(0, 0, diamArtOuter, diamArtOuter);
 
    for (int i=0; i<nFrames; i++) {
      float angle = map(i, 0, nFrames, 0, TWO_PI); 
      float pxi = diamArtInner/2 * cos(angle);
      float pyi = diamArtInner/2 * sin(angle);
      float pxo = diamArtOuter/2 * cos(angle);
      float pyo = diamArtOuter/2 * sin(angle);
      stroke(128); 
      strokeWeight(0.2);
      line (pxi, pyi, pxo, pyo);
    }
 
    // Draw the red wedge outline, highlighting the main view.
    int redWedge = 7; // assuming nFrames = 10
    for (int i=redWedge; i<=(redWedge+1); i++) {
      float angle = map(i, 0, nFrames, 0, TWO_PI); 
      float pxi = diamArtInner/2 * cos(angle);
      float pyi = diamArtInner/2 * sin(angle);
      float pxo = diamArtOuter/2 * cos(angle);
      float pyo = diamArtOuter/2 * sin(angle);
      stroke(255, 0, 0); 
      strokeWeight(2.0);
      line (pxi, pyi, pxo, pyo);
    }
    noFill(); 
    stroke(255, 0, 0); 
    strokeWeight(2.0);
    float startAngle = redWedge*TWO_PI/nFrames;
    float endAngle = (redWedge+1)*TWO_PI/nFrames;
    arc(0, 0, diamArtInner, diamArtInner, startAngle, endAngle); 
    arc(0, 0, diamArtOuter, diamArtOuter, startAngle, endAngle); 
 
 
    for (int i=0; i<nFrames; i++) {
      float angle = map(i, 0, nFrames, 0, TWO_PI); 
 
      pushMatrix();
      rotate(angle); 
      float originY = ((diamArtOuter + diamArtInner)/2)/2;
      translate(0, 0-originY); 
 
      noFill(); 
      stroke(128); 
      strokeWeight(0.2);
      line (-inch/2, 0, inch/2, 0); 
      line (0, -inch/2, 0, inch/2); 
 
      popMatrix();
    }
  }
}
 
//-------------------------------------------------------
void drawAllFrames() {
  for (int i=0; i<nFrames; i++) {
    float angle = map(i, 0, nFrames, 0, TWO_PI); 
    float originY = ((diamArtOuter + diamArtInner)/2)/2;
 
    pushMatrix();
    rotate(angle); 
    translate(0, 0-originY); 
    scale(0.8, 0.8); // feel free to ditch this 
 
    int whichFrame = i; 
    if (bAnimate) {
      whichFrame = (i+myFrameCount)%nFrames;
    }
    drawArtFrame (whichFrame); 
    // drawArtFrameAlternate (whichFrame); 
 
    popMatrix();
  }
  myFrameCount++;
}
 
 
//-------------------------------------------------------
void drawArtFrame (int whichFrame) { 
  // Draw the artwork for a generic frame of the Praxinoscope, 
  // given the framenumber (whichFrame) out of nFrames.
  // NOTE #1: The "origin" for the frame is in the center of the wedge.
  // NOTE #2: Remember that everything will appear upside-down!
 
  pushMatrix();
  noStroke();
  strokeWeight(1); 
  translate(0, 40);
  float t = map(whichFrame, 0, nFrames, 0, 1); 
  float factor = map(cos(t*TWO_PI), -1, 1, 0.3, 1); 
  float col = map(t, 0, 1, 120, 0); 
  fill(col);
  scale(factor, factor);
  triangle(0, -15, 10, 0, -10, 0); 
  triangle(10, 0, 20, 15, 0, 15); 
  triangle(-10, 0, 0, 15, -20, 15); 
  popMatrix();
 
  pushMatrix();
  translate(0, -35);
  noFill();
  stroke(0);
  float ang = map(t, 0, nFrames, 0, TWO_PI); 
  rotate(ang); 
  for (int j=0; j<10; j++){
    rotate(radians(36));
    ellipse(15, 0, 12, 5);
  }
  endShape();
  popMatrix();
 
  //draw waves
  pushMatrix();
  translate(0, 10);
  beginShape(POINTS);
  stroke(0);
  for (int i=0; i<70; i++){
    float h = 0 - map(i, 0, 69, 0, 1);
    if (whichFrame%nFrames<5){
      vertex(i - 35, 18 * cos(h * TWO_PI) + whichFrame);
    } else {
      vertex(i - 35, 18 * cos(h * TWO_PI) + (9-whichFrame));
    }
  }
  endShape();
  popMatrix();
}
 
//-------------------------------------------------------
void drawArtFrameAlternate(int whichFrame) { 
  // An alternate drawing test. 
  // Draw a falling object. 
 
 
  // Draw a little splat on the frame when it hits the ground. 
  if (whichFrame == (nFrames-1)) {
    stroke(0, 0, 0); 
    strokeWeight(0.5); 
    int nL = 10;
    for (int i=0; i<nL; i++) {
      float a = HALF_PI + map(i, 0, nL-1, 0, TWO_PI);
      float cx = 12 * cos(a);
      float cy = 10 * sin(a); 
      float dx = 16 * cos(a);
      float dy = 13 * sin(a); 
      line (cx, 45+cy, dx, 45+dy);
    }
  }
 
  // Draw a little box frame
  fill(255); 
  stroke(0, 0, 0);
  strokeWeight(1); 
  rect(-5, -50, 10, 100); 
 
  // Make the puck accelerate downward
  float t = map(whichFrame, 0, nFrames-1, 0, 1); 
  float t2 = pow(t, 2.0); 
  float rh = 8 + whichFrame * 0.5; // wee stretch
  float ry = map(t2, 0, 1, 0, 100-rh) - 50; 
 
  noStroke(); 
  fill(0, 0, 0);
  rect(-5, ry, 10, rh);
}

.

shuann-Reading02

Question 1A. Aquaponics is a system that exhibits effective complexity. It is in fact a very simple symbiotic system that mimics a natural waterway where plants can live off of the nutrient rich water that a fish tank produces. In this sense, it is highly ordered since the the stream of energy transfer is strictly from fish food to fish and then to the plant. On the other hand, it is also highly disordered because on a molecular level, the ran formation of energy happens ver organically and it's very hard say how energy is exactly being transported to a new place.

Question 1B. I would like to say a few words on the problem of authorship here. The example that the article refers to in the second last paragraph of this section really brings me back to an other probable that I have been thinking of recently regarding the popular recommendation engine that is now omnipresent on the internet. Similarly in that case, if the engines suggest you things that you might like, how do we make sure that the results are truly derived correctly from your personal preferences? Even if you do end up liking the suggestions, how ca new know it is not a confirmation bias in the sense that "Since the powerful machine that I believed in suggested that I would like these things thus I must have a higher chance of actually liking it". Here, regarding the authorship of the generative art work, we face the same dilemma once the system becomes highly intelligent. I think if we, as the creator of the systems that generate art work, believe that we still posses full control over the system and we know exactly how the magic is done, then we still have full ownership over the work. However, on the other hand, if the work is truly accidental then the ownership becomes more of a problem.