Week 4 – Reading Reflection Megan

Something that genuinely drives me crazy (and it’s not in the reading) are the old washing machines that were in dorm laundry rooms. Not even because they’re complicated, but because you literally cannot tell what they are doing. You press start, sometimes it locks, sometimes it doesn’t, sometimes it just stops and you don’t know if it’s broken or thinking. I’ve stood there many times not knowing if I should open it, wait, or restart it. And the worst part is everyone reacts differently, some people keep pressing buttons, some unplug it, some hit it (honestly valid).

But after reading Norman I realized the problem is not that the machine is complicated but it’s that it has no feedback and no signifiers. The interface doesn’t communicate what state the machine is in. Norman explains that good design should make the possible actions discoverable and understandable. But here you don’t know if is it washing? paused? locked error?

The machine technically works, but the interaction fails. The simplest improvement would actually not be adding more buttons but adding better communication. A small progress bar, a timer that updates, or even a message like “Door will unlock in 30 seconds” would fix almost all the confusion. Norman talks about how users shouldn’t need instructions for simple objects, and when they do it means the design is wrong  . A washing machine should not require guessing or trial and error.

This connects a lot to interactive media. When someone opens a sketch or website, they don’t read instructions first. They try to understand it immediately. So discoverability becomes really important. Norman calls this human-centered design, which is designing based on how people actually behave, not how we wish they behaved  .

I realized my own project actually needed these ideas too. For example, at first my visualization was confusing because nothing indicated when the music would start, how to make it stop, and even at the beginning it still takes a bit to load and you don’t know why (even though its because the image and sound is uploading). After thinking about Norman’s principles, the play button became a signifier: it communicates the action you should take. Also the movement of the dots works as feedback. When the music gets louder and the dots expand, the system is telling you your action (playing the song) had an effect.

In interactive media especially, feedback is essential, for example, as Norman explains, users keep pressing elevator buttons when they don’t receive feedback. That’s literally what happens in digital projects too, if nothing responds, users assume it’s broken. So animations, hover effects, and sound reactions are not just decoration, they are communication.

Another idea from the reading is conceptual models. People build a mental explanation of how something works. If the system behaves differently from what they expect, they get confused. My sketch actually depends on this a little I would say: people assume music causes motion, so when the dots pulse with sound, it feels intuitive. They don’t need instructions.

Overall, the reading made me realize interactive art still has to be usable. Even if something is artistic, the viewer should understand how to interact with it. Good design is not just aesthetics, it is communication between the system and the user.

Week 4 – Data Visualization Assignment Megan

“YOaMoNUEvAYoL”

“The only thing more powerful than hate is love.”

“Together, we are America.”

– Benito Antonio Martínez Ocasio 2026

Concept

The concept of this project was to create something connected to the recent social and political conversations about Latin American communities, especially in the United States. Recently I watched the Bad Bunny halftime show that became extremely popular, and as a Latina it honestly felt like a huge moment. Seeing someone singing in Spanish in front of all of the USA, especially considering the controversies around immigration and the fact that many immigrants in the USA are Latino, made me want to base my project around that idea.

Therefore, for the project I chose the song “NUEVAYoL” by Bad Bunny, which was part of the performance and also part of the album that just won two Grammys. The song talks about New York and mixes the American image of the city with a Latin cultural perspective of it. Because of that, I used an image of the Empire State Building that felt vibrant and alive. The image has four dominant colors: orange, light blue, white, and gray. Then I found official 2023 data about Hispanic/Latino population in the United States organized by state. I grouped the states into three main regions: South, West, and Northeast.

Each main color in the image (blue, orange and white) represents one of those regional population groups, and so the amount each color appears is controlled by population data.

The idea is that the skyline is not just a picture anymore. The city lights are metaphorically “powered” by the people who live there. If a region has a larger Hispanic population, that color appears more often in the picture. The music adds movement because when the song gets louder, the dots grow and become brighter, so it feels more like a celebration instead of a static visualization.

To see (and listen) it in action just click the little  ⏯  button on the lower left corner!

Click the play ⏯ button!

Process

Originally I tried to make a migration tree showing people moving from Latin America to the U.S. But when I started doing it, technically it was data visualization, but visually I didn’t like it. It looked more like a diagram than something expressive.

