yalbert-body

I really struggled to come up with a compelling idea for this project. Initially, I wanted to do something with hands using openpose. Unfortunately, it was too slow on my computer to use live.

After unsuccessfully attempting openpose, I shifted my focus to FaceOSC. I explore a couple different options, including a rolling ping pong ball, before settling on this. Initially inspired by a project Marisa Lu made in the class, I wanted to create a drawing tool where the controller is one's face. Early functionality brainstorms included spewing particles out of one's mouth and 'licking' such particles to move them around. Unfortunately, faceOSC's tongue detection system is not great so I had to shift directions.

Thinking back to the image processing project for 15-104, I thought it would be fun if the particles 'revealed' the user's face. Overall, I'm happy with the elegant simplicity of the piece. I like Char's observation when she described it as a 'modern age peephole'.However, I'm still working on a debug mode in case faceOSC doesn't read the mouth correctly.

 

ParticleSystem ps;
import processing.video.*;
 
Capture cam;
 
import oscP5.*;
OscP5 oscP5;
int found;
float[] rawArray;
Mouth mouth;
int particleSize = 3;
int numParticles = 20;
 
void setup() {
  size(640, 480);
  ps = new ParticleSystem(new PVector(width/2, 50));
  frameRate(30);
 
  setupOSC();
  setupCam();
 
  mouth = new Mouth();
}
 
void setupOSC(){
  rawArray = new float[132]; 
  oscP5 = new OscP5(this, 8338);
  oscP5.plug(this, "found", "/found");
  oscP5.plug(this, "rawData", "/raw");
}
 
void setupCam(){
  String[] cameras = Capture.list();
 
  if (cameras.length == 0) {
    println("There are no cameras available for capture.");
    exit();
  } else {
    println("Available cameras:");
    for (int i = 0; i < cameras.length; i++) {
      println(cameras[i]);
    }
 
    // The camera can be initialized directly using an 
    // element from the array returned by list():
    cam = new Capture(this, cameras[0]);
    cam.start();     
  } 
}
 
void draw() {
  if (cam.available() == true) {
    cam.read();
  }
  translate(width, 0);
  scale(-1, 1);
  background(0);  
  mouth.update();
  //mouth.drawDebug();
  ps.origin =new  PVector(mouth.x, mouth.y);
  if(mouth.isOpen || mouth.isBlowing){
      ps.addParticle();
  }
  ps.run();
  //image(cam, 0, 0);
}
 
void drawFacePoints() {
  int nData = rawArray.length;
  for (int val=0; val<nData; val+=2) {
      fill(100, 100, 100);
      ellipse(rawArray[val], rawArray[val+1], 11, 11);
  }
}
 
 
// A class to describe a group of Particles
// An ArrayList is used to manage the list of Particles 
 
class ParticleSystem {
  ArrayList particles;
  PVector origin;
 
  ParticleSystem(PVector position) {
    origin = position.copy();
    particles = new ArrayList();
  }
 
  void addParticle() {
    for(int i = 0; i < numParticles; i++){ particles.add(new Particle(origin)); } } void run() { for (int i = particles.size()-1; i >= 0; i--) {
      Particle p = particles.get(i);
      p.run();
      if (p.isDead()) {
        particles.remove(i);
      }
    }
  }
}
class Mouth{
 boolean isOpen;
 boolean isBlowing;
 float h;
 float w;
 float x;
 float y;
 float xv;
 float yv;
 
