Week 4 – Loading Data, Displaying text

Concept

I wanted to experiment with ASCII art in a dynamic way, using Perlin noise and interactive effects to create an evolving text-based visualization (I got inspired by the Coding Train videos on Perlin noise and this image manipulation + ASCII density coding challenge by him).

Each character is selected based on noise values, and their colors shift over time. As the mouse moves, it creates a ripple effect by “pushing” nearby characters away, giving a fluid and organic motion to the grid.

Demo (Click on canvas)

Code Highlight:

This part of the code uses Perlin noise to smoothly select characters from the density string and generate dynamic colors for each character.

// Using Perlin noise for smoother character selection
let noiseVal = noise(i * 0.1, j * 0.1, frameCount * 0.02) * density.length;
let charIndex = floor(noiseVal);
let char = density.charAt(charIndex);

// Generate color based on Perlin noise
let r = noise(i * 0.05, j * 0.05, frameCount * 0.05) * 255;
let g = noise(i * 0.07, j * 0.07, frameCount * 0.05) * 255;
let b = noise(i * 0.09, j * 0.09, frameCount * 0.05) * 255;

fill(r, g, b);
text(char, newX, newY);

How it works:

  • noiseVal determines the ASCII character by mapping noise to the density string length.
  • r, g, and b are also mapped to Perlin noise, creating a smooth, organic color transition over time.

Reflection & Improvements for Future Work

This experiment opened up some interesting possibilities for ASCII-based generative art. I’d love to refine the animation by adding more layers of motion, perhaps introducing gravity-like effects or different interaction styles. Another idea is to use real-time audio input to influence the movement, making the piece reactive to sound. Definitely a fun one to explore further!

Week 4: Generative Text

Concept:

This project is a gentle companion for women across different phases of their cycle. Each phase—Follicular, Ovulation, Luteal, and Menstrual—reveals its own short, reflective text. The more difficult phases, Luteal and Menstrual, include extra motivating lines to offer comfort and encouragement. Overall, it acts like a quote page, providing small bursts of support and understanding. The goal is to create a sense of connection and help women feel acknowledged in whatever day they find themselves.

Highlight:

I believe that input parsing and validation is a highlight because it makes sure the user’s number is always correct. It was tricky to get the latest number when users changed the value without pressing Enter. Moving the number conversion to the button click made sure we always use the newest value. Handling wrong numbers and showing clear messages was tough, but it is key for a smooth experience.

// if valid, choose a random entry based on phase
  let entry = "";
  switch (currentPhase) {
    case "Follicular":
      entry = random(follicularEntries); // pick random phrase
      break;
    case "Ovulation":
      entry = random(ovulationEntries);
      break;
    case "Luteal":
      entry = random(lutealEntries);
      entry += " " + random(motivationalPhrases); // add extra motivation
      break;
    case "Menstrual":
      entry = random(menstrualEntries);
      entry += " " + random(motivationalPhrases); // add extra motivation
      break;
  }

  diaryEntry = entry; // store the chosen entry
  isGenerated = true; // mark as generated
}

//validates if the day is within the correct range for the phase

function validateDayRange(phase, day) {
  if (phase === "Follicular") {
    // allowed days: 1 to 13
    return day >= 1 && day <= 13;
  } else if (phase === "Ovulation") {
    // allowed days: 14 to 16
    return day >= 14 && day <= 16;
  } else if (phase === "Luteal") {
    // allowed days: 17 to 28
    return day >= 17 && day <= 28;
  } else if (phase === "Menstrual") {
    // allowed days: 1 to 5
    return day >= 1 && day <= 5;
  }
  return false; // default false
}

Reflections, ideas for future work & Improvements:

For future work, I plan to add more customization options and richer animations. I want to explore saving user entries so that they can track their mood over time. I also plan to refine the validation process and introduce more advanced error handling. These improvements would make the project even more useful and appealing as a supportive quote page for women.

Week 4 – Reading Response

