Week 2: Reading Reflection

One of the interesting ideas mentioned in the video was how order eventually arises from randomness. This was a concept that I played around with in my work, where the longer my program runs, the more perfect a grid is formed. In other words, the more randomness or entropy that is introduced, the more order arises (see images below).

My work incorporates randomness by using a random value to determine where the line will draw next: move straight, turn left, or turn right. It does this random check at every frame. As the user interacts with the work, it speeds up the frame rate, increasing the amount of randomness experienced per unit of time. As the work is left alone by the user, the randomness slows down back to its base speed.

The balance that I attempted to achieve between total randomness and complete control is one of harmony and complementing. By that, I chose to make that the randomness controlled, but the randomness controls the piece. This idea is also calls to a similar one shown in the video at 33 minutes and 33 seconds, where the probability selected completely changes the piece of art that is formed.

 

Week 2: Loops

Concept

I was inspired by the old magazines linked in the assignment description, where multiple shapes were often drawn onto the canvas. The canvas however defaults to a line, similar to the light cycles in the original Tron movie. Because of the constant increments by default, the line will turn at consistent lengths and eventually form a grid.

To allow for multiple shapes, I put together multiple functions (e.g., drawCircle, drawTriangle, drawSquare) that the program will rotate through when the mouse is clicked. However, if the user holds down the mouse, the program will cycle through all available shapes and draw all shapes at the same time.

Code Highlight

The below snippet includes an interesting piece of my code that incorporates the randomness: The code generates a random value in the range 0 to 1, and then uses that as a mechanism to decide whether to turn left, right, or go straight. Approximately 80% of the time the code will decide straight, 10% of the time it will choose left, and 10% of the time it will choose right.

// Decide what to do based on the provided value

let randVal = Math.random();
let xDelta = 0;
let yDelta = 0;

if (randVal < 0.8) {
  direction = direction; // do nothing
} else if (randVal < 0.9) {
  direction = (direction + 90) % 360; // go clockwise
} else if (randVal <= 1.0) {
  direction = (direction - 90 + 360) % 360; // go counterclockwise
}

// Draw in direction
if (direction === 0) {
  xDelta = increment;
} else if (direction === 90) {
  yDelta = increment;
} else if (direction === 180) {
  xDelta = -increment;
} else if (direction === 270) {
  yDelta = -increment;
}

 

Demo

Hint: Clicking on the canvas will increase the frame rate and the size of the shapes. Holding <SPACE> will enable the eraser mode.

Reflection and Future Work

This was a fun project for me to put together, and I enjoyed slowly building it up with multiple steps. It is a relatively simple concept, yet incorporates additional layers of complexity as the user interacts more with the piece.

While it does incorporate a loop that draws all shapes at once when the user presses the mouse, the entire program also acts as a loop as the art board is dynamically drawn.

Future work may wish to explore additional shapes that could be added to the piece, or alternative ways for the user to interact with it. Additional threads could also be added, such that multiple lines are being drawn at the same time.

Week 1: Self-Portrait

Week 1: Self Portrait

Concept

I wanted to create a self-portrait that reflected my at times chaotic attention span, along with my curly hair. I created a relatively simple portrait, but spent a lot of time tweaking the curls and the eyes (that represent my constantly shifting attention).

Process

Constructing my self-portrait with P5.js was a relatively iterative process, that required bouncing between the provided reference manual and the IDE itself.

Highlight: One of my favorite parts of code is how the curls are drawn. While I was considering what approaches may work, I realized that my multi-variable calculus class could come in handy, representing spirals as parametric equations.

function drawSpiral(centerX, centerY, scale, strokeThickness) {
  let numPoints = 1000;
  let maxT = 6 * PI;
  let step = maxT / numPoints;

  stroke(0);
  strokeWeight(strokeThickness);

  for (let t = 0; t <= maxT; t += step) {
    let x = scale * sin(t) * t;
    let y = scale * cos(t) * t;
    point(centerX + x, centerY + y);
  }
}

Improvements: A potential improvement I would like to implement in the future is to randomize the curls in a more organic way, rather than generating uniformly-random points. For instance, they could be generated using my hair semi-circle, with semi-randomly deviations from a perfect line.

Another potential improvement may be to produce an algorithm that can more efficiently draw the rotating spirals. In it’s current implementation, it appears to be quite computationally expensive, and slows down if too many curls are present.

Code

let size = 600;
let midX;
let midY;
let curlNums = 30;
let curlSpeeds;
let curlSizes;
let curlThiccs;

function setup() {
  createCanvas(size, size);
  midX = width / 2;
  midY = height / 2;

  curlSpeeds = generateRandomArray(curlNums, -5, 5);
  curlSizes = generateRandomArray(curlNums, 1, 2);
  curlThiccs = generateRandomArray(curlNums, 2, 5);
  curlXs = generateRandomArray(curlNums, 150, 450);
  curlYs = generateRandomArray(curlNums, 75, 200);
}

