Spoon-ARSculpture

The stone box outside of CFA has a cold.

I originally intended to give the protruding lecture hall on Wean Hall a cold, but this proved to be too much of a challenge to make functional with 8th Wall, so instead, I gave another protruding box with a more reasonable scale a cold. I was looking for objects around campus that suggested to me that they could be heads, and gave one such object a face. Given the weather we've been having recently, I thought this head might be a little cold, and so it might be a bit sick as a result.

The augmented reality app brings a personality to something that previously didn't have one. It turns an inanimate object that often goes unnoticed into a character.

The faces were drawn by hand, scanned, and then animated as textures on a plane with a script in Unity. The sneeze was done with a particle system that is triggered by the same animation script. The plane and the particle system are attached to the box using the photo recognition script in 8th Wall. I took a photo of the flat face of the box and used that as a photo for the script.

Spoon-JustALine

The statues in CFA are missing parts. I wanted to give them their parts back. Many of these statues are missing arms, feat, or heads. I attempted to use justALine to prototype an app that would add these features back. Unfortunately, from the angle I could stand at, it was hard for justALine to recognize where I was actually trying to draw the shapes, so pieces ended up being in wildly incorrect places.


Spoon-Book

For my generative book, I chose to make a bad design student. The school of design teaches a required course for its freshman students called Placing, in which students produce photo essays. I wrote an algorithm that would generate these essays by taking other students' essays (with their permission) as source material for a markov chain. The resulting essays are a mish-mosh of all sorts of different content that addresses the same assignment (every time the code is run it produces an essay that addresses one of the four assignments given in Placing). These essays do not make much sense, but they tend to contain gems of crazy, nonsensical sentences or outlandish claims about society that are the result of multiple different sentences from the source material being put together in a strange way.

After the text is generated and saved into a JSON file, it is fed through a Basil.js script that places the resulting text onto different pages and pairs each paragraph with a photograph randomly pulled from a collection of photos used in students' essays for that particular assignment.

The code for the text generator is just a Markov chain with no additional logic added to it. I spent some time experimenting with the n-value for the chain because I was trying to find a balance between sentences that were more or less grammatical and not simply lifting full sentences directly from the source material. The code generates a random number of sentences between a range of 60 to 75. It then splits the resulting text into paragraphs of 5 sentences each.

The Basil.js script creates a title page for the book, then lays out two generated Placing essays (I select these essays by hand). Each page of the laid out essay features an image paired with one paragraph.

I'm not totally satisfied with the results. I would have liked the essays to be a little more interesting. At this point, they are more or less just a random set of random sentences that happen to focus on the same general topic. The essays are not random enough to be funny, but they don't make enough sense to be interesting for other reasons. I might be able to it more interesting by increasing the dataset or by having some sort of logical decision making in the code to focus the sentences a little more.

 
 

var lines = [], markov, data = [], x = 160, y = 240;
 
var paragraphs = [];
 
var essay;
 
class Paragraph {
    constructor(title, text) {
        this.title = title;
        this.text = text;
    }
}
 
function preload() {
    var essayNum = int(random(4));
    switch(essayNum) {
    case 0 :
        essay = "Stuff";
        data[0] = loadStrings('src/Essays/stuff_delgado.txt');
        data[1] = loadStrings('src/Essays/stuff_vzhou.txt');
        data[2] = loadStrings('src/Essays/stuff_carpenter.txt');
        break;
    case 1 :
        essay = "Nature";
        data[0] = loadStrings('src/Essays/nature_delgado.txt');
        data[1] = loadStrings('src/Essays/nature_fang.txt');
        data[2] = loadStrings('src/Essays/nature_carpenter.txt');
        break;
    case 2 :
        essay = "Neighborhood";
        data[0] = loadStrings('src/Essays/neighborhood_carpenter_fan_zhang.txt');
        data[1] = loadStrings('src/Essays/neighborhood_cho_fang_nishizaki.txt');
        //data[2] = loadStrings('src/Essays/stuff_carpenter.txt');
        break;
    default :
        essay = "Trash";
        data[0] = loadStrings('src/Essays/trash_delgado_powell.txt');
        data[1] = loadStrings('src/Essays/trash_choe_fang.txt');
        data[2] = loadStrings('src/Essays/trash_carpenter_ezhou.txt');
        break;
 
    }
}
 
