alchan-Project 12-proposal

For this project, I am planning on collaborating with Maddy Cha (andrew id: mecha, section E) in order to create a story-based game in which the choices a user makes affects the rest of the story. The story/ game will be presented as a series of images with text. Players will be able to choose options/ text prompts by pressing corresponding keys.

The story will revolve around the player’s trip to a grocery store, in which the player can choose to buy items that will help them progress. Each part of the story will have a different character that will respond to different objects that the user decides to purchase.

We will also implement randomness in order to make it so that each user’s experience is a little different. We will randomize the order in which characters may appear in the story as well as different events that may help the player later on.

alchan-Looking Outwards 12

For my final project I’m probably going to create a game (with Maddy Cha) along the lines of interactive fiction/ visual novels.

Kentucky Route Zero Act I, 2013, by Cardboard Computer

The first relevant project is Kentucky Route Zero, one of my favorite games. It’s being developed by Cardboard Computer, a small indie studio releasing the game in 5 acts (the 1st act was released in 2013, and the 5th act is supposed to come out in early 2018). It’s a magical-realist adventure game inspired by point-and-click games & interactive fiction, but with a heavy focus on characters and mood.  It’s essentially a winding interactive story. I really admire this focus, especially compared to more combat- or puzzle-oriented “traditional” video games. While we’re definitely not going to attempt something this ambitious (or in a similar storytelling genre), I really like how the game handles story and text.

overview of the latest version of Twine

The second project is Twine. It was originally created by Chris Klimas in 2009, but its development has since been open-sourced and is now maintained by several other people. It’s a tool meant to make the creation of interactive fiction accessible, essentially generating a series of hyperlinked HTML pages at its simplest. Games or fiction created with twine are thus easily customizable since they can be extended with Javascript, CSS, or any number of other possibilities. I admire Twine since it radically simplifies the process of making a story (as simple as writing passages and connecting them), and makes it easy to visualize the game/ story as a whole. P5 doesn’t really have the option of hyperlinks, but the idea of branching storylines and keeping track of past choices definitely applies to what we’d like to do.

 

alchan-Project 11-Composition

turtles

var turtle1;
var turtle2;
var turtle3;

var forwardAmt;
var turnAmt;

var turtles = [];
var colorBank = ["lightskyblue", "darksalmon", "gold", "coral", "skyblue", "cadetblue", "yellowgreen", "tomato"]
var colors = [];

var turtlePosition;

function setup() {
  createCanvas(480, 480);
  background(220);

  blendMode(OVERLAY);

  frameRate(10);

}

function draw() {
  for(var i = 0; i < turtles.length; i++) {
    forwardAmt = random(1, 50);
    turnAmt = random(-100, 100);
    turtles[i].setColor(colors[i]);
    turtles[i].setWeight(10);
    turtles[i].penDown();
    turtles[i].forward(forwardAmt);
    turtles[i].right(turnAmt);
    turtles[i].penUp();
  }
}

function mousePressed() {
  turtles.push(makeTurtle(mouseX, mouseY));
  colors.push(random(colorBank));
  print(colorBank);
  print(colors);
}

function keyPressed() {
  if (key === "R") {
    turtles = [];
    blendMode(NORMAL);
    background(220);
    blendMode(OVERLAY);
  }
}

// turtle graphics
function turtleLeft(d){this.angle-=d;}function turtleRight(d){this.angle+=d;}
function turtleForward(p){var rad=radians(this.angle);var newx=this.x+cos(rad)*p;
var newy=this.y+sin(rad)*p;this.goto(newx,newy);}function turtleBack(p){
this.forward(-p);}function turtlePenDown(){this.penIsDown=true;}
function turtlePenUp(){this.penIsDown = false;}function turtleGoTo(x,y){
if(this.penIsDown){stroke(this.color);strokeWeight(this.weight);
line(this.x,this.y,x,y);}this.x = x;this.y = y;}function turtleDistTo(x,y){
return sqrt(sq(this.x-x)+sq(this.y-y));}function turtleAngleTo(x,y){
var absAngle=degrees(atan2(y-this.y,x-this.x));
var angle=((absAngle-this.angle)+360)%360.0;return angle;}
function turtleTurnToward(x,y,d){var angle = this.angleTo(x,y);if(angle< 180){
this.angle+=d;}else{this.angle-=d;}}function turtleSetColor(c){this.color=c;}
function turtleSetWeight(w){this.weight=w;}function turtleFace(angle){
this.angle = angle;}function makeTurtle(tx,ty){var turtle={x:tx,y:ty,
angle:0.0,penIsDown:true,color:color(128),weight:1,left:turtleLeft,
right:turtleRight,forward:turtleForward, back:turtleBack,penDown:turtlePenDown,
penUp:turtlePenUp,goto:turtleGoTo, angleto:turtleAngleTo,
turnToward:turtleTurnToward,distanceTo:turtleDistTo, angleTo:turtleAngleTo,
setColor:turtleSetColor, setWeight:turtleSetWeight,face:turtleFace};
return turtle;}

