Week 4: Who or What is Perlin?

Concept
I have always been fascinated by ASCII art and how simple characters can create complex images. But I wanted to take it a step further. What if we could make ASCII art that moves and changes color? That is where Perlin noise comes in. By using noise to determine both the characters and their colors, we create a living, breathing ASCII landscape that is very satisfying  to watch. The idea is simple: we divide the canvas into a grid of cells. For each cell, we use Perlin noise to choose an ASCII character and a color. As time passes, the noise changes, making our ASCII world shift and flow like a colorful river of text.

Code snippet
The heart of this project is how we map noise values to both ASCII characters and colors. Here is the snippet I’m most proud of:

const noiseVal = noise(x * 0.01, y * 0.01, frameCount * 0.01);
const charIndex = floor(map(noiseVal, 0, 1, 0, density.length));
const asciiChar = density.charAt(charIndex);
const hue = map(noiseVal, 0, 1, 0, 360);
fill(hue, 100, 100);

This bit of code is doing double duty. It’s using the same noise value to pick an ASCII character and determine its color. I was able to achieve a perfect harmony/balance between the texture of the characters and their hues.

Sketch

Reflection and ideas for future improvements
I’m pretty happy with how this turned out, but there’s always room for improvement. The main issue I’m facing is performance. If I try to add more features or increase the resolution, things start to get laggy real quick.

Looking ahead, I’ve got a few ideas how I can improve this sketch:
First, I want to optimize the code. Maybe I could use a buffer to draw the ASCII characters once and then just update the colors each frame. This would cut down on the text rendering overhead. I’m also thinking about making the cell size dynamic. Imagine if the cells got smaller when you moved your mouse closer, and smaller if mouse far away. That will add more interactivity and will help to make this sketch even more dynamic. Lastly, I’d love to experiment with different noise algorithms. Perlin noise is great, but there are so many other types of noise out there. Each one could give a unique character to the ASCII landscape.

Reading Reflection – Week 4

That light switch placement in the dorm bathroom really gets on my nerves! Every time I go in there, I end up smacking the mirror instead of hitting the lights. It’s such a small thing, but it makes a big difference in how frustrating it is to use the bathroom. The switch is so close to the mirror already – why not just move it a tiny bit closer so it’s the first thing your hand hits? This seems like a perfect example of Norman’s point about the importance of mapping and natural positioning of controls. A little more thought about the user’s actions could have made this design so much more intuitive and less annoying.

The shades in the NYU New York dorms are another headache. These shades consist of multiple connected panels and have a seemingly simple string mechanism for opening and closing. However, the actual operation is far from intuitive or user-friendly. You have to pull the string sideways and then up or down, but it doesn’t always work right. Sometimes you end up doing a little dance just to open or close them. What’s more, if you try to open or close the shades too quickly, the individual panels start rotating as crazy instead of moving up or down. Why make it so complicated?

Norman’s principles could definitely be applied to interactive media design. In my other class, we’re struggling with how to show users which objects on the screen are clickable. Should we add a glow effect? An outline? Following Norman’s ideas about signifiers, we need clear visual cues to indicate interactivity. But we also don’t want to clutter up the artwork. It’s a tricky balance.

I like how Norman emphasizes the importance of feedback. With digital interfaces, it’s crucial to give users immediate confirmation that their action worked. Even a slight delay can be confusing. In our comics, we’re trying to add subtle animations or sound effects when objects are clicked. The trick is making the feedback noticeable but not distracting from the story.

Overall, the reading made me more aware of design choices all around me. I’m noticing signifiers and affordances everywhere now! It’s a good reminder that even small details can have a big impact on usability. As we create digital experiences, we need to really think through how users will interact and understand the interface. Good design isn’t just about looks – it’s about creating an intuitive experience.

Week 4 – The Design of Everyday Things : Chapter One

This reading made me realize just how imperative the ‘human’ aspect of things comes into play when it comes to interactive design. As I reflected on the chapter, I found myself connecting the concepts to my own experiences with technology and design.

The author’s emphasis on discoverability and understanding resonated with me deeply. I’ve often purchased products and I couldn’t figure out what to actually do or how it’s done without reading the instructions manual first. It made me appreciate the importance of intuitive design that communicates its functionality without requiring extensive instructions or manuals.