function setup() {
    createCanvas(500, 3500);
    textFont('times', 16);
    textAlign(LEFT);
 
    lines = ["click to (re)generate!"];
 
    // create a markov model w' n=4
    markov = new RiMarkov(3);
 
    // load text into the model
 
    for(var i = 0; i < data.length; i++) {
        markov.loadText(data[i].join(' '));
    }
 
    drawText();
}
 
function drawText() {
    background(250);
 
    if(lines.length <= 1) {
        text(lines.join(' '), x, y, 400, 400);
    }
 
    for(var i = 0; i < lines.length; i++) {
        var line = [lines[i]];
        text(line.join(' '), x, y + (i * 410), 400, 400 + (i * 410));
    }
}
 
function keyTyped() {
    if(key === ' ') {
        x = y = 50;
        var essayLength = int(random(75, 100));//int(random(4, 9));
        var fullEssay = markov.generateSentences(essayLength);
        for(var i = 0; i < int(essayLength / 10); i++) {
            lines[i] = "";
            if((i + 1) * 10 > essayLength) {
                for(var j = i * 10; j < essayLength; j++) {
                    lines[i] = lines[i].concat(' ', fullEssay[j]);
                }
            } else {
                lines[i] = "";
                for(var j = i * 10; j < (i + 1) * 10; j++) {
                    lines[i] = lines[i].concat(' ', fullEssay[j]);
                }
            }
        }
 
        var newParagraph = new Paragraph(essay, lines);
        paragraphs[0] = newParagraph;
        drawText();
 
        var output = {};
        output.paragraphs = paragraphs;
        createButton('SAVE PARAGRAPHS')
            .position(450, 3450)
            .mousePressed(function() {
                saveJSON(output, 'essay.json');
            });
    }
}

 

 

#include "../../bundle/basil.js";
 
var numPages;
 
var neighborNums = [48, 40, 33];
var neighborNames = ["carpenter_fan_zhang", "cho_fang_nishizaki", "gersing_kim_powell"];
 
var natureNums = [5, 13, 6, 6, 5];
var natureNames = ["carpenter", "delgado", "fang", "powell", "zhai"];
 
var stuffNums = [8, 9, 2, 8, 19, 7];
var stuffNames = ["carpenter", "delgado", "fang", "powell", "vzhou", "zhai"];
 
var trashNums = [13, 12, 15, 6, 5];
var trashNames = ["carpenter_ezhou", "choe_fang", "choi_zhai", "delgado_powell", "zhai_vzhou"];
 
var usedImages = [];
 