 void update(){
   PVector leftEdge = new PVector(rawArray[96], rawArray[97]);
   PVector rightEdge = new PVector(rawArray[108], rawArray[109]);
   PVector upperLipTop = new PVector(rawArray[102], rawArray[103]);
   PVector upperLipBottom = new PVector(rawArray[122], rawArray[123]);
   PVector lowerLipTop = new PVector(rawArray[128], rawArray[129]);
   PVector lowerLipBottom = new PVector(rawArray[114], rawArray[115]);
 
   float lastx = x;
   float lasty = y;
   w = rightEdge.x - leftEdge.x;
   x = (rightEdge.x - leftEdge.x)/2 + leftEdge.x;
   y = (lowerLipBottom.y - upperLipTop.y)/2 + upperLipTop.y;
   h = lowerLipBottom.y - upperLipTop.y;
   float distOpen = lowerLipTop.y - upperLipBottom.y;
   float avgLipThickness = ((lowerLipBottom.y - lowerLipTop.y) + 
                           (upperLipBottom.y - upperLipTop.y))/2;
   if(distOpen > avgLipThickness){ isOpen = true;}
   else { isOpen = false;}
 
   if(w/h <= 1.5){ isBlowing = true;}
   else { isBlowing = false;}
 
   xv = x - lastx;
   yv = y - lasty;
 
 }
 void drawDebug(){
   if(isOpen || mouth.isBlowing){
        strokeWeight(5);
       stroke(255, 255, 255, 150);
       noFill();
       ellipse(x, y, w, h);
   }
 }
}
 
 
// A simple Particle class
 
class Particle {
  PVector position;
  PVector velocity;
  PVector acceleration;
  float lifespan;
 
  Particle(PVector l) {
    acceleration = new PVector(0, 0.00);
    velocity = new PVector(random(-1, 1), random(-2, 0));
    position = l.copy();
    lifespan = 255.0;
  }
 
  void run() {
    update();
    display();
  }
 
  // Method to update position
  void update() {
    velocity.add(acceleration);
    position.add(velocity);
    //lifespan -= 1.0;
    velocity.x = velocity.x *.99;
    velocity.y = velocity.y *.99;
  }
 
  // Method to display
  void display() {
    //stroke(255, lifespan);
    //image(cam, 0, 0);
    float[] col = getColor(position.x, position.y);
    fill(col[0], col[1], col[2]);
    noStroke();
    ellipse(position.x, position.y, particleSize,particleSize);
  }
 
  // Is the particle still useful?
  boolean isDead() {
    if (lifespan < 0.0) { return true; } else { return false; } } } public float[] getColor(float x, float y){ cam.loadPixels(); int index = int(y)*width +int(x); float[] col = {0, 0, 0}; if(index > 0 && index < cam.pixels.length){
    col[0] = red(cam.pixels[index]);
    col[1] = green(cam.pixels[index]);
    col[2] = blue(cam.pixels[index]);
  }
  return col;
}
 
public void rawData(float[] raw) {
  rawArray = raw; // stash data in array
}

yalbert-telematic

Instructions: Click on the screen to build a creature that eats the dot. Work with your opponent, who has a different dot but is controlling the same creature.

Source

Summary: An interactive game in which players are forced to work together to reach their goals.

Within my game, players must try to eat the dots placed around the canvas. While each players' dots are in different locations, they are all navigating using the same springular blob. Therefore, they have to work together to ensure everyone eats their dots. This project was an extension of past spring explorations that I've done. I really like the interactive twist though because it forces a sort of collaboration that people aren't always used to. Even though everyone has separate goals, they have to work together to collectively reach them. Based on the nature of the game, it is best played when the two players on are in the same room. If this isn't possible, the game could also be played with players talking on the phone or texting one another.

yalbert-LookingOutwards03

Name: inForm

Creators: Daniel Leithinger, Sean Follmer

Year: 2013

Source

This is a project that came out of Hiroshi Ishii's incredible Tangible Media Lab. It explores how far we can push interactive surfaces and proves that they don't have to be confined to pixels on a screen. The possibilities of the concept really excite me. Imagine if every surface was covered in this material. Our future could contain interactive spaces that have a physicality to them in a way that doesn't exist in most contemporary visions of the future. One disappointing element of this project was that it was put out in 2013. I'm unsure if any improvements or compelling use cases have been created since then. I'd like to see this concept move beyond an interesting research project and into the public sphere as a consumer product, but I'm unsure when that will actually occur.

yalbert-viewing04

Speculation subverts itself in the overwhelming hodgepodge of digital media that is constantly being produced. Spectacle attempts to elevate above it in order to be considered refined and immortal.

Marco Brambilla's Civilization is interesting in the sense that it takes various spectacles from movies and hodgepodges it into speculation. It is overwhelming by design and almost mocks the artwork it's made out of by stereotyping all items into either heaven, hell or somewhere in between. Based on the dichotomies Warburton presents, it could be categorized as digital acceleration, visible, surplus (in the sense that it's reveling in it's overwhelming nature), art, and dysfunction.