I found the writer’s descriptions of engineers and their thought process during production to be quite intriguing, as I had not thought of things that way before. The chapter delves into the failings of machines due to the way certain engineers think – logically. In terms of failings, I am referring to the I learned that they often assume that the audience of their product or project will think like them or read the instruction manual. This is where they fall short. I came to understand that they struggle to grasp the idea that we all think so uniquely, and therefore, designs need to be adapted into a language that can be universal yet enjoyable enough to use. Which made me understand that this failing bleeds into the actual production of the gadgets, hence why it is not received the way they hand in mind.

As I came to learn of these concepts, I found myself looking at the designed world around me with new eyes. I started noticing how certain designs naturally suggest their use, while others require explicit instructions. This awareness has changed how I interact with and evaluate the design of everyday objects and digital interfaces. For example, the doors in D2 are just abysmal. Although they have a sensor, where if you wave at it then it opens the doors – it doesn’t always work, and I actually have to manually open the door like a jester. Although I am unsure if this fault is due to some technical thing, or a lapse in judgment, it is a problem that has existed since freshman year.

This new perspective isn’t just changing how I look at things; it’s changing how I create them too. Whether I’m putting together a presentation or organizing my workspace, I’m now thinking about how to make things clear and intuitive for others. It’s like I’ve been given a new set of glasses to see the world of design through.

Reading Reflection – Week 4

Don Norman’s arguments on affordance, discoverability, and communication offer a compelling and analytical perspective on how interactive objects should be designed. Unlike readings such as “The Art of Interactive Design,” which tend to focus more on the finished product, Norman delves into the intricacies of poorly designed objects, analyzing the root causes and suggesting improvements. His credibility as an engineer strengthens his arguments, as he understands both the technical and human aspects of design. This raises an interesting question: if a professional fictional writer presented the same solutions, would they carry the same weight? This reflects the importance of practical experience in validating design solutions. I relate to Norman’s critique of poorly designed objects; for example, VR/AR goggles often suffer from an imbalance that makes them front-heavy. Redesigning them to be lighter would significantly enhance the immersive experience. This demonstrates how affordance and ergonomics directly impact the user experience.

In discussing the “Paradox of Technology,” Norman argues that as technology becomes more functional, it becomes harder to learn. I agree but question the extent to which this should be solely a designer’s responsibility. While Norman emphasizes that engineers should approach design from a non-logical, human perspective, this principle should also apply to the Paradox of Technology. Designers face limitations in simplifying complex functionality, suggesting that the solution lies in ensuring engineers design with the average human in mind, not just other engineers. This point is particularly relevant when considering advanced technologies like Artificial Super Intelligence (ASI). While intended to serve humanity, ASI must be designed to accommodate the everyday user, not just the logical thinker, ensuring it remains accessible and understandable.

Feedback, another of Norman’s key points, is essential in design, as humans are naturally impatient with technology. However, the expectation of rapid feedback, especially in digital interactions, can have unintended psychological consequences, such as increased anxiety and impatience in the extreme usage of  cellphones among young people. While Norman rightly argues that feedback should be provided correctly and appropriately, I contend that excessively fast responses can condition users to expect instant gratification, potentially harming mental well-being. Designers and engineers must be mindful of balancing efficiency with psychological impact, aligning with the principles of Human-Centered Design (HCD).  Norman’s insights on affordance, feedback, and human-centered design are not just applicable but essential in shaping interactive media. Especially in contexts like end-of-semester showcases, where media installations encounter a diverse audience, applying Norman’s principles ensures intuitive and meaningful human-machine interactions.

Week 4: Don Norman

I found this week’s reading really assuming because I could relate to a lot of the examples he gave. Specifically referencing the washing machine and glass door, I definitely have been in situations where the design of a product is aesthetically pleasing and minimalistic, but the actual usability of the product is misleading and frustrating to work with. The example I thought of was the integration of smart screens into the dashboard of cars. I grew up learning how to dial radio stations, DVDs, and cassette tapes, so when my family were able to purchase new cars, I was dumbfounded by the flat screen instead of the variety of knob and mechanism. While it was easy to figure out, I somehow found it a nuisance at times to have to tap or scroll through the interface. I also find it hypocritical because it can be super distracting and cause the driver attention to divert from the road.

I also could relate to the thinking process of engineers and having a set logical process. I agree with the author that it feel like a lot of products are designed for machines and are not user friendly. That is why clear communication, diagrams/ mappings, and symbols are so important because builds the relationship between the individual and the people that worked hard to create the product. In my own work, I find it hard to divert from my logic and fall into the pit of false confidence that led to fallacy. As such, I find great importance in the open projects and humanities because through these studies and experience can there be a bridge between hard logic and real case situations.