function generateRandomArray(length, min, max) {
  return Array.from({ length }, () => getRandomInRange(min, max));
}

function getRandomInRange(min, max) {
  return Math.random() * (max - min + 1) + min;
}

function drawSpinningSpiral(
  centerX,
  centerY,
  scale,
  strokeThickness,
  rotationSpeed
) {
  push();
  translate(centerX, centerY);
  rotate(frameCount * rotationSpeed * 0.01);
  drawSpiral(0, 0, scale, strokeThickness);
  pop();
}

function drawSpiral(centerX, centerY, scale, strokeThickness) {
  let numPoints = 1000;
  let maxT = 6 * PI;
  let step = maxT / numPoints;

  stroke(0);
  strokeWeight(strokeThickness);

  for (let t = 0; t <= maxT; t += step) {
    let x = scale * sin(t) * t;
    let y = scale * cos(t) * t;
    point(centerX + x, centerY + y);
  }
}

function draw() {
  background(255);

  // Head
  noStroke();
  fill("#FFD58C");
  let headSize = size * 0.6;
  let headSizeW = headSize * 0.95;
  let headSizeH = headSize * 1.1;
  ellipse(midX, midY, headSizeW, headSizeH);

  // Eyes
  fill(0);
  let eyeSize = headSize * 0.1;

  // Movement
  let xOffset = 5 * cos(frameCount / 10);
  let yOffset = 5 * sin(frameCount / 10);

  // Left Eye -- todo - ovals
  let leftEyeX = midX / 1.2 + xOffset;
  let leftEyeY = midY / 1.2 + yOffset;
  circle(leftEyeX, leftEyeY, eyeSize);

  // Right eye
  let rightEyeX = size - leftEyeX;
  let rightEyeY = leftEyeY;
  circle(rightEyeX, rightEyeY, eyeSize);

  // Eyebrows
  let eyeBrowWidth = eyeSize;
  let eyeBrowHeight = eyeSize * 0.2;
  let eyeBrowThicc = eyeSize * 0.2;
  noFill();
  stroke(0);
  strokeWeight(eyeBrowThicc);

  // Left eyebrow
  let leftEyeBrowX = leftEyeX;
  let leftEyeBrowY = leftEyeY - eyeBrowWidth;
  arc(leftEyeBrowX, leftEyeBrowY, eyeBrowWidth, eyeBrowHeight, PI, 2 * PI);

  // Right eyebrow
  let rightEyeBrowX = rightEyeX;
  let rightEyeBrowY = rightEyeY - eyeBrowWidth;
  arc(rightEyeBrowX, rightEyeBrowY, eyeBrowWidth, eyeBrowHeight, PI, 2 * PI);

  // Nose bar
  let noseHeight = headSize * 0.08;
  let noseTopOffset = headSize * 0.04;
  let noseTopX = midX;
  let noseTopY = midY + noseTopOffset;
  let noseBottomX = midX;
  let noseBottomY = noseTopY + noseHeight;
  let noseThicc = headSize * 0.02;
  strokeWeight(noseThicc);
  line(noseTopX, noseTopY, noseBottomX, noseBottomY);

  // Nose arc
  let noseArcSize = headSize * 0.06;
  let noseArcLeftX = noseBottomX - noseArcSize * 0.5;
  let noseArcLeftY = noseBottomY;
  let noseArcWidth = noseArcSize;
  let noseArcHeight = noseArcSize;
  arc(noseArcLeftX, noseArcLeftY, noseArcWidth, noseArcHeight, 0, PI);

  // Smile arc
  let smileArcSize = headSize * 0.4;
  let smileYOffset = headSize * 0.25;
  let smileArcLeftX = midX;
  let smileArcLeftY = midY + smileYOffset;
  let smileArcWidth = smileArcSize;
  let smileArcHeight = smileArcSize / 2;
  arc(smileArcLeftX, smileArcLeftY, smileArcWidth, smileArcHeight, 0, PI);

  // Hair
  let hairTopX = midX;
  let hairTopY = midY;
  let hairWidth = headSize;
  let hairHeight = headSize / 2;
  let hairThicc = headSize / 10;
  stroke(20);
  strokeWeight(hairThicc);
  arc(hairTopX, hairTopY, headSizeW, headSizeH, 1.1 * PI, 1.9 * PI);

  // Curls
  let curlScale = 2;
  let curlThicc = 7;
  let curlSpeed = 3;
  for (let i = 0; i < curlNums; i++) {
    drawSpinningSpiral(
      curlXs[i],
      curlYs[i],
      curlSizes[i],
      curlThiccs[i],
      curlSpeeds[i]
    );
  }
}