I was inspired by the look of stylized subway maps (dots connected by segments in particular) and wanted to create something that partially mimicked that look. I decided to randomize the angles and segment lengths and incorporate user interaction: click to add more turtles/ paths, and press R to reset the canvas.

alchan-Looking Outwards 11

example of the Continuator (2000) by François Pachet in use, with Albert van Veenendaal on piano

The Continuator (2000) is a computational music generation system by Francois Pachet. It works in conjunction with a musician to improvise real-time musical sequences, creating melodies that may be indistinguishable to those played by the human musician (in the video shown, it’s being used as part of a “Musical Turing Test” to see if humans can tell the computer-generated music apart from the human-generated music).

I’m really interested in the way the Continuator works in conjunction with a musician, as it uses the musician’s playing as a reference point to come up with new but stylistically similar tunes. I like the idea of using algorithms in partnership with human creativity, as that seems to open up more possibilities than just trying to create an algorithm that will recreate human abilities perfectly.

alchan-Project 10-Generative Landscape

sunken

var ruins = [];
var farRuins = [];
var fog = [];

var skyColor;


function setup() {
    createCanvas(480,300);
    angleMode(DEGREES);

    for (var c = 0; c < 10; c++) {
      // populate arrays
      var ruinX = random(width);
      var farRuinX = random(width);
      ruins[c] = makeRuin(ruinX);
      farRuins[c] = makeFarRuin(farRuinX);
      for(var d = 0; d<2; d++){
        var fogX = random(width);
        fog[c] = makeFog(fogX);
      }
    }
    skyColor = color(220, 233, 239);

}

function draw() {
  background(skyColor);

  sunDraw();
  sunMove();

  fill(210, 223, 229);
  noStroke();
  rect(0, 180, width, 120);

  // draw the rest of the objects in the scene
  for(var i = 0; i < farRuins.length; i++) {
    farRuins[i].draw();
    farRuins[i].move();
  }

  for(var i = 0; i < ruins.length; i++) {
    ruins[i].draw();
    ruins[i].move();
  }

  for(var i = 0; i < fog.length; i++) {
    fog[i].draw();
    fog[i].move();
  }
}

// FOG
function fogDraw() {
  push();
  translate(this.xPos, this.yOffset);
  stroke(255, 255, 255, 40);
  strokeWeight(this.fHeight);
  line(0, 0, this.fSize, 0);
  pop();
}

// move objects across canvas, when they hit the edge
// move them back to the far edge with randomized attributes
function fogMove() {
  this.xPos += this.speed;
  if(this.xPos < 0 - this.fSize - 30) {
    this.fHeight = random(10, 50);
    this.fSize = random(30, 150);
    this.xPos = width + this.fSize + random(-25, 25);
  }
}

function makeFog() {
  var fog = {xPos: random(width), //, width*4
                 speed: random(-0.4, -0.3),
                 fSize: random(30, 150),
                 fHeight: random(20, 60),
                 yOffset: random(50, height),
                 draw: fogDraw,
                 move: fogMove};
  return fog;
}
// END FOG

// SUN
var sun = {xPos: 20, size: 20, speed:.1};