Faucets, soap dispensers, and towel dispensers in public restrooms can be really frustrating. One reason is because of the uptick in automatic dispensers and sinks. Sometimes, faucets look like they’re automatic but are really operated by handles, and same thing goes for soap/towel dispensers. Another frustrating point is when soap dispensers look like faucets. I also once saw an air/hand dryer, soap dispenser, and faucet all with the same color, build, and shape surrounding a sink; the only distinguishing factor was their size. I had to test out all of them to figure out which one was which.

Don Norman says, “The same technology that simplifies life by providing more functions in each device also complicates life by making the device harder to learn, harder to use. This is the paradox of technology and the challenge for the designer”, which I thought was interesting. The designer has to make complicated information easily digestible. Designers are like the middlemen between new technology and users.

I also think the point about having to spell out a function or affordance for users with words is poor design is compelling, but also very difficult to get right. Making designs intuitive is the ultimate goal, but the thing about intuition is that you don’t consciously think about it, so designers can’t know immediately what it intuitive. Too often, I personally lean towards words to let people know what a design does. However, I need to get more used to designing without words and simply use visual or physical cues to guide a user’s attention.

Don Norman’s main idea about how people aren’t at fault for poor design is also very important. It’s up to designers, engineers, manufacturers, and businesses to create a pleasant user experience; if a user isn’t enjoying their product, it’s not the user’s fault. Too often, experts are biased in their own highly skilled perspective that they expect others to be on that level. For example, sometimes when I’m teaching someone a skill like crochet, I see the stitches and shapes very easily, but they can’t. If I were to design a beginner’s crochet kit or tutorial, I need to take into account the average beginner’s perspective.

I really liked how Don Norman distinguishes between affordances and signifiers, indicating that affordances are relationships between people and designs, and signifiers are physical attributes that lend to discovering affordances. Distinguishing them helps designers understand what they’re working towards and how to work towards it.

Week 3 – Assignment

Introduction: Inspiration and Thought Process

When approaching this project, I wanted to create something that felt organic, dynamic, and visually engaging while staying true to my interest in systems, patterns, and movement. I’ve always been fascinated by the hidden structures that govern the natural world—whether it’s the way air currents shape cloud formations, how magnetic fields interact with charged particles, or how fluid dynamics influence ocean currents. These invisible forces dictate movement and structure on both microscopic and massive scales, yet they often go unnoticed in daily life.

This fascination led me to explore generative art as a way to reveal these hidden forces. By using a flow field generated through Perlin noise, I aimed to simulate an abstract yet realistic movement of particles through a force-driven system. The goal was to make the movement feel both unpredictable and structured—like a balance between chaos and order, mirroring how natural systems operate.

Another reason this concept resonated with me is my interest in interactive media and computational design. I see creative coding as a bridge between logic and aesthetics—using algorithms not just to solve problems but to create emotionally engaging experiences. This project became a way to explore how simple rules (vector fields and movement constraints) can lead to complex, emergent behaviors.

How I Decided on the Approach

Initially, I considered different ways of visualizing movement:

  • Cellular automata (which follows a discrete rule set)
  • Particle systems with basic physics (which simulate real-world gravity and collisions)
  • Algorithmic drawing techniques (such as recursive fractals)

However, I specifically wanted smooth, flowing movement, where particles appear to drift through an unseen force field rather than follow rigid, predictable patterns. This led me to research flow fields, a technique often used in generative art to create dynamic motion based on vector fields derived from Perlin noise. The key insight was that by giving each particle a force vector at every point in space, I could create an artwork where movement itself became the visual expression.

Connecting to My Interests

This project ties into my broader interest in interactive systems and generative design. In the future, I could see expanding this into an interactive piece, where users could manipulate the flow field in real-time, changing the behavior of the particles with gestures or sound inputs. Additionally, this exploration of emergent patterns aligns with my curiosity about how simple rules can create complexity, something that applies not just to visual art but also to fields like engineering, physics, and artificial intelligence.

Ultimately, this artwork serves as both a study of motion and structure and a reflection of how natural forces shape our world in ways we don’t always perceive.

My code:

let particles = [];
let flowField;
let cols, rows;
let scl = 20; // Scale of the grid
let zOff = 0; // Noise offset for animation

function setup() {
  createCanvas(600, 600);
  cols = floor(width / scl);
  rows = floor(height / scl);
  flowField = new Array(cols * rows);

  // Create particles
  for (let i = 0; i < 500; i++) {
    particles.push(new Particle());
  }
}