I hope in my interactive projects I don’t fall into the logical fallacies as an engineer. Especially because I have been trained through my college career to think logically and work efficiently, there will be times where something can less efficient but easily understood and easier to implement into my project. In addition, I hope to include aesthetic, yet mindful, signifiers in my project, so user are able to interactive and have fun with my projects rather than taking time to determine how my projects work.

Week 4: Data Visualization of the Paris 2024 Olympic Medals

SHORT DESCRIPTION: 

Our assignment for the fourth week is data visualization along with typography.  The data I chosen for this assignment is taken from the popular database website Kaggle. To visualize the data, I used a collection of different sized circles that was determined by the number of medals each of the 91 countries had won.

INITAL APPOACH 

Initially, I had a lot of trouble finding the data that I wanted to use for my assignment because the dataset wasn’t organize, lacked information, or was too complex to be used. In addition, I also didn’t know what I wanted to do, so I felt super unconfident and uncreative in my work. I had an early approach to display the decomposition of RGB values of a randomly chosen color, but the end product left me extremely unexcited and disappointed. Below was my first attempt for the data visualization of color.

First Sketch of Data Visualization: Click the screen to change colors

Through the frustration of this project, I decided to give data searching another chance and decided to use Kaggle since I was a familiar with the site and could filter through different categorizes of datasets. Eventually I ended up finding the Paris Olympic medal dataset and was interested in the breakdown and ranking of the countries that participated.

CODING PROCESS

To not let my RGB project go to waste, I decided to incorporate my main goals and what I learned from the RGB project into the visualization for the distribution of Olympic medals: generating a random color, changing the canvas when the mouse is clicked and having text on screen.

function getRandomColorAndRatio(){
  colorRatio = [random(0,255), random(0,255), random(0,255)];
  updateCircleColor();
}

function updateCircleColor(){
  circleColor = color(colorRatio[0], colorRatio[1], colorRatio[2]); 
}

function mousePressed() {
  getRandomColorAndRatio();
}

Also to note, I decided to remove the draw() function in my code because the name was deceptive to me, and I didn’t need my screen to constant refresh, so it was easier to declare my own function and call them in setup().

THE HARDSHIPS

Honestly, the hardest part this week was overcoming the uncreative block and finding a dataset I was interested to work with. For my code, I didn’t have too much trouble importing the data or implementing my idea because I already had a basis of what I wanted my project to look like and the interactive feature to because of my initial approach. I did have trouble figuring out the math to corelated the sizes of the circles with the number of medals awarded because I dumbly decided to work on the whole dataset instead of the first ten. So my future advice for people, is to work within a small range of your data first before implementing the code across the entire dataset. Otherwise, you’ll find that rank 84 in the world has the biggest circle and rank 1 has the smallest circle because you didn’t check your math.

Math for Circle Sizes
// circles are has a scale factor of 10% based on the ratio of a countries medals awarded with the total medals awarded in relation to the width 
  let size = 10 * (countryTotal/ totalMedalsAwarded); 
  let circleArea = (width) * size

THE UPSIDES

For my final product, I ended up with two concepts of my dataset. The first version is sort of a generative art that will randomly place the circles of the countries’ medal with a random color somewhere on the screen. Though, it sounds chaotic, I feel the circular shapes and (if lucky) the pastel color gives a bubbly and warm feeling to the viewer. It was also here that I noticed my mistake where rank 84 was much larger than the top ten because I had labeled each circle and didn’t see any of the top 10 countries listed. Through in the end, in the code I commented out the countries’ names and rank because I felt it added too much noise and unnecessary complexity into the composition.

Data Visualization 1: Click to change composition

For my second concept, I decided to have a structed data visualization. To do so, I simply implemented a double for-loop to create a 10 by 10 matrix and map each of the circle centers to their respective coordinates. Once the circles were maps, I uncommented the text label for the countries’ rank and medals, and I feel like it tired the project together. However, sometimes the randomization of the colors may cause the label, and sometimes the circle, to blend into the background and making it invisible. I think for future plans, it would be nice to figure out a solution to keep randomization but not have it blend with the background.

For-loop Code and If-Else Statement to check if there are still data to be processed
// organize the datas in a set 10 x 10 coordinate fashion 
function xyPos(){
  for (let yPos = 120; yPos < height; yPos+= 120){
    for (let xPos = 120; xPos < width; xPos += 120){
      // there will be extra spaces in the end so it is necessary to always check that there are still rows/ data left in the file to be processed
      if (fileRow < strings.length){
        lineFile = split(strings[fileRow], ",")
        runFile(xPos, yPos, lineFile);
        fileRow += 1
      }
    }
  }
}