I really like this artwork because it makes a familiar image out of works that are trying to be original and insightful. In other words, it displays how every new item that we as a society tries to produce simply fits within an ancient image of heaven and hell. As much as we can try to escape our traditional concepts of the world, it will follow us.

yalbert-reading03

Although I recognize the importance of first word art, I generally enjoy last word art more. I think this is because in many ways I'm comforted by its traditional nature and find it easier to appreciate the master of craft that is associated with it.

Technologies have an extraordinary impact on culture. One particularly interesting example of this is how modern technology shapes the way we absorb culture. The methods of consumption, instagram, facebook, youtube, affect how we interpret modern culture and therefore the content itself. On the flip side, culture has a tremendous affect on our technology. In particular, America's culture of capitalism and innovation has shaped Silicon Valley's endless drive to innovate, to both negative and positive effects.

I think that when work is technologically novel it ages poorly because work made in new media can't help but be scrappy. No one knows how to use the media yet so the craft is inevitably poor. Making something the second time around is bound to be better simply from learning from the original works' process.

yalbert-clock

For this project, I wanted to capture how time is not necessarily experienced linearly. Often time is is measured by the speed at which moments pass by. When moments are plentiful, they pass by quickly. However, when we only have a few left, we savor them lest they slip away from us. I did my best to express this in a visual way. Making this clock was fairly straightforward as I was able to reuse a particle system from a previous project. However, I'm not thrilled with how the particles clump together oddly when the minute is midway through. I think the clock is most visually pleasing at the beginning and end of the minute. Because my clock is heavily motion based, I chose to use a minute long loop. However, I think this draws away from using the clock for day long timekeeping (the hours are on the left and minutes on the right).

var offset = 25;
var plugOffset = 10;
var cirlceDist = 15;
var miniFill = 170;
var correctnessIndex = 1;
var plugThickness = 15;
var plugTipThickness = 45;
var plugTipHeight = 25;
var miniSize = 450;
 
var particleSystem;
var voiceSystem;
 
var modes = ["off", "listening", "answering"];
var currMode = "off";
 
var speech;
var mic;
 
var scenarios = []
var nullScenario;
var currScenario;
var anotherDayOfSun;
var activate;
var selectAnswers;
var demi;
var instrumental;
var startTime;
 
var instructionsX = 50;
var instructionsY = 50;
 
var barrier = 450
var baselineSec = 0
var resetted = false;
var subjectiveMin = 0
var numActiveParticles = 220;
var r
var g
var b
 
function setup(){
    angleMode(DEGREES);
    createCanvas(700, 700);
    particleSystem = new ParticleSystem();
    particleSystem.reset();
    baselineSec = second();
    subjectiveMin = minute();
 
}
 
function draw(){
    r = map(second()%60 - baselineSec, 0, 60, 0, 255);
    g = map(subjectiveMin, 0, 60, 0, 255);
    b = map(minute(), 0, 24, 0, 255);
 
    print(second(), baselineSec)
    print(second()%60 - baselineSec)
    print("")
 
    background(r, g, b);
    currMode = "listening";
    particleSystem.updateOpacity();
    particleSystem.updateVelocities();
    particleSystem.updatePosition();
    particleSystem.draw();
    // print(resetted)
    if(second()%60 == baselineSec && resetted == false){
      particleSystem.reset();
      resetted = true;
      subjectiveMin =(subjectiveMin + 1)%60
    }
    if(second() != baselineSec){
      resetted = false;
    }
    drawHours();
    drawMins();
}
 
function drawMins(){
  for(i = 1; i <= 2; i++){
    for(j = 1; j <= 30; j++){ if(i == 1){ minValue = j; }else{ minValue = j + 30; } if(subjectiveMin > minValue){
        opacity = 255;
      }else{
        opacity = 100;
      }
      fill(255-r, 255-g, 255-b, opacity);
      ellipse(width - 20 - 12*2+ 12*i, j*12 + 20, 10, 10);
      //text(minValue, width - 20 - 12*2+ 12*i, (j-1)*12 + 20)
 
    }
  }
}
 