function sunDraw() {
  var sunColor = color(234, 229, 228);
  noStroke();
  fill(252, 202, 191);
  ellipse(sun.xPos, 60, sun.size);

  // make a gradient, centered around the sun
  for (var s = 0; s <= width; s += 5) {
    var amt = map(s, 0, height, 0, 1);
    var gradient = lerpColor(sunColor, skyColor, amt);
    noFill();
    stroke(gradient);
    strokeWeight(5);
    ellipse(sun.xPos, 60, sun.size + s);
  }
}

function sunMove() {
  sun.xPos += sun.speed;
  if (sun.xPos > width + sun.size) {
    sun.xPos = 0 - sun.size;
  }
}
// END SUN

// RUINS
function ruinDraw(){
  push();
  translate(this.xPos - this.rWidth/2, height-40+this.yPosOffset);
  noStroke();
  fill(245, 245, 255);
  beginShape();
  vertex(0 + this.rWidth/2, 0 + this.yOffset);
  vertex(0 - this.rWidth/2, 0 + this.yOffset);
  vertex(0 - this.rWidth/2 - this.lean, -this.rHeight - this.spike1);
  vertex(0 - this.rWidth/2 + this.valley1, -this.rHeight + this.valley1);

  vertex(0 - this.rWidth/2 + this.valley1, -this.rHeight - this.spike2);
  vertex(0 - this.rWidth/2 + this.valley1 + this.valley2, -this.rHeight + this.valley2);

  vertex(0 - this.rWidth/2 + this.valley1 + this.valley2, -this.rHeight - this.spike2);
  endShape(CLOSE);
  // draw reflections
  scale(1, -1);
  fill(245, 245, 255, 60);
  beginShape();
  vertex(0 + this.rWidth/2, 0 - this.yOffset);
  vertex(0 - this.rWidth/2, 0 - this.yOffset);
  vertex(0 - this.rWidth/2 - this.lean, -this.rHeight - this.spike1);
  vertex(0 - this.rWidth/2 + this.valley1, -this.rHeight + this.valley1);

  vertex(0 - this.rWidth/2 + this.valley1, -this.rHeight - this.spike2);
  vertex(0 - this.rWidth/2 + this.valley1 + this.valley2, -this.rHeight + this.valley2);

  vertex(0 - this.rWidth/2 + this.valley1 + this.valley2, -this.rHeight - this.spike2);
  endShape(CLOSE);
  pop();
}

// move objects across canvas, when they hit the edge
// move them back to the far edge with randomized attributes
function ruinMove(){
  this.xPos += this.speed;
  if (this.xPos < 0 - this.rWidth) {
    this.rHeight = random(15, 40);
    this.rWidth = random(10, 60);
    this.lean = random(0, 20);
    this.xPos = width + this.rWidth + this.lean + random(-5, 50);
  }
}

function makeRuin(x) {
  var ruin = {xPos: x,
              rHeight: random(15, 60),
              rWidth: random(10, 80),
              lean: random(0, 20),
              spike1: random(0, 10),
              valley1: random(0, 10),
              spike2: random(0, 10),
              valley2: random(0, 10),
              spike3: random(0, 10),
              valley3: random(0, 10),
              yOffset: random(-5, 5),
              yPosOffset: random(-15, 15),
              speed: random(-.4, -.5),
              draw: ruinDraw,
              move: ruinMove}
  return ruin;
}
// END RUINS

// FAR RUINS
function farRuinDraw(){
  push();
  translate(this.xPos - this.rWidth/2, height-90);
  noStroke();
  fill(233, 237, 244);
  beginShape();
  vertex(0 + this.rWidth/2, 0 + this.yOffset);
  vertex(0 - this.rWidth/2, 0 + this.yOffset);
  vertex(0 - this.rWidth/2 - this.lean, -this.rHeight - this.spike1);
  vertex(0 - this.rWidth/2 + this.valley1, -this.rHeight + this.valley1);

  vertex(0 - this.rWidth/2 + this.valley1, -this.rHeight - this.spike2);
  vertex(0 - this.rWidth/2 + this.valley1 + this.valley2, -this.rHeight + this.valley2);

  vertex(0 - this.rWidth/2 + this.valley1 + this.valley2, -this.rHeight - this.spike2);
  endShape(CLOSE);
  // draw reflections
  fill(233, 237, 244, 60);
  scale(1, -1);
  beginShape();
  vertex(0 + this.rWidth/2, 0 - this.yOffset);
  vertex(0 - this.rWidth/2, 0 - this.yOffset);
  vertex(0 - this.rWidth/2 - this.lean, -this.rHeight - this.spike1);
  vertex(0 - this.rWidth/2 + this.valley1, -this.rHeight + this.valley1);

  vertex(0 - this.rWidth/2 + this.valley1, -this.rHeight - this.spike2);
  vertex(0 - this.rWidth/2 + this.valley1 + this.valley2, -this.rHeight + this.valley2);

  vertex(0 - this.rWidth/2 + this.valley1 + this.valley2, -this.rHeight - this.spike2);
  endShape(CLOSE);
  pop();
}