function setup() {
	var jsonString2 = b.loadString("neighborhood/essay (18).json");
	var jsonString1 = b.loadString("stuff/essay (44).json");
 
	b.clear (b.doc());
 
	var jsonData1 = b.JSON.decode( jsonString1 );
	var paragraphs1 = jsonData1.paragraphs;
	var jsonData2 = b.JSON.decode( jsonString2 );
	var paragraphs2 = jsonData2.paragraphs;
	b.println("paragraphs: " + paragraphs1.length + "+" + paragraphs2.length);
 
	var inch = 72;
 
	var titleW = inch * 5.0;
	var titleH = inch * 0.5;
	var titleX = (b.width / 2) - (titleW / 2);
	var titleY = inch;
 
	var paragraphX = inch / 2.0;
	var paragraphY = (b.height / 2.0) + (inch * 1.5);
	var paragraphW = b.width - inch;
	var paragraphH = (b.height / 2.0) - (inch * 2.0);
 
	var imageX = inch / 2.0;
	var imageY = inch / 2.0;
	var imageW = b.width - (inch);
	var imageH = (b.height * 0.5) + inch;
 
	numPages = 0;
	usedImages.push("");
 
 
	//first page of book
	//newPage();
	numPages++;
 
	b.fill(0);
	b.textSize(52);
	b.textFont("Archivo Black", "Regular");
	b.textAlign(Justification.CENTER_ALIGN, VerticalJustification.CENTER_ALIGN);
	b.text("Plagiarizing:", inch / 2.0, b.height / 2.0 - inch, b.width - inch, inch);
 
	b.fill(0);
	b.textSize(20);
	b.textFont("Archivo", "Bold");
	b.textAlign(Justification.CENTER_ALIGN, VerticalJustification.CENTER_ALIGN);
	b.text("A reinterpretation of other people's photo-essays from Placing", inch / 2.0, b.height / 2.0, 
		b.width - inch, inch);
 
 
	//introduce first essay
	newPage();
	b.fill(0);
	b.textSize(36);
	b.textFont("Archivo Black", "Regular");
	b.textAlign(Justification.CENTER_ALIGN, VerticalJustification.CENTER_ALIGN);
	b.text(paragraphs1[0].title + "ing", inch / 2.0, b.height - inch * 2, 
		b.width - inch, inch);
 
	b.noStroke();
	var coverImageName = imageName(paragraphs1[0].title);
	var coverImage = b.image(coverImageName, inch / 2.0, 
		inch  / 2.0, b.width - inch, (b.height / 2.0) + (2 * inch));
	coverImage.fit(FitOptions.PROPORTIONALLY);
 
	for(var i = 0; i &lt; paragraphs1[0].text.length; i++) {
		newPage();
		b.fill(0);
		b.textSize(12);
		b.textFont("Archivo", "Regular");
		b.textAlign(Justification.LEFT_ALIGN, VerticalJustification.TOP_ALIGN);
		b.text("\t" + paragraphs1[0].text[i].substring(1), paragraphX, paragraphY, 
			paragraphW, paragraphH);
 
		b.noStroke();
		var imgName = imageName(paragraphs1[0].title);
		var img = b.image(imgName, imageX, imageY, imageW, imageH);
		img.fit(FitOptions.PROPORTIONALLY);
	};
 
	if(numPages % 2 == 0) {
		newPage();
	}
 
	//Second Photo Essay
	newPage();
	b.fill(0);
	b.textSize(36);
	b.textFont("Archivo Black", "Regular");
	b.textAlign(Justification.CENTER_ALIGN, VerticalJustification.CENTER_ALIGN);
	b.text(paragraphs2[0].title + "ing", inch / 2.0, b.height - inch * 2, 
		b.width - inch, inch);
 
	usedImages = [""];
 
	b.noStroke();
	coverImageName = imageName(paragraphs2[0].title);
	coverImage = b.image(coverImageName, inch / 2.0, inch  / 2.0, b.width - inch, 
		(b.height / 2.0) + (2 * inch));
	coverImage.fit(FitOptions.PROPORTIONALLY);
 
	for(var i = 0; i &lt; paragraphs2[0].text.length; i++) {
		newPage();
		b.fill(0);
		b.textSize(12);
		b.textFont("Archivo", "Regular");
		b.textAlign(Justification.LEFT_ALIGN, VerticalJustification.TOP_ALIGN);
		b.text("\t" + paragraphs2[0].text[i].substring(1), paragraphX, paragraphY, 
			paragraphW, paragraphH);
 
		b.noStroke();
		var imgName = imageName(paragraphs2[0].title);
		var img = b.image(imgName, imageX, imageY, imageW, imageH);
		img.fit(FitOptions.PROPORTIONALLY);
	};
 
	//give credit to original authors and photographs
	newPage();
	b.fill(0);
	b.textSize(14);
	b.textFont("Archivo", "Bold");
	b.textAlign(Justification.CENTER_ALIGN, VerticalJustification.CENTER_ALIGN);
	b.text(paragraphs1[0].title + "ing", inch / 2.0, (b.height / 2.0) - (inch * 1.5), 
		b.width - inch, inch / 2.0);
 
	var authors = generateCredits(paragraphs1[0].title);
 
	b.textSize(12);
	b.textFont("Archivo", "Regular");
	b.textAlign(Justification.LEFT_ALIGN, VerticalJustification.TOP_ALIGN);
	b.text("Original text and photos by:", inch / 2.0, (b.height / 2.0) - inch, 
		b.width - inch, 14.4);
	b.text(authors.join(", "), inch, (b.height / 2.0) - inch + 14.4, 
		b.width - (inch * 1.5), inch - 14.4);
 
 
	b.textSize(14);
	b.textFont("Archivo", "Bold");
	b.textAlign(Justification.CENTER_ALIGN, VerticalJustification.CENTER_ALIGN);
	b.text(paragraphs2[0].title + "ing", inch / 2.0, (b.height / 2.0), 
		b.width - inch, inch / 2.0);
 
	authors = generateCredits(paragraphs2[0].title);
 
	b.textSize(12);
	b.textFont("Archivo", "Regular");
	b.textAlign(Justification.LEFT_ALIGN, VerticalJustification.TOP_ALIGN);
	b.text("Original text and photos by:", inch / 2.0, 
		(b.height / 2.0) + (inch * 0.5), b.width - inch, 14.4);
	b.text(authors.join(", "), inch, (b.height / 2.0) + (inch * 0.5) + 14.4, 
		b.width - (inch * 1.5), inch - 14.4);
 
	if(numPages % 2 != 0) {
		newPage();
	}
}
 