After watching The Coding Train videos about sound visualization, I followed the tutorial and experimented with audio-reactive graphics (This tutorial is also why I kept the little toggle button in the corner to play and pause the audio). I then found this Bad Bunny song and immediately thought about using the Empire State Building because the song is about New York. However, I couldn’t use the song because of issues with the UAE library store so I had to extract the audio from the YouTube video of that song and then cut the mp3 so that the intro of the video that wasn’t part of the song itself. I learned how to do this and did it with my terminal.

First I created a sound visualizer using p5.js amplitude analysis (p5.Amplitude()), which gives a number between 0 and 1 representing how loud the music is at each moment. I mapped that value to the size of the dots so louder music makes the building pulse.

Then I converted the image into a pointillism drawing (i based myself of off these p5.js examples 1, 2). Instead of drawing every pixel, the code samples every 5 pixels in both directions. This reduces detail but still makes visible dots.

After that I connected the dataset. I loaded a CSV file using loadTable() and extracted the column HispanicTotal_2023. I summed populations into regional totals (West, South, Northeast). Instead of placing states spatially, I mapped data into a visual variable: color frequency. So, higher population = higher probability a dot of that color appears.

Media and tools used:

  • p5.js

  • The Coding Train tutorials

  • U.S. Census 2023 dataset

  • audio extracted and trimmed from YouTube

  • ChatGPT used to understand pixel arrays and debugging

One of the hardest parts was understanding how images are stored in p5.js. At first the dots appeared randomly because I didn’t understand that each pixel uses 4 values (RGBA). Once I learned how to correctly calculate the pixel index, the image reconstruction finally worked.

Code that I’m proud of

The part of the code I am most proud of is the section where the program reads the color of each pixel from the image and connects it to the demographic dataset. Instead of drawing the Empire State Building manually, the sketch actually reconstructs it from the photograph itself. The program loads the image and then scans across it every few pixels rather than reading every single one. For each sampled position it calculates where that pixel exists inside the image array, extracts the RGB color values, and then classifies the pixel as sky, window light, highlight, or building structure. After that, the demographic data determines whether the dot is drawn or not, so population directly controls how frequently certain colors appear. Because of this, the building is a data-driven reconstruction made of sampled points. The line that makes this possible is the calculation that converts the (x, y) position on the image into the correct position in the pixel array, which allows the code to access the exact color information for that location.

Another reason this is the part of the code I am most proud of is because I tried many different ways of displaying the data before arriving at this solution. I experimented with horizontal assignments, bar-style representations, mapping individual states directly, and even changing the size of the dots to represent population, but none of those approaches worked visually or conceptually. They either made the Empire State Building unrecognizable or the data unclear.