function drawHours(){
  for(i = 0; i < 24; i++){ if(hour() > i){
      opacity = 255;
    }else{
      opacity = 100;
    }
    fill(255-r, 255-g, 255-b, opacity);
    ellipse(20, i*12 + 20, 10, 10);
  }
}
 
//courtesy of Golan Levin (http://cmuems.com/2015c/november-4/)
function ParticleSystem(){
    this.bigParticles = [];
    this.myParticles = [];
    this.nParticles = 300;
    this.particleSize = 10;
    this.isVisible = false;
    this.opacity = 0;
    this.reset = function(){
      this.myParticles = [];
      numActiveParticles = 220
        for (var i = 0; i < this.nParticles; i++) {
            var distFromCenter = random(0, miniSize/2);
            var degree = random(0, 360);
            var rx = cos(degree)*distFromCenter + width/2;
            var ry = sin(degree)*distFromCenter;
            this.myParticles[i] = new Particle(rx, ry, 10, random(.7, 1), true);
        }
 
        for(i = 0; i < 80; i+=2){
          var x1 = width/2 + i*3 + 10;
          var x2 = width/2 - i*3 - 10;
          var y = -i*3 + barrier;
          this.myParticles[i] = new Particle(x1, y, 10, random(.7, 1), false);
          this.myParticles[i+1] = new Particle(x2, y, 10, random(.7, 1), false);
        }
    }
 
    this.setForScenario = function(scenario){
        if(this.bigParticles.length != scenario.possibleAnswers.length){
            for(ansInd = 0; ansInd < scenario.possibleAnswers.length; ansInd++){
                this.addToBigParticles(scenario.answerProbabilities[ansInd]*250);
            }
        }
        for(ind = 0; ind < this.bigParticles.length; ind++){
            if(ind == scenario.selectedInd){
                this.bigParticles[ind].answering = true;
            }else{
                this.bigParticles[ind].answering = false;
            }
        }
    }
 
    this.addToBigParticles = function(goalSize){
        index = floor(random(0, particleSystem.nParticles));
        changingParticle = particleSystem.myParticles[index];
        changingParticle.goalSize = goalSize;
        particleSystem.bigParticles.push(changingParticle);
    }
 
    this.updateVelocities = function(){
        var gravityForcex = 0;
        var gravityForcey = -.17;
 
 
        for (var i = 0; i < this.myParticles.length; i++) {
            var ithParticle = this.myParticles[i];
            var px = ithParticle.px;
            var py = ithParticle.py;
 
 
            ithParticle.addForce(gravityForcex, gravityForcey);
 
            for (var j = 0; j < i; j++) { var jthParticle = this.myParticles[j]; var qx = jthParticle.px; var qy = jthParticle.py; var mutualAttractionAmount = (ithParticle.mass * jthParticle.mass)/2; var dx = px - qx; var dy = py - qy; var dh = sqrt(dx*dx + dy*dy); var componentInX = dx/dh; var componentInY = dy/dh; var proportionToDistanceSquared = 1.0/(dh*dh); var attractionForcex = mutualAttractionAmount * componentInX * proportionToDistanceSquared; var attractionForcey = mutualAttractionAmount * componentInY * proportionToDistanceSquared; if (dh > (ithParticle.mass + jthParticle.mass)/2 * 8) {
                    ithParticle.addForce( attractionForcex,  attractionForcey); // add in forces
                    jthParticle.addForce(-attractionForcex, -attractionForcey); // add in forces
                }else{
                    ithParticle.addForce( -attractionForcex,  -attractionForcey); // add in forces
                    jthParticle.addForce(attractionForcex, attractionForcey);  // add in forces
                }
            }
        }
    }
 
    this.updatePosition = function(){
        for (var i = 0; i < this.myParticles.length; i++) {
                particle = this.myParticles[i];
                particle.update();
        }
    }
 
    this.draw = function(){
        for (var i = 0; i < this.myParticles.length; i++) { particle = this.myParticles[i]; particle.render(this.opacity); } } this.updateOpacity = function(){ if(this.isVisible == false){ //if(this.opacity > 0){
                this.opacity = this.opacity*.9 +0*.1;
            //}
        }else{
            //if(this.opacity < 255){
                this.opacity = this.opacity*.9 + 255*.1 ;               
            //}
        }
 
    }
}
 
 
 
 
//courtesy of Golan Levin (http://cmuems.com/2015c/november-4/)
function Particle(x, y, mass, personalOpacity, canMove){
    this.personalOpacity = personalOpacity;
    this.size = mass;
    this.px= x;
    this.py=y;
    this.vx= 0;
    this.vy=0;
    this.damping = 0.96;
    this.mass = (pow(mass, 3))/100;
    this.bLimitVelocities=true;
    this.bPeriodicBoundaries=false;
    this.bElasticBoundaries=true;
    this.goalSize = mass;
    this.answering = false;
    this.opacity = 0;
    this.movable = canMove;
    this.passedThrough = false;
 
    this.addForce=function(fx,fy){
        var ax=fx/this.mass*1.5;
        var ay=fy/this.mass*1.5;
        if(this.goalSize < 30){ this.vx+=ax; this.vy+=ay; }else{ this.vx+=ax*.1; this.vy+=ay*.1; } } this.update=function(){ this.vx*=this.damping; this.vy*=this.damping; this.limitVelocities(); this.handleBoundaries(); if(this.movable){ this.py-=this.vy; this.px-=this.vx; } // if(abs(this.goalSize-this.size) > 1){
        //     this.size = this.size*.9 + this.goalSize*.1;
        // }
 
        if(this.py > barrier){
          if(this.passedThrough == false){
            numActiveParticles -=1
          }
          this.passedThrough = true
        }
    }
    this.limitVelocities=function(){
        if(this.bLimitVelocities){
            var speed=sqrt(this.vx*this.vx+this.vy*this.vy);
            var maxSpeed=10;
            if(speed>maxSpeed){
                this.vx*=maxSpeed/speed;this.vy*=maxSpeed/speed;
            }
        }
    }
    this.inBounds=function(){
        return(this.px<width&&this.px>0&&this.py<height&&this.py>0);
    }
    this.handleBoundaries=function(){
        if(this.bPeriodicBoundaries){
            if(this.px>width)
                this.px-=width;
            if(this.px<0) this.px+=width; if(this.py>height)
                this.py-=height;
            if(this.py<0) this.py+=height; }else if(this.bElasticBoundaries){ if(particle.py > height){
                this.vy = this.vy*-1; // draw all particles
                this.vx = this.vx*-1;
            }
        }
    }
    this.render=function(parentOpacity){
 
        if(abs(this.goalSize-this.size) < 15 && this.goalSize < 20){
 
                // this.opacity = 255 -getDist(this.px, this.py, width/2, height/2)*1.2 //+ this.size*this.size;
                // this.opacity = this.opacity*this.personalOpacity 
            noStroke();
 
            if(this.movable){
              if(this.passedThrough == false){
                opacity = 255;
              }else{
                opacity = map(numActiveParticles, 220, 0, 100, 25) //50;
              }
 
              fill(255, 255, 255, opacity);
              ellipse(this.px,this.py,this.size,this.size);
            }
        }else{
            if(this.answering){
                numLayers = 5;
            }else{
                numLayers = 1;
            }
           for(j = 0; j < numLayers; j++){
                diameter = this.size - j*5
                r = lerp(255, 40, 1 - j*.1);
                g = lerp(255, 140, 1 - j*.1);
                b = lerp(255, 250, 1 - j*.1);
 
                fill(r, g, b, 255*(parentOpacity/255));
                ellipse(this.px,this.py,diameter,diameter);
           }
 
        }
    }
}
 
