Breathing Tree

Nobu and I are creating a collaborative game that uses volume from microphones to spur the growth and fruition of a virtual tree. The game highlights the value of cooperation and participation in environmental efforts.

revision: Blowing as input has been dropped in favor of a more intuitive interaction. Input is now someone brushing his/her hand over a patch of grass with embedded motion sensor or photocells. Game aspect will be augmented by a timer in the form of the cycle of the sun in the background, and "high scores" in the form of memory of past trees in the background of the viz.

Prototype Code (uses stereo headphones as input):



//Breathing Tree
//by Cameron Cundiff and Nobu Nakaguchi
import ddf.minim.*;
import processing.serial.*;     // import the Processing serial library

//Bubble object
class Being {
float redVal;
float greenVal;
float blueVal;
float alphaVal;
float xloc;
float yloc;
int age;
boolean left;
boolean right;
boolean center;
boolean isPregnant;
boolean isAlive;
int progeny;
int gestation;
float speed = 2;
char side;

Being(float xloc_, float yloc_, char side_) {

side = side_;

if(side == 'L' || side == 'R') {
redVal = 255;
greenVal = 40;
blueVal = 100;
alphaVal = 255;
}

else if(side == 'C') {
redVal = 50;
greenVal = 255;
blueVal = 0;
alphaVal = 255;
}

isAlive = true;

gestation = 0;
isPregnant = false;

xloc = xloc_;
yloc = yloc_;
age = 0;
}

void wander(float speed_) {
xloc += random(-speed_,speed_);
yloc += random(-speed_,speed_);
}

void incrementValues() {
age++;
redVal -= 20;
if(alphaVal > 30) {
alphaVal--;
}
}

void rest() {
isPregnant = true;
}

void checkAge() {
if(age > maxAge * 5) {
isAlive = false;
}
}

void drawMe() {
noStroke();
fill(redVal, greenVal, blueVal, alphaVal);
ellipse(xloc, yloc, 5, 5);
wander(speed);
}

}

//Managing class
class World {

//instantiate objects
void spawn(int num, char side) {

for (int i = 0; i < num; i++) {

if(side == 'L') {
bubbles.add(new Being(leftPos, height/2, 'C'));
}
else if(side == 'R') {
bubbles.add(new Being(rightPos, height/2, 'C'));
}

else if(side == 'C') {
bubbles.add(new Being(centerPos, height/2, 'C'));
}

}
}

//multiply at location
void procreate(Being being1, Being being2) {
Being b1 = being1;
Being b2 = being2;
Being b3 = new Being(b1.xloc + random(-5,5), b1.yloc + random(-5,5), 'C');
bubbles.add(b3);
b1.isPregnant = true;
b1.progeny++;
b2.progeny++;

}

//look for mate
void scan(Being h_) {
if(h_.age < consentAge*10 || h_.progeny > maxChildren) {
// do nothing
}
else if(h_.isPregnant) {

if(h_.gestation < gestationPeriod * 10) {
h_.gestation++;
}
else {
h_.isPregnant = false;
}
}
else {
for(int i=0; i<bubbles.size(); i++) {
for(int j=0; j<bubbles.size(); j++) {
if (i != j) {
Being b1 = (Being) bubbles.get(i);
Being b2 = (Being) bubbles.get(j);
//check proximity
if(dist(b1.xloc, b1.yloc, b2.xloc, b2.yloc) < territory && b1.side != b2.side) {
if(random(1) > chanceToScore) {
procreate(b1, b2);
}
}
}
}
}
}
}
}

Minim minim;
AudioInput in;

int consentAge = 16; //can vary
int maxAge = 2000; //chance to die before this value. increment based on population size, size life expectancy should increase with the advent of techn
float gestationPeriod = .75; //fixed to 9 months, with a 2 week buffer
float territory = 15;
int maxChildren = 2;//inverse to population size
//int maxHumans = 1000;//for debugging
float chanceToScore = .99;//inverse to population size. Fewer options equals better chance to score!

float leftPos;
float rightPos;
float centerPos;

color c1 = color(0, 0, 0);
boolean isLoud = false;

ArrayList bubbles = new ArrayList();
World bigBrother = new World();

void setup(){
size(400, 400, P3D);

leftPos = width/4;
rightPos = width - width/4;
centerPos = width/2;

// always start Minim before you do anything with it
minim = new Minim(this);
minim.debugOn();
// get a line in from Minim, default bit depth is 16
in = minim.getLineIn(Minim.STEREO, 16);
}//end setup

void draw(){

stroke(255);
smooth();

background(c1);

float leftVal = 0.0;
float rightVal = 0.0;

int multiplier = 100000;

for(int i = bubbles.size()-1; i >= 0; i--) {
Being h = (Being) bubbles.get(i);
if (!h.isAlive) {
bubbles.remove(i);
}
}

for(int i = bubbles.size()-1; i >= 0; i--) {
Being h = (Being) bubbles.get(i);
h.drawMe();
h.incrementValues();
h.checkAge();
}

for(int i = 0; i < in.bufferSize() - 1; i++) {

leftVal = in.left.get(i)*multiplier;
rightVal = in.right.get(i)*multiplier;

float n = abs(in.mix.get(i))*multiplier;

bigBrother.spawn(int(leftVal/100), 'L');
bigBrother.spawn(int(rightVal/100), 'R');

println(n);
}//end for

}//end draw

void stop()
{
// always close Minim audio classes when you are done with them
in.close();
minim.stop();

super.stop();
}//end stop