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.

shuann-Interruptions

  1. The artwork is square.
  2. The artwork consists of many short black lines, on a white background.
  3. The lines all have the same length.
  4. Overall there seems to be a general direction, horizontal or vertical, that all the lines follow (noise exists obviously)
  5. However each line by itself seems to have a somewhat random direction that it points to
  6. every line overlaps with the previous line at the half-point mark
  7. There are empty holes in the piece, where there are no lines
  8. The empty spots where lines are skipped are clustered together
  9. The exact positions of the holes seem random
  10. The size of the holes are also random

GIF:

Process:

I personally think the result is pretty satisfactory especially given the fact that I was able to really get a good understanding of what noise is and how to use it through the completion of this project. I started by creating an invisible grid system where each of the grid contains a simple straight line. Then I randomly rotate each lines separately using translate and push and pop. Lastly, to create the holes I had to understand noise which I found very challenging at first. Partly because the documentation on the p5.js site were not super great at explaining the concept in plain, understandable language. However, after learning it I realized how adaptive noise can be, and I can already see how it can be used in many other cases.

shuann-Reading01

"3. The Critical Engineer deconstructs and incites suspicion of rich user experiences."

I think what this tenet is talking about them importance of not simply accepting what is offered out there in the world. Instead, we need to always keep a critical eye on the new technologies and as well as the new experiences that they claim to bring. We need to understand why they are developed and how they are adapted before we take any actions. In addition, it is in fact almost always the case that a rich user experience mutually benefits not only the users themselves but also the company who provides the service/product. With the spreading of information technology today, information is traveling at a rate that is no longer limited by space and time. Thus this also enhances the effectiveness of "word of mouth". By providing good experience with existing users, this means they business has a better chance of reaching a larger group of potential users though the self-identified promotors.