function draw() {
  background(0, 10); // Faint trail effect

  // Generate the flow field using Perlin noise
  let yOff = 0;
  for (let y = 0; y < rows; y++) {
    let xOff = 0;
    for (let x = 0; x < cols; x++) {
      let index = x + y * cols;
      let angle = noise(xOff, yOff, zOff) * TWO_PI * 4;
      let v = p5.Vector.fromAngle(angle);
      flowField[index] = v;
      xOff += 0.1;
    }
    yOff += 0.1;
  }
  zOff += 0.01;

  // Update and display particles
  for (let particle of particles) {
    particle.follow(flowField);
    particle.update();
    particle.edges();
    particle.show();
  }
}

// Particle class
class Particle {
  constructor() {
    this.pos = createVector(random(width), random(height));
    this.vel = createVector(0, 0);
    this.acc = createVector(0, 0);
    this.maxSpeed = 2;
    this.color = color(random(255), random(255), random(255), 100);
  }

  follow(vectors) {
    let x = floor(this.pos.x / scl);
    let y = floor(this.pos.y / scl);
    let index = x + y * cols;
    let force = vectors[index];
    this.applyForce(force);
  }

  applyForce(force) {
    this.acc.add(force);
  }

  update() {
    this.vel.add(this.acc);
    this.vel.limit(this.maxSpeed);
    this.pos.add(this.vel);
    this.acc.mult(0);
  }

  edges() {
    if (this.pos.x > width) this.pos.x = 0;
    if (this.pos.x < 0) this.pos.x = width;
    if (this.pos.y > height) this.pos.y = 0;
    if (this.pos.y < 0) this.pos.y = height;
  }

  show() {
    stroke(this.color);
    strokeWeight(2);
    point(this.pos.x, this.pos.y);
  }
}

 

Week 4 – Data Visualization

Link to sketch: https://editor.p5js.org/bobbybobbb/full/rBBWMFyxx

When brainstorming for this project, I wanted to represent data in a way unique to the dataset. I wanted to use what the data was about and frame it in a visually engaging way that best represented what it was about. I was digging through Kraggle and found a simple dataset about streaming platforms, so I thought it’d be cool to represent it inside a TV and the glitchy effects of a TV screen. Each bar of glitch/color is proportional to the percentage of shows or movies the streaming platform has compared to the others. Since there were two sets of data, I created buttons on the TV to toggle between them; I really liked how the functionality of the system was tied in to the visual cohesion of the piece. The colors for the glitches are also randomized, so each time you click the buttons, the colors will change regardless of whether or not the dataset changed.

Some challenges I encountered included not being able to pass in arguments to a function I call when the buttons are pressed. To overcome it, I found a way to change the mousePressed function:

showsButton.mousePressed( function(){
  displayData(2);
});

In the future, I want to make this piece more engaging because it feels a bit more static right now. Maybe users can click on the specific streaming platform and learn more about it. Or I can find more data on these platforms and users can see even more data visualization upon clicking each individual platform. Overall, this project came with a lot of testing and looking for datasets. I had to frequently test the spacing of the rectangular bars used to represent each streaming platform, text rotation, and button sizing. There was also a lot of math involved for turning the values in the dataset into rectangles that fit within a confined space.

Week 4 – Reading Response

This reading was both very illuminating and validating, especially in regards to the initial discussion of confusing doors. I had always found UI design to be an interesting topic, and this glimpse into the field of design as a whole was a lot more involved than I was prepared for. I was definitely impressed by how much work goes into properly designing a product, only for the bestowed ease-of-use resulting in users remaining completely oblivious to the effort put in. In that sense, it is similar to how good audio mixing in a song or film will go unnoticed, but bad audio will completely pull you out of the experience. There were also some good points made later on about having to carefully balance the different aspects of design, such as signals and feedback being informative yet unobtrusive, or products being feature-rich while remaining affordable.