I had to increase the canvas size to 1200 x 1200 in order for the labels to not overlap with each other, so here is the link to project, for anyone wanting to see the code.

Data Visualization 2: Visualizations of the number of medals award to each 91 participating countries

FINAL REMARKS 

While time consuming, compared to my first project, I am extremely happy with my final result. I really liked processing the data and understanding which of the data was useful to me and would be the most impactful to the viewer. I had also learned that the Olympics ranks by gold medals, not total medals. I was really confused why Brazil was ranked 8th when it had more medals than those that ranked higher than them. Overall, I had a lot of fun using the data and then seeing the work tied together.

Also, in my code, I have a breakdown of each countries’ medals by gold, silver, and bronze. I didn’t include it in concept 2 photo because I wanted to keep it straightforward and simple.

Breakdown of Countries' Medals
console.log('Team ' + countryCode + ' won ' + countryTotal + ' medals with ' + countryGold +' gold medals, ' + countrySilver + ' silver medals, and ' + countryBronze + ' bronze medals!')

Reading Response: What is interactivity?

In Chapter 1 of The Art of Interactive Design, Chris Crawford describes interactivity as a continuous loop with three steps: listening, thinking, and speaking. A system that truly engages users is one that doesn’t just react—it listens, processes input and responds differently based on type of the input.

The strongest interactive systems create a kind of “conversation” between the user and the program. It’s not just about clicking a button and getting a fixed response, but more about the system understanding the user’s input and adapting to it. For example, a simple calculator provides weak interaction because it only gives pre-programmed results. On the other hand, a video game provides a much richer experience by reacting to the player’s choices and strategies in real time.

To make my p5.js sketches more interactive, I could start by making them more adaptive to user behavior. My most important takeaway from the chapter is separating ‘participation’ as compared to ‘interactivity’. In my old sketches I have used ‘participation’ where no matter how the user approaches the system, the system outputs from a list of fixed effects. Moving to making my systems interactive would mean that any system I design would not just respond the same to each output but contain an algorithm that accurately digests the variability of inputs whether that is length, type of interaction, active or passive interaction and then react accordingly.

Instead of just reacting with basic changes like color shifts when the mouse is clicked, I hope to now build in more complex systems of interactivity in my sketches. For instance, if the user holds the mouse down longer, maybe the system reacts in a different manner. Or, I could design it so that the faster someone moves their mouse, the more dramatic the visual change. The more, the user thinks that not just their input matters, but their type of input matters as well, the more I will know I did a good job creating an interactive piece of media. By adding this kind of real-time feedback, the sketch can feel more dynamic and have deeper user involvement, a system of ‘inputs’, ‘processing’ and ‘outputs’, that Crawford would consider ‘interactive’.

Assignment 3 Generative AI

Concept:

For this project, I viewed a lot of generative artwork created using P5.JS but the idea for my particular design did not click until I was watching a 3Blue1Brown, a math youtuber’s video on Dirichlet’s theorem. The theorem tries to explain how prime numbers create certain spirals, and the randomness of primes means you cannot accurately predict the pattern. Thus the idea of working with random spiral shapes was born. I decided to create a 6×6 grid of spirals, each generating a gradient of color for unique spiral shapes. I wanted to add a level of interactivity which I did by making the spirals spin when the mouse hovers over them (mimicking an electron’s spin and change of behavior when ‘observed’). Furthermore I made it so that any mouse click on a particular spiral, changed the direction of the spin.

Code I am particularly proud of:

A part of the code I am particularly proud of is the draw_ background() function that created a smooth gradient for the background. In plain white the sketch looked dull and uninteresting but I believe the new gradient background has allowed each spiral color to ‘pop’.

//this function draws the background gradient going from dark blue to pink
function draw_background() {
  let c1 = color(60, 120, 200);  // navy blue shade
  let c2 = color(255, 182, 193);  // light pink shade
  //loop through each pixel row to create the gradient
  for (let y = 0; y <= height; y++) {
    let gradient_color = lerpColor(c1, c2, y / height);  //finds an in-between color for each pixel depending on height in the sketch
    stroke(gradient_color);  // color for the current row
    line(0, y, width, y);   // draws a line of that color
  }
}

 

Final Sketch:

Reflections:

