chromsan-telematic


Add a whisper of text and try to interpret everyone else's whispers as each changes little by little.

 

In short, this is the game of telephone for random people on the internet with text instead of audio.

I wanted to implement this game (sometimes called "Chinese whispers," evidently) with text instead of voice while preserving the same quality of randomness that it has. I initially thought about using a reCAPTCHA style system to mess up the text, but that would have been too easy to read. Messing with the unicode made it much more difficult to read the messages, and I found a nice library for doing that. What's interesting about the game telephone is that everyone has an important role in playing since they each pass the message along, but all the players are equal in the sense that everyone's job is the same, except for one. In the original game, the person that starts the message is somewhat more important, but I got rid of this dynamic by having each visitor enter their own message, which is then interpreted by the other visitors.  In my version, all the players have the same role.

I had the idea to keep the messages so that the next visitor could add to the chain of whispers, which Glitch didn't seem to easily be able to do. I then decided to make my life much more difficult by shifting over to python and making a backend with Django, which I then put on Heroku, which was probably really unnecessary especially for this one feature. However, now the whispers that are added are persistent, so anyone can add to each of the chains and the initial phrases can keep morphing- they don't disappear when no one is online.

Direct link to project.

Initial ideas about messing up the text

Project's code can be found on GitHub.

 

chromsan-viewing04

Spectacle: things made with the intention to 'wow' the audience through technical means or particularly beautiful aesthetics.

Speculation: things made with the intention to critique, either something else or itself- usually very self aware.

This is a demo for Unreal Engine's upcoming support for Ray Tracing capabilities with new Nvidia RTX graphics cards. It was developed by Epic Games and Industrial Light and Magic. Defined in Warburton's terms, it is pure spectacle. It's meant to showcase a new technical feature while serving as advertising for both Unreal Engine and Disney. Technically, it is really quite impressive; it could have been lifted from the next Star Wars movie. It has a little narrative, and the characters are familiar, but there's nothing critical or self-aware about it. It's meant to accelerate technology and real time rendering, and it is meant to be very visible considering it is essentially an advertisement, and it has around 1.5 million views on YouTube. The demo is commerce, it is really quite distinct from the world of critical art. I would say it errs on the side of surplus, it's beautiful and clean and new, but probably unnecessary. Finally, it is functional in that it has a clear purpose and has excelled at fulfilling that purpose.

 

 

chromsan-clock

Prevent time from passing by knocking away the incoming seconds. The more you prevent time from passing, the faster the seconds will come. If one of the seconds makes it past you, time is reset and can even go into the future.

(use side to side arrow keys to control the paddle)

I liked the idea of making some sort of a game for this project, as time is usually quite a large part of games. I also wanted to make a clock that was not quite accurate, so that you could have some control over the clock's speed. I took inspiration from Pong, so that the player can prevent time from being added to the clock. To give a bit of a challenge, I made time speed up as the player is more successful. This way, if they miss, time is both reset and a couple seconds may be added to the clock. The main issue with using p5.js for this project is that the animation stops drawing when you leave a tab, so the time that is drawn will be different from the real time, and the scoring system gets messed up. I'd like to find a way around this.