function newPage() {
	b.addPage();
	numPages++;
}
 
function imageName(assignment) {
	var fileName = "";
	while(usedImagesIncludes(fileName)){
		if(assignment == "Neighborhood") {	
			var i = b.floor(b.random(neighborNames.length));
			fileName = neighborNames[i] + b.floor(b.random(neighborNums[i]) + 1);
		} else if(assignment == "Nature") {	
			var i = b.floor(b.random(natureNames.length));
			fileName = natureNames[i] + b.floor(b.random(natureNums[i]) + 1);
		} else if(assignment == "Trash") {	
			var i = b.floor(b.random(trashNames.length));
			fileName = trashNames[i] + b.floor(b.random(trashNums[i]) + 1);
		} else {
			var i = b.floor(b.random(stuffNames.length));
			fileName = stuffNames[i] + b.floor(b.random(stuffNums[i]) + 1);	
		}
	}
	usedImages.push(fileName);
	return "images/" + assignment + "/" + fileName + ".jpg";
}
 
function usedImagesIncludes(fileName) {
	for(var i = 0; i &lt; usedImages.length; i++) {
		if(usedImages[i] == fileName)
			return true;
	}
	return false;
}
 
function generateCredits(assignment) {
	if(assignment == "Neighborhood") {	
		return ["Sebastian Carpenter", "Danny Cho", "Sophia Fan", "Alice Fang", "Margot Gersing",
				"Jenna Kim", "Julia Nishizaki", "Michael Powell", "Jean Zhang"];
	} else if(assignment == "Nature") {	
		return ["Sebastian Carpenter", "Daniela Delgado", "Alice Fang", 
				"Michael Powell", "Sabrina Zhai"];
	} else if(assignment == "Trash") {	
		return ["Sebastian Carpenter", "Eunice Choe", "Julie Choi", "Daniela Delgado", "Alice Fang", 
				"Mimi Jiao", "Michael Powell", "Sabrina Zhai", "Emily Zhou"];
	} else {
		return ["Sebastian Carpenter", "Daniela Delgado", "Michael Powell", 
				"Sabrina Zhai", "Vicky Zhou"];	
	}
}
 
b.go();

Spoon-Viewing

After viewing Allison Parrish's 2015 EyeO lecture, there were a few things that stuck with me. The first thing, and what probably struck me the most was her opinion that the role of the AI bot in art should not be to replicate what a human artist might do, but rather to go into spaces that a human would not go into. Parrish demonstrated this point with her example of semantic space and how her bots typically attempt to navigate into the empty parts of this semantic space, effectively going somewhere where people would not be able to systematically go on their own.

I was also struck by how humorous generative literature can be. Much of Parrish's work appears to have a humorous element to it, and this might be by design, but I would not be surprised if much of this actually is the result of how we interpret language. Often times, absurd, nonsensical pairings of words are humorous to us. This is, of course, exactly what Parrish's bots produce.