function getDist(x1, y1, x2, y2){
    return sqrt(pow(x1 - x2, 2) + pow(y1 - y2, 2))
}
 
//for testing
 
function mousePressed(){
 
}
 
function keyPressed(){
    if(particleSystem.isVisible){
        particleSystem.isVisible = false;
    }else{
        particleSystem.isVisible = true;
    }
}

yalbert-LookingOutwards02

microsurface video from Maayan Albert on Vimeo.

Title of Work: Microsurface
Artist: AlteredQualia
Link

This is a generative art project that features a series of multicolored cubes that rotate when you move over them. I admire how engaging the work is and how the individual cubes form a larger pattern. I suppose that this algorithm simply generates many three dimensional cubes that are programmed to rotate in response to the mouse. Some easing function is probably involved in the motion and the colors are likely generated by perlin noise or some similar function. I admire how organic the motion feels and how it is juxtaposed by the technological modernity implied by the colors and medium.

yalbert-AnimatedLoop

Although this pretty much turned out as an elaborate loading bar, I enjoyed making this piece. I'm partial to animation and using an easing technique was a good way to hone that skill. One part of this that I struggled with was color balance. It took me a while to experiment with black on white, white on black, and other colors until I finally settled on this combination. An element that I didn't fully resolve was the relationship between the spiral and the canvas. Making it too small made it feel like it was floating in space, but when it was too big it fit oddly in it.