// move objects across canvas, when they hit the edge
// move them back to the far edge with randomized attributes
function farRuinMove(){
  this.xPos += this.speed;
  if (this.xPos < 0 - this.rWidth) {
    this.rHeight = random(50, 100);
    this.rWidth = random(40, 100);
    this.lean = random(0, 50);
    this.xPos = width + this.rWidth + this.lean + random(-5, 50);
  }
}

function makeFarRuin(x) {
  var farRuin = {xPos: x,
              rHeight: random(50, 100),
              rWidth: random(40, 100),
              lean: random(0, 50),
              spike1: random(0, 10),
              valley1: random(0, 10),
              spike2: random(0, 10),
              valley2: random(0, 10),
              spike3: random(0, 10),
              valley3: random(0, 10),
              yOffset: random(-15, 15),
              speed: random(-.4, -.2),
              draw: farRuinDraw,
              move: farRuinMove}
  return farRuin;
}
// END FAR RUINS

 I wanted to create a landscape that had something to do with water, and ended up going with icebergs or jagged ruins drifting through a pale sea. The size and shape of the icebergs are all randomly determined, as are the fog clouds. I had also wanted to extra “surprise” elements to the landscape (a sunken ship, and a glimpse of a sea serpent) but I ran out of time and wasn’t able to implement them the way I had planned.

alchan-Looking Outwards 10

(promotional image for Ooblets, by Rebecca Cordingley)

Rebecca Cordingley (or nonplayercat) is an indie game designer/ developer/ artist. She’s currently making Ooblets (alongside Ben Wasser), a game described as “Harvest Moon meets Pokémon meets Animal Crossing” due to be released sometime in 2018. Cordingley is primarly using Unity, Maya, and Mixamo to develop the game and 3d-model, along with programs like Photoshop and Illustrator.

(gifs from the in-progress game, by Rebecca Cordingley)

I really like the simplified visual style of the game, which I think works particularly well with the game’s focus. The main aspect of the project I really appreciate though is how open Cordingley has been about the game’s development, as both she and Wasser regularly share screenshots, gifs, and progress posts on the work they’re doing on the game.

 

alchan-Project 09-portrait

portrait

var portraitImg;
var rotateAngle;

function preload() {
    var portraitLink = "https://i.imgur.com/pn2LErg.jpg";
    portraitImg = loadImage(portraitLink);
}

function setup() {
    createCanvas(480,480);
    portraitImg.loadPixels();
}

function draw() {
    background(0);
    for(var y = 0; y <= height; y+=15) {
      for(var x = 0; x <= width; x+=15) {
        var pixelColor = 10 + brightness(portraitImg.get(x, y));
        var mouseDist = dist(mouseX, mouseY, x, y);
        strokeWeight(map(mouseDist, 0, 679, 0.5, 8));
        stroke(pixelColor);
        push();
        translate(x, y);
        rotate(map(mouseDist, 0, 679, 0, 360));
        line(0-3, 0, 0+3, 0);
        pop();
        line(x, y-3, x, y+3);
      }
    }
}

For my portrait, I knew I wanted to have the entire picture rendered at first, and user interaction to change the portrait somehow. I decided to go with a somewhat abstract grid of crosses to represent the picture at first, using the source image’s brightness values instead of colors to create a more cohesive image. (The source image is an extremely cropped view of a face from the eyes up, in case you couldn’t make it out.) The crosses change thickness (and the horizontal bar rotates) depending on how far away the mouse is, obscuring areas of the image the mouse is closest to. Unfortunately the program runs a little slow for me, so it’s not quite the experience I had planned, but it’s as close as I could get.

alchan-Looking Outwards 9

(Jakub Javora, Dark Forest, 2016)

I came across Jackie’s post on Jakub Javora’s work and was really drawn in by the style and slight animation. Though I’ve grown to be less impressed by how “realistic” something is rendered and more interested with different methods of stylization, I think that the level of realism works well in this piece. Like Jackie, also I really appreciate the narrative level the artist brought into the work. The glowing rectangle (doorway? light?) adds an element of mystery to the piece, which combined with the lighting and the movement of the deer work to create an overall atmosphere of mystery.

alchan-Looking Outwards 08

(Eyeo 2015 – Amor Muñoz from Eyeo Festival // INSTINT)

Amor Muñoz is a multimedia artist from Mexico City. She’s interested in combining experimental technology with more traditional media, in particular textiles. Most of her projects are heavily interdisciplinary, reflecting this interest. She originally studied law at the National Autonomous University of Mexico before briefly moving to the US to study art at the New Orleans Academy of Fine Arts, though she continued practicing law for some years after.

As someone who’s also very interested in textile art and embroidery, I’m fascinated by the way Muñoz combines/ incorporates them with technology. Her art is also frequently commentary on cultural or social issues, and Muñoz lists her “Maquila Region 4” (MR4) project as a prime example of all these pursuits. The project is a take on “maquilas,” which are cheap labor factories typically owned and managed by US companies along Mexican border towns. MR4 is a moble “maquila” that hires people (at the US minimum wage, which is much higher than the Mexican minimum) in poor areas to produce embroidered soft circuits with conductive thread.

alchan-Project 07-curves

curves

// attributes of shape
var points;
var angle;

function setup() {
  createCanvas(480, 480);
  angleMode(DEGREES);
  frameRate(30);
}

function draw() {
  background(200);
  // set attributes to change with mouse position
  points = map(mouseX, 0, width, 0, 300);
  angle = map((mouseX + mouseY / 2), 0, width, 0, 360);
  // draw "main" curve
  stroke(209, 122, 110);
  drawCurve(width/2, height/2);

  // draw "background" curves, setting each to a random color
  // and a randomized position
  stroke(random(200, 255), random(200, 255), random(200, 255));
  drawCurve(width/2 + random(-10, 10), height/2 + random(-10, 10));
  stroke(random(200, 255), random(200, 255), random(200, 255));
  drawCurve(width/2 + random(-20, 20), height/2 + random(-20, 20));
  stroke(random(200, 255), random(200, 255), random(200, 255));
  drawCurve(width/2 + random(-30, 30), height/2 + random(-30, 30));
  stroke(random(200, 255), random(200, 255), random(200, 255));
  drawCurve(width/2 + random(-40, 40), height/2 + random(-40, 40));
}

function drawCurve(posX, posY) {
  var x;
  var y;
  //var a = 400;
  var b = map(mouseY, 0, 480, 20, 80);

  strokeWeight(1);
  noFill();

  push();
  translate(posX, posY);
  rotate(angle);
  // astroid curve (http://mathworld.wolfram.com/Astroid.html)t
  beginShape();
  for (var i = 0; i < points; i++) {
    x = 3 * b * cos(i) + b * cos(3* i);
    y = 3 * b * sin(i) - b * sin(3 * i);
    vertex(x + random(0,2), y + random(0,2));
  }
  endShape(CLOSE);
  pop();
}

Since I was a little overwhelmed with the idea of creating a project based on mathematical curves, I decided to approach the project by “sketching” in the browser and figuring out how the code/ curve would render as I modified different variables. I chose an astroid for my curve. I also hadn’t originally set the angle mode of the drawing to degrees, and ended up with a spirograph-like drawing at first which was visually interesting, but not quite what I was going for.

Once I had figured out how to make the curve actually a curve, I added in variables to make the size, number of points drawn, and rotation of the curve interactive. I also added elements of randomization to try to achieve a flickering/ glitching effect to add more interest.