paukparl-spoon_Automaton

We created a lollipop-licking robot for our automaton. The robot uses an IR distance sensor to pick up when someone has placed a lollipop into the lollipop holder, which initiates the robot's licking. A tongue made out of a sponge moves up and down (powered by a servo) while the lollipop rotates (powered by a stepper). The tongue dips into a tray at the bottom of its movement that is supposed to contain water to keep the tongue wet for its licking.

We came up with a couple of automaton ideas, but really couldn't settle on anything to begin with, so we went to the Center for Creative Reuse to get some inspiration. The entire project was inspired by sponges we found at there. The scale of the robot, the type of lollipop the robot was licking, and the movement were all based around using the sponge as a tongue. To keep the tongue wet, we had a couple of ideas. We first thought we might be able to wet the tongue once before it starts licking a lollipop, but we realized that this did not provide enough water to get through an entire lollipop. We then planned to have water dripping down onto the sponge from the top (and later from the side), but both ideas necessitated the use of some sort of valve, and we figured this was too complicated for the scope of the assignment. We then settled on the idea of using a stationary tray of water that the tongue could dip into when needed.

After settling on a design for the robot, we modeled the structure and shell in SolidWorks, accounting for the electronics that would be placed inside.

Despite our measurements, we failed to account for a couple of things. Most notably, we didn't account for the space needed for the servo's plug (we just made space for the Arduino on its own). As a result, our robot currently doesn't have its back. Also, we did not have time to seal the water tray, so our robot currently cannot hold water. The tongue knocks the the lollipop out of its slot sometimes (an issue that could be addressed by moving the tongue a couple of millimeters back and making the lollipop slot a little bit tighter). As a final point of improvement, the tongue's movement could be adjusted to make it a more natural movement.

Spoon-LookingOutwards04

Senseless Drawing Bot by So Kanno and Takahiro Yamaguchi

Senseless Drawing Bot is a robot that features a four wheel base driven with motors and then a double pendulum attached to the base  with a can of spraypaint mounted to the end of the double pendulum. The inertia acting on the arm causes the paint to be released from the spraypaint can in abstract patterns somewhat reminiscent of graffiti art. To accentuate the erratic movement of the spraypaint can, there is a rotary encoder at the fulcrum of the pendulum that allows the movement of the robot's base to accentuate the movement of the arms.

The robot's movement is particularly interesting because of how out of control it looks. Despite this appearance, the robot is very much under control. Its movements are programmed such that it can create a maximum amount of movement in the arm. The robot controls its movement to simulate being very out of control. It plays with motion to raise questions about how much control the artist has over their art, and how much that control matters in the art that's being produced.

The piece definitely draws influence from abstract graffiti, looking at the street art somewhat for its form, but also for the process of vandalism. Vandalism, like the arm of the robot itself, suggest a lack of control in an environment with lots of control (the purposeful designs of man-made urban structures that often fall victim to vandalism).

Spoon-Body

https://youtu.be/H_0f3B_2JLY

This project ended up being more of an exploration of motion than an art piece. The goal was to take a motion capture skeleton and have it interact with some physics objects -- like springs and particles -- to produce an interesting piece. I achieved about half of that. Using the physics engine found in the Toxiclibs library in Processing, I attached a series of short springs and particles (which look like yellow strings in the video) to various points of the body. When the person moves, the springs move around and respond, accentuating the person's movement.

The affectation on the motion definitely came first. To be honest, the skeleton used in the piece was almost an afterthought. If I had left myself more time, I would have liked to get some motion capture footage of someone doing quick motions that cover a lot of space. This would have likely made the springs move all over the place and produce interesting paths and curves as the person moved.

 

 

import toxi.geom.*;
import toxi.geom.mesh.*;
import toxi.geom.mesh.subdiv.*;
import toxi.geom.mesh2d.*;
import toxi.geom.nurbs.*;
import toxi.physics3d.*;
import toxi.physics3d.behaviors.*;
import toxi.physics3d.constraints.*;
 
public class PBvh
{
  public BvhParser parser;  
 