// This is a template for creating a looping animation in p5.js (JavaScript). 
// When you press the 'F' key, this program will export a series of images into
// your default Downloads folder. These can then be made into an animated gif. 
// This code is known to work with p5.js version 0.6.0
// Prof. Golan Levin, 28 January 2018
 
// 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 = "nickname";
var nFramesInLoop = 120;
var bEnableExport = true;
var dots = [];
var radius = 1200;
var dotSize = 45;
var worms = [];
var numWorms = 5
var colors = [[255, 0, 0], [0, 255, 0], [0, 0, 255]]
var curColor = [255, 0, 0]
var curColorInd = 0;
 
// 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;
  angleMode(DEGREES)
 
  for(j = 0; j <= numWorms; j++){ var r = lerp(243, 0, (j)/(numWorms)); var g = lerp(206, 0, (j)/(numWorms)); var b = lerp(80, 0, (j)/(numWorms)); var color = [r, g, b] var worm = new Worm(color) worm.setup() worms.push(worm) } } //=================================================== 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!
 
  //----------------------
 
 
 
 
  // here, I set the background and some other graphical properties
  background(0);
  smooth();
  stroke(0, 0, 0);
  strokeWeight(2);
 
 
  // if(percent == 0.75){
  //   curColorInd +=1;
  //   curColorInd = curColorInd % colors.length;
  //   curColor = colors[curColorInd];
  // }
 
  print(percent)
 
  for(j = 0; j < worms.length; j++){
    var r = lerp(curColor[0], 0, (j)/(numWorms));
    var g = lerp(curColor[1], 0, (j)/(numWorms));
    var b = lerp(curColor[2], 0, (j)/(numWorms));
    var color = [r, g, b];
 
    var worm = worms[j];
    worm.col = color
    worm.update((percent + .04 *j)%1, j);
  }
 
 
}
 
function Worm(c){
  this.dots = []
  this.col = c
  this.setup = function(){
    for(i = 0; i< 60; i++){
      dots.push(new Dot(i * .003))
    }
  }
  this.update = function(percent, sizeOff){
    for(i = 0; i < dots.length; i++){
      d = dots[i];
      d.calculatePos(percent);
      d.draw(this.col, sizeOff);
    }
  }
}
 
function Dot(off){
  this.offset = off
  this.x = 0
  this.y = 0
  this.radius = map(this.offset, 0, 1, 0, radius)
  this.angle = 0
  this.eased = 0
 
  this.calculatePos = function(percent){
    this.eased = quadraticInOut((percent + this.offset)%1);
    this.eased = (this.eased - 0.25)%1.0; // shifted by a half-loop, for fun
    this.angle = map(this.eased, 0, 1, 0, 360); 
 
    this.x = cos(this.angle)*this.radius + width/2;
    this.y = sin(this.angle)*this.radius + height/2;
  }
 
  this.draw = function(color, sizeOff){
    fill(color);
    noStroke();
    ellipse(this.x, this.y, dotSize + sizeOff*2, dotSize + sizeOff*2);
  }
}
 
 
// 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 gompertz(_x, _a){
    if(!_a) _a = 0.25; // default
    var min_param_a = 0.0 + Number.EPSILON;
    _a = max(_a, min_param_a);
 
    var b = -8.0;
    var c = 0 - _a*16.0;
    var _y = exp( b * exp(c * _x));
 
    var maxVal = exp(b * exp(c));
    var minVal = exp(b);
    _y = map(_y, minVal, maxVal, 0, 1);
 
    return(_y);
}
 
function quadraticInOut(_x) {
    if(_x < 0.5)
    {
      return(8 * _x * _x * _x * _x);
    }
    else
    {
      var _v = (_x - 1);
      return(-8 * _v * _v * _v * _v + 1);
    }
}
function sineOut(_x) {
    return(sin(_x * HALF_PI));
  }
 
function cubicIn(_x) {
    return(_x * _x * _x);
}

yalbert-Reading02

1. One example of effective complexity that I am particularly fond of is the sound of shoes walking through gravel. Initially I assumed that similar to white noise and static, this would be an example of total randomness. However, upon further thought it occurred to me that there is some order and rhythm to it because of the way a shoe distributes weight as it steps.

 

2. I relate to the problem of authenticity. This is because I often times see generative art as an opportunity to solve technical problems and get lost in these challenges as opposed to the form of expression itself. However, in the end the value I derive from such projects is the self expression, so I would say that generative art is authentic.

yalbert-Interruptions

This project was an interesting challenge because it forced me to evaluate the correctness of my work in an analogue as opposed to digital manner. By this I mean that I was not evaluating whether my project worked or not in absolute terms. Instead, I was observing its correctness in a fluid manner. There were many instances where I looked at it and thought, this almost seems right, but it isn't quite there yet. This is a workflow that I'm accustomed to in design classes but feels foreign in the context of a computationally based course. That being said, I really enjoyed working on this. In particular, computing line visibility on a grid taught me a new thing about Perlin noise. I've been using it for a few years but didn't fully understand how it worked until this project, which was really exciting.

var segs = [];
var margin = 20;
var segLength = 20;
var numSegs = 1500;
var clicked = false;
var segsSpacing = 13;
var xVisOff = 0.0;
var yVisOff = 0.0;
 
function setup() {
  angleMode(DEGREES);
  createCanvas(555, 555);
  resetSegs();
}
 
function draw() {
  background(255);
  if(clicked){
    clicked = false;
    resetSegs();
  }
  drawSegs();
}
 
function resetSegs(){
  segs = [];
  for(i = 0; i < sqrt(numSegs); i++){
    xVisOff = xVisOff + 0.2;
    yVisOff = 0.0
    for(j = 0; j < sqrt(numSegs); j++){
      yVisOff = yVisOff + 0.2;
      var x1 = i*segsSpacing + margin*1.5;
      var y1 = j*segsSpacing + margin;
      var secondPoint = setSecondPoint(x1, y1);
      var x2 = secondPoint[0];
      var y2 = secondPoint[1];  
      newSeg = new Segment(x1, x2, y1, y2, setVisibility());
      segs.push(newSeg);
    }
  }
}
 
//uses perlin noise to determine whether the line is visible or not
function setVisibility(){
  var opValue = noise(xVisOff, yVisOff);
  if(opValue > .3){
    return true;
  }else{
    return false;
  }
}
 
function drawSegs(){
  for(i = 0; i < numSegs; i++){
    segs[i].drawSeg();
  }
}
 
 
function mousePressed() {
  clicked = true;
}
 
function Segment(x1, x2, y1, y2, visibility){
  this.x1 = x1;
  this.x2 = x2;
  this.y1 = y1;
  this.y2 = y2;
  this.drawSeg = function(){
    stroke(0);
    strokeWeight(1);
    if(this.isVisible){
      line(this.x1, this.y1, this.x2, this.y2);      
    }
  }
  this.isVisible = visibility;
}
 
//Calculates the second point by selecting a point at the edge of a circle
//where the radius is the desired length of the lines and the center
//is the first point
function setSecondPoint(x1, y1){
  var angle = random(50, 150);
  var y2 = sin(angle) * segLength + y1;
  var x2 = cos(angle) * segLength + x1; 
  return [x2, y2];
}