Week 2 — Art

1.   Sketch and Code

Code

2.   Overview

For this assignment, I created an interactive artwork featuring a grid of neon pink “needles” that respond dynamically to user input. The sketch utilizes vector math, coordinate transformations, and mapped interactive feedback.

3.   Concept

The goal of this work was to explore the concept of a sort of “magnetic field” using geometric shapes. I wanted to create something that felt alive under my curser. The choice of a vibrant pink palette against a deep background was inspired by modern neon-noir visuals as I am somewhat interested in cyberpunk styles, and also my favourite color being pink.

4.   Process and Methods

My process focused on moving from a rigid grid to a fluid, reactive environment. I broke the project down into three main layers:

    • The grid: I used a nested for() loop to populate the canvas. By using a spacing variable, I ensured that the elements were perfectly aligned and easy to adjust.
    • Trigonometric alignment: I used trigonometry to calculate the relationship between each grid point and the mouse to make it interactive. I used atan2() for this to determine the specific angle needed for each shape to face the mouse.
let angle = atan2(mouseY - y, mouseX - x);
    • Visual mapping: I used the dist() function to measure how close each point is to the cursor, then used the map() function to link that distance to the stroke weight, length, and color intensity of the lines.
// Calculate how far each point is from the cursor
let d = dist(mouseX, mouseY, x, y);

// Use the distance (d) to drive the visuals
// Closer to mouse = brighter color and thicker lines
let pinkIntensity = map(d, 0, 400, 225, 50);
let weight = map(d, 0, 400, 5, 1);
let lineLen = map(d, 0, 400, 35, 10);

// Set styles
stroke(255, pinkIntensity, 220); // RGB: Pink variations
5.  Technical Details
    • I implemented push() and pop() within the loops, which allowed me to use translate() to move the origin to each grid point and rotate(). If not for this, every line would rotate around the top-left corner (0,0) of the screen rather than its own center.
    • To achieve the motion blur effect, I set the transparency to be 30 in in the draw() loop, making it so that the previous frames don’t disappear immediately, leaving a trail behind the moving lines.
    • Because the lines are drawn from a negative to positive value relative to the translated center, the rotation occurs perfectly around the midpoint of the line.
line(-lineLen, 0, lineLen, 0);
    • The color is updated dynamically where, as the mouse moves closer, the green channel in RGB increases, shifting the color from a deep purple-pink to a bright, glowing, neon pink.
let pinkIntensity = map(d, 0, 400, 225, 50);
6.  Reflection

This assignment developed my understanding of the transformation matrix (push, pop, translate, rotate). My biggest challenge was getting the rotation to feel natural; initially, the lines were spinning wildly, which was how I found the map() function, which helped me map the distance to the angle. Tiny adjustments to the mapping ranges completely changed the mood of my work.

7.  Resources

Week 1 — Self Portrait

1.   Sketch and Code


Code

2.   Overview

For our first assignment, I developed a sketch that is a non-literal self-portrait. The project features a shark – my favourite animal – swimming through a deep-sea environment. The sketch incorporates procedural animation, gradient rendering, and state-based movement logic.

3.   Concept

My goal of this project was to communicate identity through something other than a human face. I chose a shark because it is my favourite animal. In fact, I own a vast collection of shark themed items in real life, so it felt like the most authentic representation of my personality.

4.   Process and Methods

My process began with translating shark anatomy into geometric shapes:

    • I broke the shark down into a main body (ellipse), a tail (triangles), and facial structure (lines and arcs).
function drawSharkSprite() {
  noStroke();
  fill(160); 
  
  // BODY
  ellipse(85, 60, 150, 50); 
  
  // TAIL
  let tailX = 145;   // Local variable
  triangle(tailX, 45, 185, 60, tailX, 75);  // Connection fin
  triangle(195, 95, 145, 45, 185, 60);      // Bottom tail lobe
  triangle(200, 30, 145, 75, 185, 60);      // Top tail lobe
  
  // FINS
  triangle(90, 15, 65, 40, 95, 40);
  
  // GILLS
  stroke(80); 
  strokeWeight(1);
  noFill();
  for(let i = 0; i < 3; i++) {
    let gx = 65 + (i * 5); // Spacing the gills
    arc(gx, 60, 10, 25, -HALF_PI, HALF_PI);
  }

  // --- HEAD DETAILS ---
  noStroke();
  // Teeth
  fill(255);
  for (let tx = 25; tx < 55; tx += 6) {
    let slantY = map(tx, 25, 55, 72, 65); 
    triangle(tx, slantY, tx + 6, slantY, tx + 3, slantY + 7);
  }
  
  // Slanted Eye
  stroke(0); 
  strokeWeight(3);
  line(25, 58, 35, 52); 
  
  // Slanted Mouth Line
  stroke(166, 58, 55); 
  strokeWeight(2);
  line(22, 72, 58, 65); 
}
    • I studied some p5.js functions to avoid static movement of the shark. Instead of the shark simply sliding across the screen, I used the sin() function to give it a bobbing effect.
// Apply bobbing motion using sin() to the Y translation
translate(shark.x, shark.y + sin(shark.yOff) * 10);
scale(2.0); 
drawSharkSprite();
5.   Technical Details
    • To create depth, I wrote a drawOcean() function that uses a for loop to iterate through the canvas height, and using the lerpColor() function transitions from a darker blue to a lighter blue.
    • For the bobbing motion of the shark, I had to make a sort of mathematical function to make it look like the shark is floating in the water. The y-axis position of the shark is updated in the updateShark() function, which makes it so that the values of the y-offset (animation timer) are altered accordingly to be used in the sine function.
function updateShark() {
  shark.x += shark.vx;
  shark.y += shark.vy;
  shark.yOff += 0.05;    // Increment animation timer

  // Boundary checks
  if (shark.x < 30 || shark.x > 220) shark.vx *= -1;
  if (shark.y < 40 || shark.y > 220) shark.vy *= -1;
}
    • To keep the shark within the visible “ocean” and prevent it from swimming off canvas, I implemented a kind of “bounce” motion with a collision system. Since the shark’s movement is controlled by a y and x velocity, its direction can be reversed by simply flipping the sign of the values if the shark passes a certain x or y value.
    • Rather than drawing each tooth and gill individually, I used loops to ensure that, if the shark’s position changes, the features remain aligned..
    • I used translate() and scale() within the draw() loop to allow the shark to move as a single unit without having to update the (x,y) coordinates for every single shape in the sprite.
6.   Reflection

This project was a significant learning curve in managing coordinates. Initially, my shark would fall apart when it moved because the fins weren’t “attached” to the body’s x and y variables. Learning to use translate() solved this by creating a new local coordinate system for the shark. Also, I had a lot of trouble with the “bounce” mechanism of the shark before properly understanding how the coordinates worked. I am proud of my final result, but I hope that in the future I will be able to complete the coding process quicker now that I’ve better grasped some of the built-in functions and coordinate system.

7.   Resources