  private ArrayList controlPoints;
  private ArrayList trailingPoints;
  private Particle finalPoint;
  private ArrayList springs;
  private VerletPhysics3D physics;
 
  private float w = 10;
 
  public PBvh(String[] data) {
    parser = new BvhParser();
    parser.init();
    parser.parse( data );
  }
 
  public void update( int ms ) {
    parser.moveMsTo( ms );//30-sec loop 
    parser.update();
  }
 
  public void setup() {
    controlPoints = new ArrayList();
    trailingPoints = new ArrayList();
    springs = new ArrayList();
    physics = new VerletPhysics3D();
    Vec3D gravity = new Vec3D(0, -10, 0);
    GravityBehavior3D gb = new GravityBehavior3D(gravity);
    physics.addBehavior(gb);
 
    finalPoint = new Particle(0, 0, 0);
 
    for( BvhBone b : parser.getBones())
    {
      if (true/*!b.hasChildren()*/)
      {
        Particle p1;
        ArrayList p2;
        p1 = new Particle(b.absEndPos.x, 
                          b.absEndPos.y, 
                          b.absEndPos.z);
        p2 = new ArrayList();
 
        for(int i = 0; i &lt; 10; i++) {
          Particle p = new Particle(b.absPos.x, 
                                    b.absPos.y, 
                                    b.absPos.z);
          p2.add(p);
        }
 
        p1.lock();
        ArrayList armSprings;
        armSprings = new ArrayList();//new VerletSpring3D(p1, p2.get(0), 5, 0.05);
        armSprings.add(new VerletSpring3D(p1,
                                          p2.get(0),
                                          1,
                                          0.1));
        for(int i = 1; i &lt; 10; i++) {
          VerletSpring3D s = new VerletSpring3D(p2.get(i - 1),
                                                p2.get(i),
                                                1,
                                                0.1);
          armSprings.add(s);
        }
        armSprings.add(new VerletSpring3D(armSprings.get(armSprings.size() - 1)
                                          finalPoint,
                                          1
                                          0.1)
        controlPoints.add(p1);
        physics.addParticle(p1);
        for(Particle p : p2) {
          trailingPoints.add(p);
          physics.addParticle(p);
        }
        for(VerletSpring3D spring : armSprings) {
          springs.add(spring); 
          physics.addSpring(spring);
        }
      } 
    }
  }
 
  public void draw() {
    fill(color(255));
    int counter = 0;
    for( BvhBone b : parser.getBones())
    {
      pushMatrix();
      translate(b.absPos.x, b.absPos.y, b.absPos.z);
      ellipse(0, 0, 2, 2);
      popMatrix();
 
      Particle p1 = controlPoints.get(counter);
      p1.x = b.absPos.x;
      p1.y = b.absPos.y;
      p1.z = b.absPos.z;
      counter++;
 
      if (!b.hasChildren())
      {
        pushMatrix();
        translate(b.absEndPos.x, b.absEndPos.y, b.absEndPos.z);
        println("xbody = ", b.absEndPos.x);
        println("ybody = ", b.absEndPos.y);
        println("zbody = ", b.absEndPos.z);
        ellipse(0, 0, 10, 10);
        popMatrix();  
 
        /*Particle p1 = controlPoints.get(counter);
        p1.x = b.absEndPos.x;
        p1.y = b.absEndPos.y;
        p1.z = b.absEndPos.z;
        counter++;*/
      }
 
    }
 
    physics.update();
    counter = 0;
    /*for(Particle p1 : controlPoints) {
      counter++;
      int bvhCounter = 0;
      for( BvhBone b : parser.getBones()) {
        if(!b.hasChildren()) {
          bvhCounter++;
        }
        if(bvhCounter == counter) {
          p1.lock();
          p1.x = b.absEndPos.x;
          p1.y = b.absEndPos.y;
          p1.z = b.absEndPos.z;
 
          println("xpoint = ", p1.x);
          println("ypoint = ", p1.y);
          println("zpoint = ", p1.z);
        }
      }
    }*/
 
    /*for(Particle p2 : trailingPoints) {
      p2.display();
    }*/
    for(VerletSpring3D spring : springs) {
      Particle p1 = (Particle) spring.a;
      Particle p2 = (Particle) spring.b;
 
      p1.display(2);
      p2.display(2);
 
      float x1 = p1.x;
      float y1 = p1.y;
      float z1 = p1.z;
 
      float x2 = p2.x;
      float y2 = p2.y;
      float z2 = p2.z;
 
      stroke(255, 198, 0);
      line(x1, y1, z1, x2, y2, z2);
    }
  }
}
// Originally from http://perfume-dev.github.io/
 
BvhParser parserA = new BvhParser();
PBvh /*bvh1,*/ /*bvh2,*/ bvh3;
 
public void setup()
{
  size( 1280, 720, P3D );
  background( 0 );
  noStroke();
  frameRate( 30 );
 
//  bvh1 = new PBvh( loadStrings( "A_test.bvh" ) );
//  bvh2 = new PBvh( loadStrings( "B_test.bvh" ) );
  bvh3 = new PBvh( loadStrings( "C_test.bvh" ) );
 
//  bvh1.update(0);
//  bvh2.update(0);
  bvh3.update(0);
 
//  bvh1.setup();
//  bvh2.setup();
  bvh3.setup();
 
  loop();
}
 
public void draw()
{
  background( 0 );
 
  //camera
  float _cos = cos(millis() / 7000.f);
  float _sin = sin(millis() / 5000.f);
  camera(width/8.f + width/8.f /** _sin +200*/, height/2.0f-100, 550 + 150/* * _cos*/, width/2.0f + 400, height/2.0f, -400, 0, 1, 0);
 
  //ground 
  /*fill( color( 255 ));
  stroke(127);
  line(width/2.0f, height/2.0f, -30, width/2.0f, height/2.0f, 30);
  stroke(127);
  line(width/2.0f-30, height/2.0f, 0, width/2.0f + 30, height/2.0f, 0);
  */stroke(255);
 
  pushMatrix();
  translate( width/2, height/2-10, 0);
  scale(-1, -1, -1);
 
  //model
//  bvh1.update( millis() );
//  bvh2.update( millis() );
  bvh3.update( millis() );
 
//  bvh1.draw();
//  bvh2.draw();
  bvh3.draw();
 
  popMatrix();
}
class Particle extends VerletParticle3D {
  Particle(float x, float y, float z) {
    super(x, y, z);
  }

void display(int n) {
pushMatrix();
translate(x, y, z);
//ellipse(0, 0, n, n);
popMatrix();
}
}


	

Spoon-LookingOutwards03

A Journey, Seoul by Mimi Son and Elliot Woods.

Seoul is a piece of interactive art that consists of numerous clear acrylic plates with white detailing added to them. The plates can be placed into a box by the viewer that illuminates the plates and projects colors and patterns onto them. The project focuses on the viewer's memories, calling to viewer to assemble a box that means something to them.

I like how self-defined the piece is. It tries not to be meaningful to the viewer by making  a statement on its own. Instead, its meaning comes from the meaning that the viewer puts into it. This is interactive art on a very base level, i.e., the user has a hand in creating their own version of the piece. In fact, no two viewers will view the same piece. While they both might assemble the same plates in the same order, nobody will assemble the box for the same reason, thus fundamentally changing the piece.

A Journey, Seoul is part of a series created by the artists that includes a couple of other cities. Each city provides different interactions-- for example, A Journey, London, uses changing sounds and lights to tell different stories through the same physical model. Each iteration of the project includes the user more and more in the personalization (A Journey, Dublin is the last in the series and allows the viewer to actually draw on the panels to create their own narrative completely).

Of the series, though, I think Seoul is the strongest. London leaves little up to the viewer, making its interaction a mostly passive experience. Dublin, while quite visually pleasing, seems to give too much freedom to the viewer. The piece becomes more of a white-board than an art piece. Seoul, on the other hand, gives the viewer enough freedom to make their own memories from the predefined plates, while still keeping control over what the viewer is seeing to a certain extent. There is value to limiting what a viewer can do, as it forces the imagination to fill in gaps in their head, rather than giving the viewer the freedom to fill in those gaps in the physical world.