As for applying these design principles to interactive media, I am at a bit of a loss. In the past few weeks I have experimented with using simple forms of interactivity like clicking or using arrow keys to move an object in the p5.js scene. These ended up being fairly intuitive in the sense that they are reasonable choices that one would expect when using a computer. However, if the user was not aware that these pieces were interactive to begin with, a huge chunk of the experience is lost. I had left comments at the top of the code detailing the viable inputs, but of course this requires navigating from the WordPress site to the p5.js editor. A straightforward way to deal with this would be to include text instructions in the piece itself, or having some sort of help menu, but I still feel like this would negatively impact the more artistic pieces.

Reading Reflection – Week #4

In The Design of Everyday Things by Don Norman, the main argument is that usability issues stem from poor design rather than user error. This challenges the common assumption that people are to blame when they struggle with everyday objects. Come to think of it, well-designed objects should naturally guide users, minimizing confusion.

One example of poor design that was not mentioned in the reading but has always frustrated me are cable, especially USB ones. The traditional USB connector can only be inserted one way, yet there is rarely a clear indication of which side is correct. This often leads to flipping the plug multiple times before it fits. A simple signifier like a physical marker or a visible point, could solve this issue and make the interaction effortless.

Norman’s principles are also highly relevant to interactive media. Websites and apps often suffer from unclear navigation, hidden menus, or buttons that don’t look clickable. Applying affordances and signifiers like distinct buttons and clear labels would improve usability. Similarly, feedback is important as when users submit a form or click a link, they should receive a confirmation to avoid uncertainty.

week4-text display

Concept & Reflections

Visual Metaphor:
When I created this piece, I envisioned the soft, drifting clouds as a metaphor for the ever-changing nature of our thoughts and emotions. Like clouds that pass through our skies, our feelings are transient and beautiful in their subtlety. The kind words dripping from them represent those gentle, uplifting messages that, although they may seem small at first, eventually leave a lasting impression on our hearts.

Interactive Design:
I designed the clouds to continuously emit letters—a dynamic, ever-changing canvas that mirrors life itself. It feels like these tiny bursts of positivity, dropping down from above, gradually build up over time. It’s a reminder that even the smallest kind words can accumulate and transform our mood, much like a few gentle raindrops can eventually water a parched garden.

Further Exploration:
Working on this project made me realize that there’s so much more to explore. What if, by simply clicking on a cloud, you could change the words, or adjust the drip speed, almost as if you were altering the course of your own thoughts? Experimenting with different color schemes or fonts could also deepen the emotional resonance of the piece, evoking the exact mood you need on a tough day.

For me, this project wasn’t just about code it was about capturing the delicate interplay between fleeting thoughts and the lasting impact of kind words. I hope it inspires you to see how even the softest whispers of positivity can make a profound difference in our lives.

Code im proud of :

// Return the next letter of the word, or null if finished
  getNextLetter() {
    if (this.letterIndex < this.word.length) {
      let letter = this.word.charAt(this.letterIndex);
      this.letterIndex++;
      return letter;
    } else {
      // Option: reset to loop the word
      // this.letterIndex = 0;
      // return this.word.charAt(this.letterIndex++);
      return null;
    }
  }
  
  update() {
    // Slow horizontal drift
    this.x += this.driftSpeed;
    if (this.x > width + this.size) {
      this.x = -this.size;
    }
    // Optionally, add slight vertical oscillation for more life
    this.y += sin(frameCount * 0.005) * 0.5;
  }

 

Reading Reflection – Week 4

Something that drives me crazy is doing laundry on campus. Many of the drying machines in my building don’t show the duration of the cycle/program, so it is hard to know exactly when it will finish and often times, I end up making multiple trips to the laundry room just to take out my clothes from the dryer. The washing machines are also not without their problems – even though they have timers that show how much time is left for the cycle to finish, the timers are often inconsistent and don’t match up with the progress of the washing programs. I feel that my laundry experience would be so much better if the laundry machines enabled me to: 1. know exactly how long it will take 2. by the time I get to the laundry room after my alarms go off, my laundry is actually finished and I don’t have to wait extra 10-15 minutes for  the machine to stop spinning. Something as routine as laundry should be intuitive and not become a guessing game.

I think the ideas mentioned in the book are all applicable and relevant to interactive media. Affordances and signifiers are important to make the interaction between the user and the system clear. For example, buttons should look clickable and links need to be presented in such a way to indicate that they can be clicked. The controls that allow the user to interact with the system need to logically map to their effects to provide an intuitive experience. When the user performs an action, they should get a feedback signal to notify that their actions were registered.

Week 4: Data Visualization

Concept

I was looking through the datasets linked in Professor Mathura’s repo when I came across the data for the World Happiness Report. It contained numerical data for various happiness indicators across countries around the world and I was immediately interested as I was looking to create some sort of a color visualization. Using this data, I decided to create a data visualization using color that shows where each country stands in terms of happiness, as well as in terms of the other indicators that impact happiness, including health, freedom and economy.

Process

I planned to create individual visualizations for each of the four focus indicators using different colors, and then make them transition into one another smoothly. While searching about ways to do color transitions and gradients, I came across the lerpColor() function that works similarly to the lerp() function, and allows to interpolate between two colors based on a parameter.

Initially, I drew equal size rectangles for each country. Even though I liked that the whole canvas was perfectly divided into uniform rectangles, it felt like it lacked coherent meaning. I thought it might make the visualization much more interesting if it offered a comparison of the indicators across not only countries, but also across continents. The original data file unfortunately did not have continent data, so I manually went through the CSV file to add a continent column entry for each country. Using this data, I divided the canvas vertically into 6 equal columns representing each continent (Asia, Europe, Africa, South America, North America, Oceania), and then further divided each column into smaller segments for each country. This way, the visualization offered a richer narrative – it illustrates how happiness indicators vary across the different regions in the world, and exposes potential trends linked to geography and economic factors.

After
Before

 

 

 

 

 

 

Brighter/lighter colors represent higher scores on the happiness level indicators and darker colors represent lower scores. This means that a country ranks high in happiness if it consistently appears bright throughout the visualization. Toggle feature is added to allow the sketch to also be viewed solely as an art piece, so if you want to see the countries represented by the boxes, you have to click the mouse.

Challenges

Implementing the transition between the visualizations was the biggest challenge of this project.

I created a helper function to get the color based on the score and the indicator, by mapping the score ranges for each indicator to RGB color ranges (0-255). Setting the RGB color values took surprisingly many trials and repeated references to RGB color theory to get the exact color gradient I wanted for each indicator.

// helper function to get the color based on score & indicator
function getColorForIndicator(score, indicator) {
  // higher the score, brighter/lighter the color
  // lower the score, darker the color (nearing 0)
  if (indicator === "Happiness") {
    // yellow <-> dark
    return color(map(score, 2, 8, 0, 255), map(score, 2, 8, 0, 255), 100);
  } else if (indicator === "Health") {
    // green <-> dark
    return color(100, map(score, 0, 1, 0, 255), 100);
  } else if (indicator === "Freedom") {
    // blue <-> dark
    return color(50, map(score, 0, 1, 50, 200), map(score, 0, 1, 50, 200));
  } else if (indicator === "Economy") {
    // magenta <-> dark
    return color(map(score, 0, 2, 40, 255), 100, map(score, 0, 2, 20, 255));
  }
  return color(200);
}

To achieve a smooth color transition, I used the lerp() and lerpColor() functions to get the colors corresponding to the indicator scores interpolated by the current progress of the transition.

// get the country's scores for the current and next indicator
let currScore = getScore(countries[j], currIndicator);
let nextScore = getScore(countries[j], nextIndicator);
// get the current and next score belend by interpolating by the transition progress
let blendedScore = lerp(currScore, nextScore, transitionProgress);

// get the colors for the current and next indicator based on blended score
let col = getColorForIndicator(blendedScore, currIndicator);
let nextCol = getColorForIndicator(blendedScore, nextIndicator);
// retrieve the final color by similarly interpolating by progress
// this ensures smooth color transition between the visualizations
let finalColor = lerpColor(col, nextCol, transitionProgress);

Reflections & Improvements for Future Work

I learned a lot while working on this week’s project, including new functions like lerpColor(), millis() and loadFont(). The process was not only technically challenging, but also required thinking more deeply about how to transform raw numbers into a compelling visual story. Moving forward, I hope to continue being intentional about the design and meaning of my projects.