I am happy with the results of this assignment. It is visually interesting, pleasing to look at and interactive but I do believe there is room for improvement. Another level of interactivity I hope to add is the spirals changing colors when hovered over or maybe when clicked upon. Another idea I had was the spirals morphing into different shapes based on user movement across the canvas. Overall I believe this assignment sets up a good basis for further development of this seemingly simple idea.

Week 3 Assignment: Generative Artwork using OOP: Screensavers and Physics

Concept and Inspiration

For this assignment, I drew inspiration from the the era of late 1990s and early 2000s. At that time, Windows 98 and Windows XP had some unique screensaveers which I still remember and which evokes a sense of nostalgia in me. These screensavers (link) often featured geometric shapes and repetitive, hypnotic patterns, such as oscillating lines, spirals, and pendulum-like movements.

Fig: Some of the Windows Screensavers

For this, I used robotic arms and “Inverse Kinematics” as my inspiration. From my point of view, I saw this as the perfect oppertunity to blend computational techniques into this visual style. The robot arms represent the pendulums of the past, but with a unique twist. Instead of being merely simple lines, these arms demonstrate the principles of Object-Oriented Programming (OOP), where each pendulum is treated as an independent object, following specific behaviors such as oscillation and length growth. Moreover, inverse kinematics allows each arm to dynamically respond to changing positions, mimicking the flexibility and precision of robotic motion. The result is a digital artwork that blends the nostalgia of retro visuals with the sophistication of modern computational design.

Code Explanation

“Arm” class

This class features a constructor which initializes each pendulum’s amplitude, angle, angle velocity, and length growth. It also has the “update()” function to update the pendulum’s length (amplitude) and oscillation (angle). Using the “display()” function, it calculates the current position of the pendulum and draws a line from the previous position.

// Defining the Arm class
class Arm {
  constructor(amplitude, angle, angleVel, lengthGrowth) {
    this.amplitude = amplitude;      // Initial length of the arm
    this.angle = angle;              // Starting angle
    this.angleVel = angleVel;        // Angular velocity
    this.lengthGrowth = lengthGrowth; // How fast the arm grows in length
  }

  // Method to update the arm's properties (growth and oscillation)
  update() {
    this.amplitude += this.lengthGrowth; // Increase length over time
    this.angle += this.angleVel;         // Update angle for oscillation
  }

  // Method to display the arm
  display(prevX, prevY) {
    let x = sin(this.angle) * this.amplitude;  // Calculate x position
    let y = cos(this.angle) * this.amplitude;  // Calculate y position
    line(prevX, prevY, x, y); // Draw line from previous position to current
    return { x, y };          // Return current x, y for the next arm
  }
}
“setup()” function

The “setup()” function initializes the canvas size and prepares the environment. It disables fills for the shapes and sets default stroke properties. It randomizes the number of pendulum arms (num_arms) and the other arm’s properties, with each arm receiving random values for amplitude, angular velocity, and growth rate. The arms are stored in an array, each represented as an object with properties for oscillation and growth.

function setup() {
  createCanvas(800, 800);
  noFill();
  stroke(255); // Initial stroke color
  strokeWeight(1); // Initial stroke weight

  // Randomize the number of arms between 2 and 10
  num_arms = int(random(2, 10));

  // Initialize the Arm objects with random values
  for (let i = 0; i < num_arms; i++) {
    let amplitude = random(70, 150);
    let angleVel = random(0.01, 0.05);
    let lengthGrowth = random(0.1, 0.5);

    // Create new Arm and push to the arms array
    arms.push(new Arm(amplitude, 0, angleVel, lengthGrowth));
  }

  // Initially set the center to an off-canvas position
  centerX = -1000;
  centerY = -1000;
}
“draw()” function

This function creates a semi-transparent background overlay to maintain the fading trails without fully erasing the canvas. The “rect()” draws a slightly transparent rectangle over the entire canvas, producing the trailing effect. The “translate()” function shifts the origin of the canvas to the clicked point (centerX, centerY), which acts as the center of the pendulum system. A loop iterates over each arm, calculating its new position based on its current angle and amplitude using “Inverse Kinematics.” The arms are drawn as lines connecting from one pendulum to the next, simulating the robot arm movement whos length increases with time.

// Draw Function
function draw() {
  if (hasStarted) {
    fill(0, 10); // Semi-transparent background to maintain trails
    rect(0, 0, width, height);

    // Set the center of the arms to the clicked position
    translate(centerX, centerY);

    let prevX = 0;
    let prevY = 0;

    // Loop through each arm and update and display them
    for (let i = 0; i < arms.length; i++) {
      let arm = arms[i];

      // Update arm properties
      arm.update();

      // Display the arm and update the previous position for the next one
      let newPos = arm.display(prevX, prevY);
      prevX = newPos.x;
      prevY = newPos.y;
    }
  }
}
“mousePressedI()” function