(update on 9/25- I've found a kinda hacky way around this to fix the score and time, embedded sketch has been updated)

The green theme denotes AM and red denotes PM.

Original ideas:

Code also on GitHub.

// real time (not based on game)
let realMS, realS, realM, realH; 
// game time (based on game performance)
let gameS, gameM, gameH; 
// previous logged times 
let p_realS, p_realM, p_realH; 
// how often the times appear [0-999]
const timeFreqInit = 999;
let timeFreq = timeFreqInit, timeFreqInc = 20;
// assorted global vars 
const deflectorSpeed = 15, timeSpeed = -10;
const dispTimeY = 80;
 
let deflector, time = 0;
let times = [], score = 1, dispScore = 0, highScore = 0;
let font, fSize = 60, fillCol, backCol;
let millisRolloverTime = 0; 
 
function preload() {
  font = loadFont('digital-7.ttf');
}
 
function setup() {
  createCanvas(600, 750, P2D);
  textFont(font);
  textSize(fSize);
  updateRealTime();
  // to start, game time is the same as real time
  gameS = realS;
  gameM = realM;
  gameH = realH;
  // if it's AM, color green, otherwise red 
  setAmPmCol();
  // paddle 
  deflector = new Deflector(10, 100, width/2, height/4);
}
 
function draw(){
  setAmPmCol();
  fill(fillCol);
  textSize(fSize);
  background(backCol);
  updateRealTime();
  drawGameTime();
  // every time theres a new second...
  if (p_realS != realS){
    millisRolloverTime = millis();
    p_realS = realS;
  }
  realMS = floor(millis() - millisRolloverTime);
  // add a new time based off freq
  if (realMS % timeFreq == 0) {
    time++;
    let expanded = secondsToMin(time);
    let temp = {s: expanded.s, m: expanded.m, h: realH, disp: expanded.s};
    times.push(new Time(temp, random(30, width - 30), height));
  }
 
  for (let i in times) {
    if (times[i].offscreen()){
      // remove offscreen times
      times.splice(i, 1);
    }
    if (times[i].intersects(deflector)){
      // remove times after hitting paddle
      times[i].bounceMove(random(-10,10), random(6, 12));
      if (score > highScore) { highScore = score }
    } 
    if (times[i].escaped() && !times[i].t.used){
 
      // if the time made it past the paddle
      times[i].t.used = true;
 
      if (-score <= 0){ let res = addToTime(gameS, gameM, gameH, times[i].t.s, times[i].t.m); gameS = res.s; gameM = res.m; gameH = res.h; } // measure the diff between real and game time to get score let d1 = new Date(2018, 9, 20, gameH, gameM, gameS); let d2 = new Date(2018, 9, 20, realH, realM, realS); let d = d2.getTime() - d1.getTime() if (realS - gameS > 0) {score = d /1000;}
      else {score = realS - gameS}
      time = 0;
      timeFreq = timeFreqInit;
    }
    if (times[i].goingUp) {
      // move the times up by speed
      times[i].move(timeSpeed);
    }
    times[i].update();
    times[i].draw();
  }
  deflector.update();
  deflector.draw();
  drawScore();
}
 
function keyReleased() { 
  if (keyIsDown(RIGHT_ARROW)) { deflector.move(deflectorSpeed) }
  else if (keyIsDown(LEFT_ARROW)) { deflector.move(-deflectorSpeed) }
  else { deflector.move(0) }
}
 
function keyPressed() {
  if (keyCode == LEFT_ARROW){ deflector.move(-deflectorSpeed)} 
  if (keyCode == RIGHT_ARROW){ deflector.move(deflectorSpeed)} 
}
 
function updateRealTime() {
  realS = second();
  realM = minute();
  realH = hour() % 12;
}
 
function drawGameTime() {
  textAlign(CENTER);
  text(gameH, width/4, dispTimeY);
  text(":", width/8 * 3, dispTimeY)
  text(gameM, width/4 * 2, dispTimeY);
  text(":", width/8 * 5, dispTimeY)
  text(gameS, width/4 * 3, dispTimeY);
}
 
function drawScore() {
  textSize(25);
  textAlign(CENTER);
  let res = secondsToMin(score);
  let disp, pos = "";
  if (score < 0) {pos = "+"} // check if we need to display minutes if (res.m > 0) {disp = pos + (-1 * res.m) + ":" + res.s}
  else {disp = pos + (-1 * res.s)}
  text(disp, width - 80, 80);
  textSize(12);
  //text("s", width - 150, height- 20);
  textSize(25);
  text("Max:  " + highScore, width - 75, height - 20);
  textSize(12);
  text("s", width - 30, height- 20);
}
 
function secondsToMin(sec) {
  let secRl = sec % 60;
  let minRl = Math.floor(sec / 60);
  return {s: secRl, m: minRl};
}
 
function addToTime(pS, pM, pH, s, m) {\
  // add seconds and minutes new seconds and minutes
  let fixedS = pS + s, fixedM = pM + m, fixedH = pH;
  // case check for if any go over 60 
  if (fixedS >= 60) { 
    let res = secondsToMin(fixedS);
    fixedS = res.s;
    fixedM += res.m;
  }
  if (fixedM >= 60){
    // we can use the same function for mins + hrs
    let res = secondsToMin(fixedM);
    fixedM = res.s;
    fixedH += res.m;
  }
  if (fixedH > 12){
    // kinda hacky but really unlikely to happen
    fixedH = hour() % 12;
  }
  return {h: fixedH, m: fixedM, s: fixedS}
}
 
function setAmPmCol() {
  if (hour() > 12) {fillCol = color(205, 0, 0); backCol = color(20, 0, 0)}
  else { fillCol = color(0, 205, 0); backCol = color(0, 20, 0) }
}
 
// the 'paddle' that used to hit the numbers
class Deflector {
  constructor(h_, w_, x_, y_){
    this.height = h_;
    this.width = w_;
    this.x = x_;
    this.y = y_;
    this.deltaX = 0;
  }
  update() {
    this.x += this.deltaX;
    this.x = constrain(this.x, this.width/2, width - this.width/2);
  }
  move(steps) { this.deltaX = steps }
 
  draw() {
    textAlign(CENTER);
    textSize(70);
    text("___", this.x, this.y);
    textSize(fSize); 
  }
}
 
// the numbers that appear
class Time {
  constructor(t_, x_, y_) {
    this.t = t_;
    this.x = x_;
    this.y = y_;
    this.deltaY = 0;
    this.deltaX = 0;
    this.goingUp = true;
    this.col = color(fillCol.levels[0], fillCol.levels[1], fillCol.levels[2]);
    this.used = false;
  }
  update() {
    this.y += this.deltaY;
    this.x += this.deltaX;
  }
  move(steps) { 
     this.deltaY = steps;
  }
  bounceMove(stepX, stepY){
    this.deltaX = stepX;
    this.deltaY = stepY;
  }
  escaped(){
    if (this.y < height/4) { return true }
    else {return false}
  }
 
  intersects(deflector) { 
    // from https://developer.mozilla.org/en-US/docs/Games/Techniques/2D_collision_detection
    if (deflector.x < this.x + 50 && deflector.x + deflector.width > this.x + 40 &&
        deflector.y < this.y + 10 && deflector.y + deflector.height > this.y - 40) {
      if (this.goingUp) {
        // if the time hits the deflector...
        score++; 
        if (score > 0 && timeFreq >= 750){
          timeFreq -= timeFreqInc;
        }
 
      }
      this.goingUp = false;
      this.t.disp = '#'
      return true;
    }
    return false;
  }
 
  offscreen() {
    if (this.y < -40 || this.y > height + 40) return true;
    else return false;
  }
 
  draw() {
    textAlign(CENTER);
    if (this.escaped()){
      let a = this.col.levels[3];
      this.col.setAlpha(a - 15);
      fill(this.col);
      text(this.t.disp, this.x, this.y);
    } else {
      fill(fillCol);
      text(this.t.disp, this.x, this.y);
 
    }
 
  }
}

chromsan-LookingOutwards02

This is a project by Marcin Ignac entitled "City Icon" from 2012. It's a generative city simulation with a variety of randomly created systems. Their interactions create a dynamic and constantly changing view of an imaginary city. I like the balance between the order of a city and the randomness in the direction that its parts take. How the water flows through and energy sources appear is fairly random, but what's impressive is that it is still is identifiable as a city, like you might see from an airplane above when flying at night. The generation transforms when it becomes a series of cells that make up a living organism or a collection of energy sources. I imagine there was quite a bit of pseudo-random number generation involved in making this project, as its parts seem to relate in a nice way.

Original source. 

chromsan-reading03

I think it's interesting that Naimark sees such a distinction between these types of art. Naimark claims that half the art world sees "first word" art as true art and the other half sees"last word" as true art. So, if we take that to be the case, then good art only comes around at the start or end of an artistic era. This seems silly, especially considering our classifications of the artistic periods are approximations, and there are many examples of outstanding artists working uncharacteristically differently for their time period. How can it be that we compare Giotto to Raphael and make a value judgement about their works simply based off of the technologies and knowledge that each artist had in his time?

We can only examine each artist and their work within the context of their lifetimes, and from there decide if it is good or not. Otherwise, we end up making choices solely based off of variables outside of the artists' control. Would it make sense to make the claim that since computers and computer art has advanced to a vastly more complex state, Vera Molnár's works are bad? Or conversely that her works are the best representation of computer art because they were the first? These seem like difficult propositions to back up.

Technological innovation, culture, and the art that follows is a long lineage that builds on the past. Certainly, Beethoven took what Haydn built and added his own take to it; Beethoven moved Haydn's creation beyond its original state. Is it better or worse for that? Neither. It's something different.

chromsan-AnimatedLoop

For this project, I wanted to make something using just geometry made in processing and try to give it a hypnotic quality. I also wanted to play with 2D and 3D shapes and try and find ways to create illusions with their combinations. I experimented a bit and decided on making a set of triangles which merge into a square, which then becomes a pyramid viewed above, which then becomes a triangular hole. After creating this, I wasn't really getting the hypnotic quality I was going for so I added a ball for your eye to follow around the screen, also playing with the physics of the ball and how it interacts with the shapes, in addition to some random movement to give the illusion of air resistance. When coding the loop, the most helpful function, by far, was the map function. I used this to control how every movement is played and the amount of time that each takes as a percentage of the total time. In addition, I added the double exponential sigmoid function to liven up the movements. I liked the way that it gives the transitions a snappy feel. I'm fairly happy with the result, but I think the loop could be reworked towards the end to create a cleverer transition to the beginning, which is much more interesting.

Code is formatted incorrectly by wordpress, the original file is on GitHub

// looping template by
// Prof. Golan Levin, January 2018
 
// Global variables. 
String  myNickname = "nickname"; 
int     nFramesInLoop = 260;
int     nElapsedFrames;
boolean bRecording; 
float myScale = 0.01;
float radius = 100.0;
 
void setup() {
  size (640, 640, P3D); 
  bRecording = false;
  nElapsedFrames = 0;
  frameRate(40);
}
 
void keyPressed() {
  if ((key == 'f') || (key == 'F')) {
    bRecording = true;
    nElapsedFrames = 0;
  }
}
 
void draw() {
 
  // Compute a percentage (0...1) representing where we are in the loop.
  float percentCompleteFraction = 0; 
  if (bRecording) {
    percentCompleteFraction = (float) nElapsedFrames / (float)nFramesInLoop;
  } else {
    percentCompleteFraction = (float) (frameCount % nFramesInLoop) / (float)nFramesInLoop;
  }
 
  // Render the design, based on that percentage. 
  renderMyDesign (percentCompleteFraction);
 
  // If we're recording the output, save the frame to a file. 
  if (bRecording) {
    saveFrame("frames/" + myNickname + "_frame_" + nf(nElapsedFrames, 4) + ".png");
    nElapsedFrames++; 
    if (nElapsedFrames >= nFramesInLoop) {
      bRecording = false;
    }
  }
}
 
void renderMyDesign (float percent) {
 
  smooth(); 
  // create a rounded percent for easy positioning within the loop
  int roundedPercent = round(percent * 100);
 
  pushMatrix();
 
  if (roundedPercent >= 0 && roundedPercent <= 33){ background(201, 197, 209); noStroke(); // triangles moving in from edges to center float eased = function_DoubleExponentialSigmoid(map(percent, 0, 0.34, 0, 1), 0.8); fill(112, 88, 124); triangle(0, height, width, height, width/2, map(eased, 0, 1, height, height/2 )); fill( 57, 43, 88); triangle(0, 0, 0, height, width/2 * map(eased, 0, 1, 0, 1), height/2); fill(45, 3, 32); triangle(0, 0, width, 0, width/2, height/2 * map(eased, 0, 1, 0, 1)); fill(108, 150, 157); triangle(width, 0, width, height, map(eased, 0, 1, width, width/2), height/2); // rectangle in center shrinking float rectWidth = map(eased, 0, 1, width, 0); float rectHeight = map(eased, 0,1, height, 0); fill(219, 216, 224); rect((width- rectWidth)/2, (height - rectHeight)/2, rectWidth, rectHeight); } else if (roundedPercent > 33 && roundedPercent <= 66) { pushMatrix(); translate(width/2, height/2, 0); ortho(-width/2, width/2, -height/2, height/2); float eased = function_DoubleExponentialSigmoid(map(percent, 0.33, 0.67, 0, 1), 0.9); // Move from above to side, rotate 45 deg as well rotateX(PI/map(eased, 0, 1, 1200, 2)); rotateZ(PI/map(eased, 0, 1, 1200, 2)); background(201, 197, 209); // 3D Triangle fill(45, 3, 32); beginShape(); scale(map(percent, 0.33, 0.66, 3.6, 1.8), map(percent, 0.33, 0.66, 3.6, 1.8), map(percent, 0.33, 0.66, 3.6, 1.8)); vertex(-100, -100, -100); vertex( 100, -100, -100); vertex( 0, 0, 100); endShape(); fill(108, 150, 157); beginShape(); vertex( 100, -100, -100); vertex( 100, 100, -100); vertex( 0, 0, 100); endShape(); fill(112, 88, 124); beginShape(); vertex( 100, 100, -100); vertex(-100, 100, -100); vertex( 0, 0, 100); endShape(); fill( 57, 43, 88); beginShape(); vertex(-100, 100, -100); vertex(-100, -100, -100); vertex( 0, 0, 100); endShape(); popMatrix(); } else if (roundedPercent > 66) {
 
    float eased = function_DoubleExponentialSigmoid(map(percent, 0.66, 1, 0, 1), 0.9);
    // transition from pale blue to dark purple
    color start = color(108, 150, 157);
    color end = color(102, 71, 92);
    color lerpedCol = lerpColor(start, end, eased);
 
    if (roundedPercent < 83){
 
     background(201, 197, 209);
     fill(lerpedCol);
     translate(0,0);
     // replace 3D triangle with 2D triangle (nasty specific values)
     triangle(map(eased, 0, 1, 140, -140), map(eased, 0, 1, 500, 700), 
              map(eased, 0, 1, 500, 700), map(eased, 0, 1, 500, 700), 
              map(eased, 0, 1, 320, 320), map(eased, 0, 1, 140, -140));
    }
    else {
      // square move in from center to complete loop
      float eased2 = function_DoubleExponentialSigmoid(map(percent, 0.82, 1, 0, 1), 0.5);
      background(lerpedCol);
      float rectWidth = map(eased2, 0, 1, 0, width);
      float rectHeight = map(eased2, 0, 1, 0, height);
      fill(219, 216, 224);
      rect((width- rectWidth)/2, (height - rectHeight)/2, rectWidth, rectHeight);
    }
  }
  popMatrix();
  pushMatrix();
  renderBall(percent, roundedPercent);
  popMatrix();
}
 
// weird bouncing ball shenanigans 
void renderBall(float percent, int roundedPercent) {
 
  // make nose map that gives ball a little random sway 
   int currStep = frameCount % nFramesInLoop;
   float t = map(currStep, 0, nFramesInLoop, 0, TWO_PI); 
   float px = width/2.0 + radius * cos(t); 
   float py = width/2.0 + radius * sin(t);
   float noiseAtLoc = height - 100.0 * noise(myScale*px, myScale*py);
   float noiseAdjusted = map(noiseAtLoc, 570, 620, -15, 15);
   float xDrift = noiseAdjusted;
   float yDrift = noiseAdjusted;
 
 
    if (roundedPercent <= 25){ // ball moves towards bottom, larger float x = lerp(width/2, width/2 + 100, map(percent, 0, 0.25, 0, 1)); float y = lerp(height/2, height - 200, map(percent, 0, 0.25, 0, 1)); float scale = lerp(5, 75, map(percent, 0, 0.25, 0, 1)); translate(x + xDrift, y + yDrift, 400); fill(map(scale, 85, 5, 255,50)); sphere(scale); } else if (roundedPercent > 25 && roundedPercent <= 55) { // ball moves towards center, smaller float x = lerp(width/2 + 100, width/2, map(percent, 0.25, 0.55, 0, 1)); float y = lerp(height - 200, height/2, map(percent, 0.25, 0.55, 0, 1)); float scale = lerp(75, 2, map(percent, 0.25, 0.55, 0, 1)); translate(x + xDrift, y + yDrift, 400); fill(map(scale, 85, 5, 255, 50)); sphere(scale); } else if (roundedPercent > 55 && roundedPercent <= 60) { // ball moves to side float x = lerp(width/2, 15, map(percent, 0.55, 0.6, 0, 1)); float y = lerp(height/2, height/2, map(percent, 0.55, 0.6, 0, 1)); float scale = lerp(5, 25, map(percent, 0.55, 0.6, 0, 1)); translate(x + xDrift, y + yDrift, 400); fill(map(scale, 75, 5, 255, 50)); sphere(scale); } else if (roundedPercent > 60 && roundedPercent <= 66) { // ball goes right float x = lerp(15, width/4, map(percent, 0.6, 0.66, 0, 1)); float y = lerp(height/2, height/3 * 2, map(percent, 0.6, 0.66, 0, 1)); //float scale = lerp(5, 25, map(percent, 0.6, 0.6, 0.66, 1)); translate(x + xDrift, y + yDrift, 400); fill(map(25, 75, 5, 255, 50)); sphere(25); } else if (roundedPercent > 66 && roundedPercent <= 85) { // ball gets smaller, towards center float x = lerp(width/4, width/2, map(percent, 0.6, 0.85, 0, 1)); float y = lerp(height/3 * 2, height/2, map(percent, 0.6, 0.85, 0, 1)); float easedx = function_DoubleExponentialSigmoid(map(x, width/4, width/2, 0, 1), 0.8); float easedy = function_DoubleExponentialSigmoid(map(y, height/3 * 2, height/2, 0, 1), 0.8); float scale = lerp(25, 15, map(percent, 0.66, 0.85, 0, 1)); translate(map(easedx, 0, 1, width/4, width/2) + xDrift, map(easedy, 0, 1, height/3 * 2, height/2) + yDrift, 400); fill(map(scale, 75, 5, 255, 50)); sphere(scale); } else if (roundedPercent > 85 && roundedPercent <= 100) {
    // ball gets larger 
    float scale = lerp(15, 5, map(percent, 0.85, 1, 0, 1));
    translate(width/2 + xDrift, height/2 + yDrift, 400);
    fill(map(scale, 75, 5, 255, 50));
    sphere(scale);
  }
}
 
// https://github.com/golanlevin/Pattern_Master
float function_DoubleExponentialSigmoid (float x, float a) {
  // functionName = "Double-Exponential Sigmoid";
 
  float min_param_a = 0.0 + EPSILON;
  float max_param_a = 1.0 - EPSILON;
  a = constrain(a, min_param_a, max_param_a); 
  a = 1-a;
 
  float 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;
}

chromsan-Scope

PDF: praxinoscope-output

This design is essentially four triangles whose top point moves towards the center. I made it as a practice for my main GIF, which uses this same effect. I experimented with coloring the triangles, but in the end settled for a black and white combination, which sort of gives the illusion of a 3D space. The triangles move with an easing function so they close faster than they open, which gives a more interesting movement than just a constant open and close.

The relevant code, inserted into the praxinoscope template:

void drawArtFrame (int whichFrame) { 
 
  int height = 40;
  int width = 40;
  translate(-20, -20);
 
  if (whichFrame <= nFrames/2){
    // close quickly 
    float eased = function_DoubleExponentialSigmoid(map(whichFrame, 0, nFrames/2, 0, 1), 0.7);
    fill(50);
    triangle(0, height, width, height, width/2, map(eased, 0, 1, height, height/2 ));
    fill(100);
    triangle(0, 0, 0, height, width/2 * map(eased, 0, 1, 0, 1), height/2);
    fill(150);
    triangle(0, 0, width, 0, width/2, height/2 * map(eased, 0, 1, 0, 1));
    fill(200);
    triangle(width, 0, width, height, map(eased, 0, 1, width, width/2), height/2); 
 
  } else {
    // open slowly
    float eased = function_DoubleExponentialSigmoid(map(whichFrame, nFrames/2, nFrames, 0, 1), 0.05);
    fill(50);
    triangle(0, height, width, height, width/2, map(eased, 0, 1, height/2, height ));
    fill(100);
    triangle(0, 0, 0, height, width/2 * map(eased, 0, 1, 1, 0), height/2);
    fill(150);
    triangle(0, 0, width, 0, width/2, height/2 * map(eased, 0, 1, 1, 0));
    fill(200);
    triangle(width, 0, width, height, map(eased, 0, 1, width/2, width), height/2); 
  }
}
 
// Taken from https://github.com/golanlevin/Pattern_Master
float function_DoubleExponentialSigmoid (float x, float a) {
  // functionName = "Double-Exponential Sigmoid";
 
  float min_param_a = 0.0 + EPSILON;
  float max_param_a = 1.0 - EPSILON;
  a = constrain(a, min_param_a, max_param_a); 
  a = 1-a;
 
  float 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;
}

 

 

chromsan-Reading02

1A. I think a good example of something exhibiting effective complexity is the structure of a tree. Trees grow in a random way, so no two trees are alike. In this sense they posses some randomness. That said, trees can be relied upon to grow in a fairly recognizable way; everyone can recognize the shape and structure of a tree when they see it. They are ordered in this regard. I think trees, among many natural things, perfectly straddle the line between chaos and order.

1B. The Problem of Locality, Code, and Malleability. This is philosophically an interesting problem, there are clear parallels between it and the mind-body problem. It is an important one as well, as it frames how we think of generative art. The dualist might argue that the art exists discretely from the code, that it exists in the place in which it is seen. The materialist might argue that the art is the code itself. I think to some degree both are correct; though the art is a function of the code, the code may have artistic properties and running it produces other artistic properties that cannot be experienced from just the code itself.

 

chromsan-Interruptions

My observations:

  1. The lines are drawn in a square
  2. There are 55-60 lines per side
  3. Each line is the same length
  4. The lines can either intersect each other or not
  5. The lines that intersect only do so on each other's ends- they never cross in the middle
  6. The majority of the lines are vertical
  7. Non-vertical lines rotate several degrees left or right, but are never horizontal
  8. The lines are rotated about their ends middles
  9. There are sometimes white areas in the center of the square with no lines within
  10. The white areas account for ~0-10% of the total area

I started by creating a grid of lines. It seemed like the lines' spacing was more important than the number of lines, so I opted to base my for loops on padding values rather than by line number. Next I worked on creating the semi-random rotation values for the lines. I accomplished this by utilizing p5's built in vector object which I could use to create an angle for each by using the fromAngle constructor. For the angle values, I experimented with both the noise function and randomGaussian function before settling on the latter. A key feature of Molnár's work is that the lines are mostly vertical, and the randomGaussian function allowed me set the median to 90 degrees and then allow a certain amount of randomness by giving the function a standard deviation of around 30.

A problem I ran into at this point was that I was rotating the lines around the endpoints fixed to the grid and the lines were intersecting each other in the middles- something that does not happen in Molnár's work. I eventually found a solution to this by fixing the lines to the grid by their center points, thus ensuring that any overlapping only occurs on the ends of the line segments. After playing with the values, the result is quite close to the way that the lines in the reference works behave.

Finally, the white spaces. For these I used the noise function and took advantage of the double for loop to create a noise map in 2D space. I mapped the values on the range [0, 255] such that I could pick some arbitrary color of gray, say 155, and use that as a mask. Any time that the color value of the map goes above 155, no lines are drawn. With an appropriate offset value to keep the size and quantity of the holes correct, the result is somewhat similar to the random quality of Molnár's. However, hers is slightly different in that the holes are both small and large. Using this technique, I am not able to get the same effect because any offset value I choose will create a map with uniform variations. In other words, I can either get large or small holes, not both. This is definitely the main area to improve on. I'd be quite interested to see how she achieved this unique affect.

chromsan-lookingoutwards01

The Crown Fountain has become an iconic part of Millennium Park in Chicago. It's an interactive work of public art designed by artist Jaume Plensa that was installed in 2004. The fountain consists of two 50 foot towers made out of glass blocks, which have a huge array of LEDs that create an image on the side of the towers. Water cascades down the sides of the towers into a large granite reflecting pool. The piece encourages public interaction; during the summer kids fill the pool and stand under the flow of water from the towers.

Though Plensa designed the sculpture, it was executed by Krueck and Sexton Architects. The total cost of construction and design is estimated to be $17 million, and it costs the City of Chicago $400,000 a year to maintain.  Filming of the people on the towers was done at the School of the Art Institute of Chicago by 2o masters students in 2004.

The Fountain uses a large number of sensors to determine how to distribute water. The water falling from the top varies based off of the wind speed and direction to avoid loosing water due to splashing. Sensors in the pool measure water temperature and level and adjust the flow accordingly. The images displayed on the towers are randomly picked from a selection of around 1,000. Brightness and contrast of the video is automatically adjusted based off light conditions. When the faces "spit" a stream of water, the water and video are aligned across both towers. All the software written for this project was proprietary and runs on a number of controllers designed for shows.

Plensa was inspired by large European fountains with figures in the center. He wished to preserve the same principle but allow the public to become a part of the water feature. Where many decorative fountains are fenced off or prohibit public interaction, Crown Fountain encourages interaction. Instead of spitting gargoyles, Plensa enlisted Chicago residents to be the subjects in the ever changing face of the towers.