//sampling the image, skiping every 5 px to make dots
  for (let x = 0; x < img.width; x += 5) {
    for (let y = 0; y < img.height; y += 5) {

      // locate pixel inside the 1D pixel array to extract its color
      let index = (x + y * img.width) * 4;

      // RGB values of that pixel
      let r = img.pixels[index];
      let g = img.pixels[index + 1];
      let b = img.pixels[index + 2];

      // brightness decides how visible the dot should be
      let brightness = (r + g + b) / 3;

      // darker parts = bigger dots for the building silhouette to appear
      let dotsize = map(brightness, 0, 255, 2, 0);

      // music reaction: louder song = dots expand
      dotsize = dotsize + vol * 20;

      // classify the pixel color
let regionType;

if (r > 200 && g > 200 && b > 200){
  regionType = "white";      // bright highlights
}
else if (r > 200 && g > 120 && b < 100){
  regionType = "orange";     // other buildings
}
else if (b > r && b > g){
  regionType = "blue";       // background
}
else{
  regionType = "structure";  // gray building
}

// population controls how often each color appears
let maxRegion = max(westPopu, southPopu, eastPopu);
let allow = 0;

if(regionType === "orange"){        // WEST
  allow = map(westPopu, 0, maxRegion, 0.1, 1);
}
else if(regionType === "white"){    // SOUTH
  allow = map(southPopu, 0, maxRegion, 0.1, 1);
}
else if(regionType === "blue"){     // EAST
  allow = map(eastPopu, 0, maxRegion, 0.1, 1);
}
else{
  allow = 0.15; // so that the building structure always faints
}

noStroke();
fill(r, g, b);

if(random() < allow){
  circle(x, y, dotsize);
}
}

Overall Reflection

Overall I’m really happy with the final result. I like thaat it balances data and aesthetics. It is not a traditional chart, but it still encodes real demographic information so you don’t read numbers directly, but still you perceive distribution through color presence.

If I continue this project, I would add multiple years of census data and animate the skyline over time so you could see growth instead of a single snapshot. Another improvement would be adding clearer interaction (for example hovering to reveal which region each color corresponds to). Although, a limitation is that the visualization depends on the colors of this specific image so if the image changes, the mapping must also change.

Week 3 Reading Reflection – Megan

After reading this chapter I realized that a strongly interactive system is really like having a good conversation. Both sides have to listen think and speak well for it to feel alive. If one side fails it just becomes boring or frustrating. So for a system to be truly interactive it has to respond to the user in a way that feels meaningful not just random or automatic. I liked how Crawford explains that interaction is not the same as reaction like with a fridge or a movie you can only watch but you don’t actually talk to it. That made me think about my p5 sketches because sometimes I make things move or change when the mouse touches them but it feels kind of one-sided. I realized that to make them more interactive I could have the objects respond in different ways depending on how you interact with them like changing speed direction color or even start a little animation that is unique for each action. I also liked thinking about giving each object its own “voice” like the dice in my project could react differently to the same input so it feels more alive and less predictable. I want to experiment more with letting the user affect not just the movement but the behavior and appearance over time so it feels like the sketch is listening and thinking a little bit before it reacts. Overall reading this made me want to make my sketches feel more like a real conversation between me and the code rather than just me controlling it.

Week 3 -Object Oriented Programming Megan

Concept:

The concept of this project comes from cacho, a traditional Bolivian dice game. The game is played by putting five dice inside a small leather cup, and you try to roll combinations like a poker hand, for example House, Full, etc. This game caught my attention because all the dice are identical, so I thought I could use them as objects for this project in object-oriented programming.

click it!

Process:

For the process, I first started with a photo of the cubilete, the cup, and the dice inside to be able to draw it in p5.js. I used my iPad to make a sketch highlighting the geometric shapes I needed and how I had to rotate them at different angles. At first, I used translate to set the origin at zero, zero, but later I switched to WEBGL because the dice are 3D geometric shapes.

For the rotations, I set the canvas to use Degrees so I could control exactly how much each shape rotated. That is how I made the base drawing. Then came the dice, which was the most difficult part. I started with 2D using rect, and I wanted them to rotate, so I created a move function inside the Dice class and used rotate so that every time a dice completes a full rotation, it moves forward.

Then I realized 2D did not really give the impression of a real dice, so I switched to 3D and used box from the p5.js 3D examples. For some reason, the box was deforming, its sides would stretch or cut off, so I had to apply rectMode(CENTER), which I was already using for 2D. After trying several methods, this was the only one that kept the cubes from deforming, probably because a box is made of rectangles, and if they are centered, they do not distort.

Next, I wanted to put numbers on the dice, but that turned out to be very complicated. When I tried, the dice faces blinked and did not display properly. The solution I found was to color the different faces with various shades of white and gray to make them look more realistic. I also made sure each dice had a random starting position so they did not all start at the same place.

Another very important part of the process was setting boundaries for the dice. At first, when I got a square to move, it went off the canvas, so I looked at examples from p5.js on how to use the command constrain on balls and adapted that idea. I set random limits so each dice would stop at different places on the table, giving the scene more realism like when you through dice. A key detail was making sure the dice stop rotating once they reach their limits because when I first tried it the dices would keep going on circles once they stopped moving forward.

Full code:

let dice1;
let dice2;
let dice3;
let dice4;
let dice5;

function setup() {
  createCanvas(600, 600, WEBGL);
  colorMode(HSB);
  //Random starting points
  dice1 = new Dice(random(-50, 30), random(-200, 30));
  dice2 = new Dice(random(-50, 30), random(-200, 30));
  dice3 = new Dice(random(-50, 30), random(-200, 30));
  dice4 = new Dice(random(-50, 30), random(-200, 30));
  dice5 = new Dice(random(-50, 30), random(-200, 30));
}

function draw() {
  background("#deb887");
  angleMode(DEGREES);
  
//Table
  push();
  rotate(48);
  fill("black");
  rect(-320, -250, 800, 800);
  pop();
  
  push();
  rotate(20);
  fill("black");
  rect(-210, -310, 500, 200);
  pop();
  
// Cubilete
   //Outside
  fill(30, 85, 30);
  noStroke();
  quad(-193,-286, -250, -130, -100, 40, 40, -230);
  push();
  rotate(14);
  fill(30, 85, 30);
  ellipse(-253, -145, 102, 172);
  pop();
  //Inside
  push();
  rotate(19);
  fill(120, 100, 15);
  stroke(30, 85, 38);
  strokeWeight(15);
  ellipse(-50, -77, 220, 292);
  pop();
  
  //Dice
  dice1.move()
  dice1.show()
  dice2.move()
  dice2.show()
  dice3.move()
  dice3.show()
  dice4.move()
  dice4.show()
  dice5.move()
  dice5.show()
}

// Function to restart dice when clicking
function mousePressed(){
  dice1 = new Dice(random(-50, 30), random(-200, 30));
  dice2 = new Dice(random(-50, 30), random(-200, 30));
  dice3 = new Dice(random(-50, 30), random(-200, 30));
  dice4 = new Dice(random(-50, 30), random(-200, 30));
  dice5 = new Dice(random(-50, 30), random(-200, 30));
}
class Dice{
  constructor(_x, _y){
    this.x= _x;
    this.y= _y;
    
    //So that dice can rotate
    this.angle=0
    
    //Random limit to make dice stop going forward at a certain place in the table 
    this.maxX = random(-10, 260);
    this.maxY = random(-10, 260);
    
    this.colors=["#FFFFFF", "#808080", "#D3D3D3", "#FFFDD0", "#F5F5F5", "#FAEBD7"]
    
    //To stop dice rotation
    this.stopped = false;
    
  }
  move(){
    //To make dice stop rotating when limit reached
    if (!this.stopped){
    
      //Movement forward
    this.x += 3
    this.y += 3
    
    //Limits for dice to not go past the table
    this.x = constrain(this.x, -10, this.maxX);
    this.y = constrain(this.y, -50, this.maxY);

    //If limit reached, stop rotation
    if (this.x >= this.maxX && this.y >= this.maxY) {
        this.stopped = true;
      }
    
    
    //To make them roll
    this.angle += 15; 
    this.angle %= 360;
     
    }
  }
  
 show(){
  push();
  translate(this.x, this.y);
  rotate(this.angle);
  rectMode(CENTER);

  noFill(); // Base box color
  box(65);   // 3D dice

  // Color each face with planes 
  let s = 65/2; // Half the size of the dice, used to position the planes at each face

  // Front face
  push();
  translate(0, 0, s); 
  fill("#FFFFFF");         
  plane(65, 65);       
  pop();

  // Back face
  push();
  translate(0, 0, -s); 
  rotateY(180);         
  fill("#808080");        
  plane(65, 65);        
  pop();

  // Right face
  push();
  translate(s, 0, 0);  
  rotateY(90);          
  fill("#D3D3D3");         
  plane(65, 65);
  pop();

  // Left face
  push();
  translate(-s, 0, 0); 
  rotateY(-90);         
  fill("#FFFDD0");       
  plane(65, 65);
  pop();

  // Top face
  push();
  translate(0, -s, 0); 
  rotateX(90);          
  fill("#F5F5F5");       
  plane(65, 65);
  pop();

  // Bottom face
  push();
  translate(0, s, 0);  
  rotateX(-90);         
  fill("#FAEBD7");       
  plane(65, 65);
  pop();

  pop(); 
  }
}

Code I am proud of:

I am especially proud of the code I wrote to set the boundaries for the dice. As I mentioned, it was really challenging to figure out how to make the dice stop moving and rotating once they reached their limit. Also, making sure the dice did not deform while rolling was tricky, and using rectMode(CENTER) solved that problem perfectly.

//Random limit to make dice stop going forward at a certain place in the table 
    this.maxX = random(-10, 260);
    this.maxY = random(-10, 260);
    
    
    //To stop dice rotation
    this.stopped = false;
    
  }
  move(){
    //To make dice stop rotating when limit reached
    if (!this.stopped){
    
      //Movement forward
    this.x += 3
    this.y += 3
    
    //Limits for dice to not go past the table
    this.x = constrain(this.x, -10, this.maxX);
    this.y = constrain(this.y, -50, this.maxY);

    //If limit reached, stop rotation
    if (this.x >= this.maxX && this.y >= this.maxY) {
        this.stopped = true;
      }
show(){
  push();
  translate(this.x, this.y);
  rotate(this.angle);
  rectMode(CENTER);

  noFill(); // Base box color
  box(65);   // 3D dice

Overall reflection:

The hardest part was definitely trying to put numbers on the dice, which I did not fully achieve. Maybe with more research on 3D objects, I could eventually make it exactly how I intended. But I really liked the final result, especially because it looks a lot like the cacho I play in real life. I also enjoyed working with objects because it made it easy to create multiple dice without repeating code, giving each one different positions and starting points that I could edit freely.

Overall, this project taught me a lot about working with 3D shapes and object-oriented programming. I learned that even small details, like the starting position, rotation, and boundaries, make a huge difference in making the animation feel realistic. I am happy with how I managed to combine my reference from real-life cacho, geometric thinking, and programming, and I feel more confident creating multiple interactive objects in p5.js for future projects.

Week 2 – Creative Reading Megan

Casey Reas Eyeo talk on chance operations

How are you planning to incorporate random elements into your work? Where do you feel is the optimum balance between total randomness and complete control?

Before answering this questions, I wanted to implement by myself the concepts the video was talking about. Randomness. One quote by Casey Reas really stayed with me: “change it by a little so that movement creates chaos in an ordered way.” And I think that perfectly represents what I was trying to do in my work. The things I changed to create this element of randomness were data, small pieces of data that might seem insignificant at first, but machines are built based on that, so even the smallest thousandth can make a difference.

This brings me to another quote from the video, about Dadaism: “Dada wished to replace the logical nonsense of the men of today with an illogical nonsense.” To what point is something considered logical? Computers are supposed to be based on pure logic. And yet, artists find ways to turn that logic into illogical sense. And yet, it still has meaning. It’s like deconstructing the logic embedded in the machine with the purpose of illogically creating something that has meaning behind it. Or the other way around, creating something without meaning by using the logic of the computer. Either way, I feel this can be applied to the artworks shown in the video.

A quote by Gerhard Richter captures this idea very well: “Above all, it’s never a blind chance; it’s a chance that is always planned but also always surprising.” These artists construct from chance as a base. It’s about bringing in disorder that has been curated with intention, in a way that even surprises the artist themselves. I think that finding this balance between total randomness and complete control is about using logic and repetition, patterns and algorithms that allow you to repeat a process, while the outcome is completely different, but the essence of it remains present.

Something that really caught my attention in the video was the idea of the million random digitized values and the 1,000 normal deviates. It honestly amazed me to think about the power, the expectation, and the importance that randomness has in our lives when we understand that art imitates or simulates the real world, and the real world is chaotic and not curated by anyone, but rather filled with randomness. Without a doubt, this video and this work opened my eyes and helped me understand even better the importance of chaos within order, and order within chaos.

Week 2 – Generative Art Megan

Concept

“The whole universe is based on rhythms. Everything happens in circles, in spirals.” — John Hartford

The idea of my artwork was that I wanted to create a spiral using the colors of the flower often called the “national Bolivian” flower, the kantuta. This flower is one of the most amazing ones, not only because of its vibrant colors, but also because it grows up to 3,800 meters above sea level, in its native land in the Andes. This flower has a lot of meaning for me, starting with its colors red, yellow, and green, which are those of the Bolivian flag, and also because it represents the nature and resilience of my home country.

Speaking of nature, I wanted to represent this flower through some kind of shape. Spirals are a shape that is found everywhere in nature and, for me at least, they are mesmerizing to look at and to understand. Inspired by the video we watched on randomness in digital art, I wanted to find an “organic” yet “random” way of showcasing the kantuta. The spiral has movement and flow without breaking its structure or bringing chaos. Moreover, it has such a unique presence, it almost calls the viewer to follow its path, and it feels like you’re watching something alive.

That’s why, and as I promised myself in the last exercise, I wanted to challenge myself, so I looked up many tutorials on how to create spirals in p5.js.

Run it and Click it!

Process

Most of my learning for this project came from these video tutorials on YouTube by Strive Math. First, I had to learn about transformations and translations in p5.js. It has to do with the coordinates in the canvas you’re creating. So, when you translate, you move the entire coordinate plane up, down, left, or right. And when you transform, you’re rotating the entire coordinate plane at an angle that you choose (you must select if the angle will be measured in degrees or radians first). It rotates clockwise if the number is positive and counterclockwise if it’s negative.

With transformations, you rotate the axis around the center point you chose when you did the translation. So, with this, I first learned how to draw a simple circle using these concepts. For example, if I translate the origin so it is right in the middle of the canvas, and I keep making this coordinate plane rotate clockwise while drawing a dot somewhere that is not the origin, the dot will keep moving with the coordinate plane and trace a circle when the coordinate plane completes a full 360° rotation.

This is how the code for that looks (this is no longer in code as such since it was just to practice and understand how it works):

function setup() {
  createCanvas(600, 600);
  angleMode(DEGREES)
}

let angle = 0

function draw() {
  translate(width/2, height/2)
  rotate(angle)
  angle++
  point(30,0)
}

The next step, was to think how this could turn into a spiral. Since the circle shape that I made is actually just a point following a path that is determined by the radius of the circle, if I increase the radius of the circle, and as the point moves more the radius also increases more, this will create a path of a spiral. Therefore now the code will look like this:

let angle = 0
let radius = 0

function draw() {
  translate(width/2, height/2)
  rotate(angle)
  angle+= 31
  radius+= 0.4
  strokeWeight(6)
  point(radius,0)

Now I can control the way the spiral looks by playing with the amount of degrees the angle of the coordinate plane is going to change every time and how wide will the radius of the circle increase be. A small issue with this was that the frame rate of how fast or how slow the points would appear will sometimes be too slow and I wanted to change that. Therefore, just like in the tutorial that i watched, I inserted a for loop to control the frame rate of everything, and for it to not change and save the sate of the canvas I used the ‘push’ and ‘pop’ commands so that the origin always stays in the center of the canvas and doesn’t move every time the for loop repeats.

As I said, I wanted to implement the concept of randomness, so I created random variables for the values of the angle and the radius to be able to make a different spiral every time you click on the screen. To make this happen, I added a mousePressed() function that resets the angle and radius to their initial values, generates new random values for the angle increment and radius increment, and clears the canvas:

function mousePressed() {
  angle = 0;               
  radius = 0;             
  angleValue= random(300, 3000); 
  radiusValue = random(0.009, 0.2);
  background("black");    
}

This way, every time the canvas is clicked, the spiral starts fresh with completely new random parameters, creating a unique pattern while keeping the interaction simple and intuitive. However, it took me a while to figure out this approach, because at first I tried using a boolean variable and a separate restart function in a more complicated way, and my code would get messy: sometimes the spiral wouldn’t appear, or new spirals would overlap with the old ones. Then I realized, why complicate things? I could just reset the main variables and redraw, and it worked perfectly.

Something I did very intentionally was set the limits of the random value of the angle to very big numbers and the numbers of the radius to very small decimals. I did this because I started playing around with those values and I realized the smaller the radius was the more dense the spiral looked and if the angle was a high number there was more chance of there being more ‘spiral lines’. I liked how this looked because it made the spiral look more like a piece of art. I also changed the background color to black so that the colors of the points would stand out more.

Code I am proud of

Then came the most important part: the color of the kantuta image. I uploaded the image to p5.js and made the spiral match the colors of the pixels by randomly selecting a pixel from the image and using its color for each point on the spiral.

let ix = floor(random(img.width))   
    let iy = floor(random(img.height))  
    let c = img.get(ix, iy)             
    stroke(c)

I’m really proud of this code because it wasn’t easy to get it right at first. I had to change the image I was using because the first one had too much black, which blended with the background and made the spiral almost invisible, and it also took too long to load. Then I realized that I needed to extract the colors pixel by pixel from the image, and I did this inspired by the p5.js example of pointillism, where each point takes the color of a random pixel from the image. This finally permited me to have a spiral that not only looked colorful and vibrant, but also represents the kantuta in a way that felt alive and organic, and I really liked it.

Final Code

//Image variables
let img;

//Image upload
function preload() {
  img = loadImage("kantuta.jpg");
}

//==============
//Setup
//==============
function setup() {
  createCanvas(600, 600);
  background("black")
  angleMode(DEGREES);
  frameRate(80);

  angleValue= random(300, 3000)
  radiusValue = random(0.009, 0.2)

  img.loadPixels(); // allow us to access image pixels
}

//Spiral variables
let angle = 0
let radius = 0

let angleValue
let radiusValue

//===================
//Drawing the spiral
//===================

function draw() {
  //To control the frame rate
  for (let i= 0; i<80; i++){
    
  push()//save the state of our canvas
  
  translate(width/2, height/2);
  rotate(angle);
  angle+= angleValue;
  radius+= radiusValue;
  strokeWeight(4);
    
  //Pick a color from the image
    let ix = floor(random(img.width))   
    let iy = floor(random(img.height))  
    let c = img.get(ix, iy)             
    stroke(c)                          
    
   point(radius,0);
  
  pop()//come back to the old state before 'push'
  }
}

// ===================
// Replay spiral on click
// ===================
function mousePressed() {
  angle = 0;               
  radius = 0;             
  angleValue= random(300, 3000); 
  radiusValue = random(0.009, 0.2);
  background("black");    
}

General Reflection

Overall, I think this project taught me a lot about patience, experimentation, and problem-solving. I also realized how important it is to simplify my approach: sometimes trying too many complicated solutions only slows you down, and the best results come from understanding the core idea and working with it directly.

I’m proud of how the final spiral turned out, not just technically but also conceptually. I feel like it feels alive, vibrant, and connected to something meaningful, which reflects the beauty of the kantuta and the creative process behind it. This project was the hands-on reminder of the video we saw that taught me that coding can be as expressive as painting or drawing, and that combining randomness, structure, and careful choices can create something truly mesmerizing.

Week 1 – Self Portrait by Megan Del Villar

Concept

I wanted to create a self portrait that genuinely reflects who I am. I love the color green, and for some reason sticking my tongue out has become a small habit of mine whenever I do not know what to say or when I am in an awkward situation. It works as my personal ice breaker. On top of that, I had never animated anything before and had never programmed in JavaScript. Because of this, I decided to challenge myself by animating the tongue in my self portrait.

Click on it!

Implementation

For the self portrait overall, I used basic geometric shapes such as ellipses and circles, mainly working with the stroke of ellipses. For the bangs and the eyelids, I was inspired by this self portrait example by fenfenrita that I found on p5.js. From that example, I learned that you can use only the stroke of an ellipse to create curved lines and that by adjusting the numerical values you can control how stretched or flattened the stroke looks. I experimented extensively with this technique for the eyelids and later applied it to the eyebrows, nose, and mouth.

For the bangs, that same example, combined with experimenting with PI, TWO_PI, and HALF_PI, helped me understand how ellipses and circles can be partially filled using angles. I adapted a similar code structure and adjusted the size, position, and colors to match my own portrait.

Since I have a background in graphic design, I am used to working with HSB color mode, so I changed the color mode at the beginning to keep the entire portrait consistent. For more specific colors, I searched for their hex codes online and used them directly. The rest of the code was taken from the p5.js library. For the animation, I used the mousePressed function and created a tongueOut function so that the tongue only appears when the self portrait is clicked.

Code I Am Proud Of

// Tongue animation
let tongueOut = false;

// Tongue out or in when clicked
if (tongueOut) {
  fill("#C76A6A");
  noStroke();
  arc(305, 372, 40, 55, 0, PI);
}

//Tounge animation
function mousePressed() {
  tongueOut = !tongueOut;

I am especially proud of how I managed to make the tongue animation work, because deciding how to implement it took a lot of trial and error. At first, I wanted the tongue to move along the x axis of the mouse as it moved across the screen. However, this required setting limits, otherwise the tongue would move outside the mouth area and even beyond the canvas. When I finally achieved this, I did not like the result because the movement was not very noticeable unless the mouse moved a lot.

After experimenting more, I explored other mouse related functions and realized that using mousePressed would allow the tongue to appear and disappear completely. This interaction felt much cleaner and more intentional, so I decided to implement it this way.

Reflection

Overall, I really enjoyed this project and learned a lot, especially considering it was my first time using a program like this and my first time discovering that it is possible to draw using code. I understood everything I was doing, and the more I played with numerical values and visual elements, the more connected I felt to my own drawing. For a future project, I would like to challenge myself even more by creating animation without using the mouse and by experimenting with different types of shapes.