The “mousePressed()” function updates the center of the pendulum system to wherever the user clicks on the canvas (mouseX, mouseY). This triggers the pendulum animation by setting “hasStarted” to true. Upon clicking, it randomizes the stroke color, weight, and number of arms, creating variety and making each user interaction unique. It also reinitializes the pendulum arms with new random values, ensuring a different pattern is generated with every click.

// This function will run when the mouse is pressed
function mousePressed() {
  // Set the new center of the arm system to the clicked location
  centerX = mouseX;
  centerY = mouseY;
  hasStarted = true;

  // Randomize background and stroke properties
  stroke(random(0, 255), random(0, 255), random(0, 255));
  strokeWeight(random(1, 10));

  // Randomize the number of arms between 2 and 6
  num_Arms = int(random(2, 6));

  // Reinitialize the arms array
  arms = [];
  for (let i = 0; i < num_Arms; i++) {
    let amplitude = random(80, 150);
    let angleVel = random(0.01, 0.05);
    let lengthGrowth = random(0.1, 0.5);

    // Create new Arm objects with random values
    arms.push(new Arm(amplitude, 0, angleVel, lengthGrowth));
  }
}

Sketch

Further Improvements which can be made

Smoother Transitions: Currently, the background might change too quickly when clicking. Adding a smooth transition effect between pendulum sets can make the animation more fluid and visually appealing.

Scaling upto 3D space: I had originally though of introducing a responsive 3D canvas using “WEBGL” mode in p5.js, but that was making the idea of user interaction a little complex, so I had to drop that for now.

Damping: Currently, the simulation runs the pendulums until another person clicks it. Introducing damping can be another way to introduce realism to it.

Collission: Various arms when coming in contact with each other change their path/length can be another aspect which can be looked to.

Reflection

This project modernizes the retro screensaver aesthetic using modern programming techniques such as OOP and inverse kinematics, combined with user interactivity. The code is modular, making it easy to add new features or improvements, and the possibilities for further customization and expansion are vast.

Assignment 03: River Flow

Concept: I wanted to create a view of my home country, Bangladesh, which is filled with. hundreds of rivers, and with time the rivers are shrinking. So, I used this assignment to create something that represents the rivers from Bangladesh in a sunset light.

Code:

I’m particularly proud of the FluidSimulation and FlowingMotif parts of my code because they exemplify a thoughtful blend of creativity and technical skill. The FluidSimulation class captures the essence of fluid dynamics with its elegant use of vectors and noise functions to simulate realistic flow patterns. The display() method, with its precise rendering of flow lines, transforms abstract concepts into a visually engaging experience. This method doesn’t just draw; it visually narrates the movement of fluid in a way that is both aesthetically pleasing and scientifically intriguing.

Similarly, the getForceAt() method provides a crucial interface for interaction with the fluid simulation, enabling dynamic elements like particles to seamlessly integrate with the simulated environment. This capability is pivotal in creating a responsive and immersive visual experience.

The FlowingMotif class adds a touch of artistic flair to the simulation. By using dynamic shapes that evolve over time, it introduces an additional layer of visual interest and complexity. The carefully crafted constructor ensures that each motif is unique, with properties that allow for smooth and captivating motion. The motifs enhance the overall aesthetic of the simulation, bringing a sense of life and movement to the scene.

display() {
    noFill();
    strokeWeight(1);
    for (let y = 0; y < this.rows; y++) {
      for (let x = 0; x < this.cols; x++) {
        let v = this.field[y][x];
        let px = x * this.gridSize;
        let py = y * this.gridSize;
        stroke(255, 100);
        line(px, py, px + v.x * this.gridSize, py + v.y * this.gridSize);
      }
    }
  }

  getForceAt(x, y) {
    let col = floor(x / this.gridSize);
    let row = floor(y / this.gridSize);
    col = constrain(col, 0, this.cols - 1);
    row = constrain(row, 0, this.rows - 1);
    return this.field[row][col].copy();
  }
}

// FlowingMotif class definition
class FlowingMotif {
  constructor(x, y, size) {
    this.x = x;
    this.y = y;
    this.size = size;
    this.angleOffset = random(TWO_PI);
    this.frequency = random(0.01, 0.05);
  }

P5.js Sketch: