I was interested in the idea of using corn on the cob as a visual medium. The kernels align in a way that is staggered uniformly. I also have never worked using pixel art with a hexagonal base, which ended up being surprisingly challenging.

I used a stock photo from this website. In photoshop I was able to create three different frames containing  "on" kernels spaced with enough room between them so that I could apply loose masks to display a single kernel at a time.






By using a rough mask I could isolate each kernel with enough room for variation so that the same mask could be used for each kernel while still getting precise edges.

Having never worked in hexagonal pixel space before, I opted to store the pixel data as 2d arrays because I figured it would be easier. It actually ended up being more difficult because I had to compensate for the offset columns of kernels. Especially with smaller, lower resolution sprites like this, even the difference of half of a pixel can make a big difference. I had to save two copies of each sprite so that when the 2d arrays are converted to the hexagonal pixels there isn't any inconsistency.

The following are a few sketches of the sprites. I thought about making the design rotate about a sort of sphere but I decided to keep the illusion 2-dimensional.



var kernels = [];
var activeKernels;
var colSize;
var rowSize;
var img;
var kern1;
var kern2;
var kern3;
var kernMask;
var randDelay;
var t = 0;
var blink = 0;
var faces = [
      [1, -1],
      [-1, -1],
      [-1, 1],
      [0, 1],
      [1, 1]
      [1, -2],
      [-1, -2],
      [1, 0],
      [-1, 0],
      [0, 1]
      [2, -2],
      [-2, -2],
      [-2, 1],
      [-1, 2],
      [0, 2],
      [1, 2],
      [2, 1]
      [2, -2],
      [-2, -2],
      [-2, 1],
      [-1, 1],
      [0, 2],
      [1, 1],
      [2, 1]
var rat;
function preload() {
  img = loadImage("assets/corn_0.png");
  kern1 = loadImage("assets/corn_1.png");
  kern2 = loadImage("assets/corn_2.png");
  kern3 = loadImage("assets/corn_3.png");
  kernMask = loadImage("assets/corn_matte.png");
var ctracker;
function setup() {
  // setup camera capture
  randDelay = random(50,200);
  var videoInput = createCapture();
  videoInput.size(400, 300);
  videoInput.position(0, 0);
  // setup canvas
  cnv = createCanvas(800, 800);
  rat = 2000 / width;
  rowSize = height / 16;
  colSize = width / 11;
  cnv.position(0, 0);
  // setup tracker
  ctracker = new clm.tracker();
function draw() {
  image(img, 0, 0, width, height);
  //image(kern1,0,0,width, height);
  // get array of face marker positions [x, y] format
  var positions = ctracker.getCurrentPosition();
  var p = positions[62];
  var q = positions[28];
  var r = positions[23];
//  for (var i = 0; i < positions.length; i++) { // stroke("black"); //if (i == 23) stroke("green"); // set the color of the ellipse based on position on screen //fill(map(positions[i][0], width * 0.33, width * 0.66, 0, 255), map(positions[i][1], height * 0.33, height * 0.66, 0, 255), 255); // draw ellipse at each position point //ellipse(positions[i][0], positions[i][1], 8, 8); // } if (p != undefined) { var d = dist(q[0],q[1],r[0],r[1]); var size = 0; if (d>110) size =1;
    if (d<40) size =2; var ck = getCoors(map(p[0],0,255,0, 800),map(p[1],0,255,0, 800)); drawFace(ck[0], ck[1], size); } pop(); if (t>=randDelay) {
  	randDelay = random(50,200);
  if (blink>0) blink--;
  print (p);
function Kernel(col, row) {
  this.col = col;
  this.row = row;
  this.x = colSize * (col + 0.5);
  this.y = rowSize * (row + 0.75);
  this.upCol = col % 2 == 0;
  if (this.upCol) this.y -= rowSize / 2;
  this.img = img;
  if (col % 2 == 0) {
    if (row % 3 == 0) this.img = kern2;
    if (row % 3 == 1) this.img = kern3;
    if (row % 3 == 2) this.img = kern1;
  } else {
    if (row % 3 == 0) this.img = kern1;
    if (row % 3 == 1) this.img = kern2;
    if (row % 3 == 2) this.img = kern3;
  } = function() {
    var l = this.x - colSize;
    var gLeft = l * rat;
    var t = this.y - rowSize;
    var gTop = t * rat;
    var w = colSize * 2;
    var gWidth = w * rat;
    var h = rowSize * 2;
    var gHeight = h * rat;
    var dis = this.img.get(gLeft, gTop, gWidth, gHeight);
    image(dis, l, t, w, h);
function loadKernels() {
  var col = [];
  for (var c = 0; c < 11; c++) {
    for (var r = 0; r < 16; r++) {
      col.push(new Kernel(c, r));
    col = [];
function getCoors(x, y) {
  var col = x / colSize - 0.5;
  if (round(col) % 2 == 0) y += rowSize / 2;
  var row = y / rowSize - 0.75;
  if (col < 0) col = 0; if (col > 10) col = 10;
  if (row > 15) row = 15;
  if (row < 0) row = 0;
  return [int(round(col)), int(round(row))];
function drawFace(col, row, size) {
  var face = faces[size][0];
  if (col % 2 == 0) face = faces[size][1];
  var p;
  var c = 0;
  var r = 0;
  for (var i = 0; i < face.length; i++) { p = face[i]; c = int(col + p[0]); r = int(row + p[1]); if ((c >= 0 && r >= 0 && c < 11 && r < 16)&&


This is a chatroom where the more words you try to use in a single message, the more deteriorated and confusing the message becomes. If you send short messages of few words, the room will usually send the message as is. If the user tries to use too many words or letters, the message starts to disappear.

My original concept for this chatroom was a platform where the user has to collect the materials needed to construct their sentence. I planned on including some sort of pixel pile that you would have to collect from to get enough material to type out and send your message.  In the working app above, each message is given a certain amount of length tolerance- the longer a message is, the more likely characters are to be missing. The result is a tool that forces the user to use as few words as possible at the risk of having their message made unreadable.

The design aspect that this project addresses most clearly is its criticality and self-awareness. Ordinarily, chatrooms like this have no limit to the length of messages you can send. With most things we do in life, however, there is a trade off between pleasure or action and the resources spent to make that happen. By using this chatroom, where there is an implied limit to the length of your message it makes the user consider the "luxury" of digital tools their limitless theoretical resources.



Spectacle is the use of video and mixed digital media as a vessel for existing ideas.

Speculation is the investigation of this digital form, exploring the nature of software and exposing facets of digital medium itself in a way that's self-aware.

A project I learned about this semester is a YouTube channel called alantutorial. The content on this channel is, above anything else, extremely unsettling. The idea behind the videos is that this bizarre character, alantutorial, is recording a series of random and seemingly pointless tutorials to upload to his channel. These tutorials demonstrate how to do things like making mail into square and how to build a weatherized hole. These aren't things that anyone would need a tutorial for, but that's the idea of the project: a series of tutorials that exhibit not the process of doing something, but the strange behaviors of this deranged character as he tries his best to present an informational video for his fans. This work is definitely more speculative than it is spectacular.

It plays off the style of tutorial/DIY videos popular on YouTube in it's earlier days (before 2012 or so). There are a countless number of these no-budget tutorials online which make up their own genre of online video, all following the same vague structure of every other YouTube tutorial. Alantutorial rejects, while simultaneously parodying, the style of production value seen in these tutorials, where they're usually made by some kid in a garage with a 144p camcorder. He even parodies nuances of these videos like struggling to hold something down with one hand while having to hold the camera in the other.

This work stands closer to speculation because of what it presents at face value. At their surface, these videos are no different from any other tutorial on YouTube. He introduces his channel and what the tutorials about, he goes through the steps of some process, and finally presents the product of his efforts and signing off. By making the viewer question not only why they're watching the video, but why anyone would want to watch it, it begs the question of where it came from why it exists, which is not often asked of the videos we watch online.

The idea that you are watching someone demonstrate how to do something you have no interest in doing is what makes these videos seem to drag on and almost be a waste of time. They're only a few minutes each, but because they're essentially nonsense, the viewer is more skeptical of whether watching it is worth their time.

There are many details hidden in these videos that are not addressed directly, but add a layer of surrealism and nuanced cohesion through each video leaning slightly towards the realm of spectacle. You can tell that there was care put into setting all of these videos in the same universe with the same reoccurring imagery and callbacks. It makes for a confusing, unsettling, uninviting, visual spectacle.

The places where he films these videos is usually disgusting. Paint and dirt everywhere, things leaking, things bleeding, dangerous power tools, etc. Most of the time he's pouring stuff on the floor and making a mess, and at the same time there's a sense of neediness and not-having. For example, he makes a video about getting a dollar one day.  Watching him operate makes you almost empathize with and worry for him while he mindlessly destroys his things.

I would say these videos touch deeper into the art side of experience rather than the commerce side. When taken at face value these videos have very little substance, and most of the time don't even teach the things they claim to. They require a deeper analysis from the context of the YouTube tutorial genre as a whole, and what parts of that formula are being exploited and distorted.


I chose to make a clock that looked like nothing more than an ordinary video. The way it works is the bottle of water is drank over the course of an hour, meaning that the level of the water gives you an idea for how far into the hour it is (the height of the water resets to the bottom of the label). The hours are indicated by the street lights. Reading from the closest stop light as the most significant digit, you can read the hour in binary from the color of each consecutive light. Both of these functions can be manipulated by the user by typing 1-4 to change the color of each respective light, and 5 to take a sip.  The most challenging part of this project was figuring out a way to organize and execute longer animations from a sequence of images. The gif above shows how each layer can be isolated and animated independently . I like that this clock doesn't reveal itself easily. I tried to present it as a single shot, as if it were a live video recording of real events. And although these events seem to be happening over time, time never passes in this scene.

I made these car assets because I originally planned on including them as a component of the clock. I may spend more time in the future to get this working.

These gifs show how the empty road was constructed from pieces of a long video, over time.


1A) Something that exhibits effective complexity is shaving. With the stroke of a razor, a 2 dimensional area of skin is shaved, removing the hair that's growing there. The goal of shaving is to remove enough of these areas around your face so that there's no hair left. The shapes of these areas have to be arranged so that:

-Every point on your face lies in one of the areas

-You don't shave the same place too many times and give yourself razor burn (these areas will have many areas overlapping them)

Most people do this by shaving an area at the edge of the face, and then removing the hair around it until the face is empty of hair or they start working on a different section. These areas are arranged and ordered so that the edge of each stroke  slightly overlaps the edge of the previous stroke. Longer strokes make for more efficient shaving because it reduces the number of strokes and the area of overlapping shapes.

Shaving is pretty chaotic because it depends on the person's abilities and methods of packing shapes into a area. Everyone who shaves is generally going to remove the hair that they want to remove, the most variation lies in the razor strokes that remove that area of hair.

1B) The Problem of Meaning touches on some interesting ideas in terms of finding purpose and meaning in generative art. The idea behind generative art is to create functioning systems where each component interacts with another in a ordered, calculated way. The reasoning and logic behind the systems operations or products is what is interesting about the art. If these systems are based on systems that already exist, the art becomes the study of that system and the ability to simulate it in a different medium (like simulating the way clouds form in code).

These systems are designed by artists with a product in mind. Despite the fact that a generative work may be based off of an existing natural system, the artist has to understand that system in order to exploit or reproduce it. It's a study of how humans reason over dynamic processes.


My original goal in making this gif was to familiarize myself with loading animating picture files in p5.js. I started with a simple animation sequence that I processed in photoshop to isolate each element.


The hardest part was getting different cells to animate the frames at a different offset interval. The solution I used wasn't very elegant but in the future I think I'll set up some sort of data structure for frames that are being animated.

I used the PennerEaseInOutBack easing function to shift the entire frame of cells down smoothly. I think the smooth sweeping motion of the frame looks nice against the choppier, hand-drawn animation. The way it stretches slightly past the end point also makes the motion look like waves crashing. What I like about the gif is the way that the hand-drawn animations works with the perfect grid layout and smooth sweeping animation. The combination of those two qualities makes it satisfying for me to watch.

If I were to spend more time on it I would get more creative with the way that the animations are triggered. There are probably more interesting patterns and different ways activate the animation sequences of the folding papers.


// This is a template for creating a looping animation in p5.js (JavaScript). 
// 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
// 1. Run a local server, using instructions from here:
// 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;
// Other global variables you don't need to touch.
var nElapsedFrames;
var bRecording;
var theCanvas;
var frames = [];
var frameN = 0;
var unit;
var activeFrames = [];
var framesToDeact = [];
var levels = [[[4,4],[4,5],[5,4],[4,3],[3,4]],
var levels1 = [[[4,4],[4,5],[5,4],[5,5]],
function setup() {
  theCanvas = createCanvas(1920, 1920);
  bRecording = false;
  nElapsedFrames = 0;
  unit = width/9;
function keyTyped() {
  if (bEnableExport) {
    if ((key === 'f') || (key === 'F')) {
      bRecording = true;
      nElapsedFrames = 0;
      frameN = 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');
    frameN ++;
    if (nElapsedFrames >= nFramesInLoop) {
      bRecording = false;
function renderMyDesign (percent) {
  if (percent >0.95) activeFrames = [];
  // 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
  stroke(0, 0, 0);
  var inc = nElapsedFrames%6;
  var offset = height*function_PennerEaseInOutBack (percent);
  for (var row=0; row<9; row++) {
    for (var col=0; col<9; col++) {
  if (inc==0&&nElapsedFrames<30) {
    //start next tier of animation
  for (var i=0; i<activeFrames.length; i++){//animate the active frames;
    activeFrames[i][2] = (activeFrames[i][2]+1)%frames.length;
    //if (activeFrames[i][2]>frames.length) framesToDeact.push(activeFrames[i]);
// symmetric double-element sigmoid function (a is slope)
// See
// From:
function loadFrames() {
  var fileName;
  for (var i=0; i<12; i++) {
    fileName = join(["frames/sqr_",str(i+1),".png"],"");
  for (i=11; i>-1; i--) {
    fileName = join(["frames/sqr_",str(i+1),".png"],"");
function animateTier(x) {
  var tier = levels[x];
  var xyt;
  var start = 0;
  for (var i=0; i<tier.length; i++) {
    xyt = [tier[i][0], tier[i][1], 0];
function removeXYT(xyt, l) {
  for (var i=0; i<l.length; i++) {
    if (xyt[0]==l[i][0] && xyt[1]==l[i][1]) {
function drawActiveFrames(offset) {
  var af;
  for (var i=0; i<activeFrames.length; i++) {
    af = activeFrames[i];
    image(frames[af[2]%frames.length], unit*af[1], unit*af[0]+offset, unit, unit);
//The following function was taken from pattern_master - 
function function_PennerEaseInOutBack (x) {
  var s = 1.70158 * 1.525;
  x /= 0.5;
  var y = 0; 
  if (x < 1) {
    y = 1.0/2.0* (x*x*((s+1.0)*x - s));
  else {
    x -= 2.0;
    y = 1.0/2.0* (x*x*((s+1.0)*x + s) + 2.0);
  return y;

High-Res Gif (1920x1920):



  1. The artwork is in the shape of a square
  2. The artwork has a white background featuring many black lines.
  3. The center of each line seems to fall on the coordinates of a 56x56 unit grid
  4. The length of each line is equal to the lines spacing on the grid so that some of the lines intersect.
  5. Each line is set at a random angle. The angle of each line seems independent from the angles of the lines around it.
  6. Some spaces on the grid that you would expect to see a line are missing a line.
  7. The lines' angles seem to tend toward vertical.
  8. Theres an empty border around the lines.
  9. The randomness of the rotation of the lines seems independent from where lines are missing.
  10. The missing lines seem to be related to each other; there are "areas" on the grid where lines are missing or appear to be blown away.
  11. Even in places where there isn't a large area of lines missing, there are a few lines missing here and there.

The most challenging part about this piece was having to figure out the logic behind what seems like a chaotic image. While it looks very busy, the most defining features of this piece for me where the grid layout of the lines, the vertical-tending angled lines, and the large spaces where lines were missing. The large spaces of empty lines was the most difficult aspect to reproduce, followed by the angles of the lines. I chose to generate probability coefficients for each line using the noise() function. Because the noise() function used the row/column of each line, there were specific areas where lines were much  less likely to occur. T0 randomize the angle of the lines I first generated a random x-coordinate within the line's cell and made the end fall on that coordinate. This way there would be a higher probability of the line tending vertical. I enjoyed recreating this piece because it made me think about how to distinguish patterns and key characteristics of a seemingly random system. Having to break down the each element into basic properties to recreate was a fun exercise .




// Starter Code for "Embedded Iteration + Randomness"
var boolDoRefresh;
var lines = [];
var intersections = [];
var lineLength = 300;
var circWidth = 30;
var numLines = 12;
var numIntersections = 0;
function setup() {
  createCanvas(720, 480);
  boolDoRefresh = true;
function draw() {
  if (boolDoRefresh) {
    lines = [];
    intersections = [];
    numIntersections = 0;
    for (var i = 0; i < numLines; i++) {
      lines.push(new Line());
      for (var j = 0; j < i; j++) {
        intersects(lines[i], lines[j]);
    for (i = 0; i < numLines; i++) {
    for (i = 0; i < numIntersections; i++) {
    boolDoRefresh = false;
function mousePressed() {
  boolDoRefresh = true;
function Line() {
  this.x1 = int(random(width));
  this.y1 = int(random(height));
  this.angle = int(random(360));
  this.radians = 0;
  this.display = function() {
    line(this.x1, this.y1, this.x2, this.y2);
  this.updateRad = function() {
    this.radians = radians(this.angle);
  this.angle = modAngle(this.angle);
  this.x2 = this.x1 + lineLength * cos(this.radians);
  this.y2 = this.y1 + lineLength * sin(this.radians);
function Intersection(x, y) {
  this.x = x;
  this.y = y;
  this.display = function() {
    ellipse(this.x, this.y, circWidth, circWidth);
function linePointAngle(line1, px, py) {
  var lx = line1.x1;
  var ly = line1.y1;
  //this first angle is between universal and two points
  var angle = degrees(atan((py-ly)/(px-lx)));
  if (px<lx) angle+=180;
  //angle -= line1.angle;
  angle = modAngle(angle);
  //then find the difference of the line angle and this
  //angle to get the relative angle of the point to the line
  return angle;
function modAngle(angle) {
  while(angle<0) angle += 360;
  while(angle>=360) angle -=360;
  return angle;
function splits(line1, line2) {
  var angle1 = linePointAngle(line1, line2.x1, line2.y1);
  var angle2 = linePointAngle(line1, line2.x2, line2.y2);
  if (angle1>angle2) {
		var temp = angle1;
    angle1 = angle2;
    angle2 = temp; //ang1<=ang2
  if (angle2-angle1>=180) {
    return(line1.angle<=angle1 || line1.angle>=angle2);
  return(line1.angle<=angle2 && line1.angle>=angle1);
function intersects(line1, line2) {
  var res = splits(line1,line2) && splits(line2,line1);
  if (res) intersection(line1, line2);
  return res;
function intersection(line1, line2) {
  var x1 = line1.x1;
  var x2 = line2.x1;
  var y1 = line1.y1;
  var y2 = line2.y1;
  var m1 = (line1.y2-y1)/(line1.x2-x1);
  var m2 = (line2.y2-y2)/(line2.x2-x2);
  var X = ((x1*m1)-y1-(x2*m2)+y2)/(m1-m2);
  var Y = m1*(X-x1)+y1;
  var res = new Intersection(X, Y);
  numIntersections += 1;


// Starter Code for "Embedded Iteration + Randomness"
var boolDoRefresh;
var rand;
var margin;
var side;
function setup() {
  createCanvas(400, 400);
  boolDoRefresh = true;
  margin = 10;
  side = (width-margin*9)/8
function draw() {
  if (boolDoRefresh) {
    for (var row = 0; row&lt;8; row++) {
			for (var col = 0; col&lt;8; col++) {
        drawElem(row, col);
    boolDoRefresh = false;
function mousePressed() {
  boolDoRefresh = true;
function drawElem(row, col) {
  var left = col*(side+margin)+margin;
  var top = row*(side+margin) + margin;
  rand = int(random(20));
  if (rand == 0) {
  	left += int(side/2);
    top += int(side/2);
    ellipse(left, top, side/2, side/2);
  else rect(left, top, side, side);



"9. The Critical Engineer notes that written code expands into social and psychological realms, regulating behaviour between people and the machines they interact with. By understanding this, the Critical Engineer seeks to reconstruct user-constraints and social action through means of digital excavation."

This tenet observes that the code we write and the way it's expressed to users exist only in a theoretical space, but the experiences and ideas they create for their users have a very concrete impact on physical reality and our mind's perception of it. These are technologies that express how a person constructs an understanding of their reality. In this way, the technologies themselves have a significant impact on the way we understand reality and the decisions we make based on that understanding.

We design technologies that operate within our physical reality, consisting of 3 spacial dimensions along a single-dimensional timeline that we experience linearly. The mechanisms in our brains however, provide an understanding of reality where information is transmitted through space independent of mass or energy. In my personal reality I can make someone laugh, start a club, gain respect, or transcend the laws of physics by only touching a piece of glass with my body, all without having to exist at any specific point in physical space.