Week 04: Data Visualisation

Inspiration:

As someone who believes in the transformative power of data visualization, I see it as an essential tool for making complex information accessible and understandable. Visualizing data allows us to grasp patterns, trends, and insights that would otherwise remain hidden in raw numbers. In the context of healthcare, especially with something as critical as blood pressure, effective visualization can significantly impact how we interpret and respond to health data.

One of my inspirations for this project comes from the recent work of renowned data artist, Giorgia Lupi, a renowned information designer known for her innovative approach to data visualization. I loved how she turned data visualisation in an artistic human connector engaging us with tales, emotions, informations through data.

Concept: The blood pressure dataset, originally from Stata, serves as a crucial resource for understanding the impacts of various factors on blood pressure among different demographic groups. I have taken the dataset from the Github Open Source For Data Science account. The idea came into my mind after remembering how blood pressure runs into families and sometimes we don’t care about it. Though the dataset is pretty small and not too complex, I enjoyed creating a visualisation for the first time.

Codes which I’m proud of:

I had trouble with managing datasets so I used the console.log technique that we learned in the class to check if  the CSV data loads correctly before any processing occurs or not. This is crucial because if the data is not available, the subsequent calculations will fail. I created two charts as the dataset had more columns, so I made two error handling for each chart.

    function preload() {
    data = loadTable('blood_pressure.csv', 'csv', 'header');
}

else {
        console.error("Data not loaded properly"); // Error handling if data isn't loaded
    }

I couldn’t find it possible nto fit the data in one chart. To make it easy to understand I created two charts which are connected based on the parameters. Here is the snippet for dynamically creating two separate charts for age and sex while keeping the layout clear. It required careful organization of data and chart settings.

function createAgeGroupChart() {
    const ageGroups = {}; // Object to hold blood pressure data by age group
    
    // Ensure the data is loaded
    if (data) {
        // Iterate through each row of data
        data.rows.forEach(row => {
            const agegrp = row.getString('agegrp'); // Get age group
            const bp_before = row.getNum('bp_before'); // Get blood pressure before
            const bp_after = row.getNum('bp_after'); // Get blood pressure after

            // Initialize arrays for this age group if it doesn't exist
            if (!ageGroups[agegrp]) {
                ageGroups[agegrp] = { before: [], after: [] };
            }
            // Add blood pressure data to the respective arrays
            ageGroups[agegrp].before.push(bp_before);
            ageGroups[agegrp].after.push(bp_after);
        });

        // Extract labels and average values for the chart
        const labels = Object.keys(ageGroups);
        const avgBefore = labels.map(label => avg(ageGroups[label].before)); // Average BP before
        const avgAfter = labels.map(label => avg(ageGroups[label].after)); // Average BP after

        // Get context for the first chart
        const ctx1 = document.getElementById('chart1').getContext('2d');
        chart1 = new Chart(ctx1, {
            type: 'bar', // Bar chart type
            data: {
                labels: labels, // X-axis labels (age groups)
                datasets: [
                    {
                        label: 'BP Before', // Dataset label
                        data: avgBefore, // Data for BP before
                        backgroundColor: 'rgba(75, 192, 192, 0.6)', // Fill color
                        borderColor: 'rgba(75, 192, 192, 1)', // Border color
                        borderWidth: 1 // Border width
                    },
                    {
                        label: 'BP After', // Dataset label
                        data: avgAfter, // Data for BP after
                        backgroundColor: 'rgba(153, 102, 255, 0.6)', // Fill color
                        borderColor: 'rgba(153, 102, 255, 1)', // Border color
                        borderWidth: 1 // Border width
                    }
                ]

Similarly, I did another chart for the average counts according to sex.

function avg(array) {
    return array.reduce((a, b) => a + b, 0) / array.length;
}

Lastly, I tried to ensure that the charts adjust to different screen sizes while keeping everything readable and visually appealing. I used this part of code to implement it. I used Inspiration from Bar_Chart_in_P5.js video.

options: {
    responsive: true,
    scales: {
        y: { beginAtZero: true, title: { display: true, text: 'Blood Pressure' } },
        x: { title: { display: true, text: 'Age Group' } }
    }
}

 

p5.js Sketch:

Challenges and further improvements: 

The main challenge was to find a proper dataset. I tried to use different data sets from Google Dataset Search, Kaggle, Data.gov etc but they were not showing into charts, sometimes error was coming or the dataset was too big to use for this project. I took help from chatgpt to give me resources for fine datatsets but it didn’t give me any dataset or link, suggested me to check on Github so I used the idea and searched github.

 

 

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:

Week 03: Reading Reflection

In the first chapter of Chris Crawford’s The Art of Interactive Design, I found myself engaged with the foundational ideas presented. From Crawford’s exploration into the nature of interaction as the core of design, one of the key takeaways for me was Crawford’s emphasis on interaction being the core of design. Before reading this, I often saw interactivity as an added feature rather than the fundamental aspect of the user experience. It was like a light bulb went off when he explained that interaction isn’t just about adding clickable elements but about making the whole design process revolve around the user’s actions and reactions. This perspective has made me question my approach to design—am I truly focusing on how users interact with my work, or am I just ticking off boxes?

I agree with him on the definition of interactivity. If I am creating an interactive device/art/product, I should always remember how four parameters, aka listening, speaking, thinking, and responding, work on my idea. I really enjoyed how he explained interactivity with examples, making it easier to even teach my 7-year-old brother about interactivity in technology.

Lastly, on the notion of traditional entertainment like movies being non-interactive, I think that might get shifted with emerging technologies. Digital media, including interactive films, virtual reality experiences, and even interactive web-based stories, demonstrate that interactivity can be a fundamental aspect of various media forms. The rise of these new media formats challenges the traditional notion that interactivity is limited to games or other explicitly interactive experiences.

Assignment 02: Flower Artistry

Concept:  My interactive media class starts just at the end of the day and when I was sitting in the class, brainstorming what I do with loops, I randomly started to draw some simple flowers on P5.js to relieve some stress from having a long day. Then the idea came to my head that having a flower bouquet can make me feel so good, if I don’t have one, then why not create a virtual one? So, here it is!

Proud Moment: I initially sketched some basic flower-like shapes using circles in P5.js, but they didn’t turn out as expected. To achieve the variety and complexity I wanted, I decided to use loops directly as we discussed in class, as manually coding each flower would have been impractical. My main challenge was ensuring that all the flowers had distinct appearances. To address this, I designed each flower type, reusing a base code and adjusting parameters to create different flower variations.

Code Snippet:

function drawDaisy(x, y, size, rotation) {
  let petalCount = 9;
  let petalLength = size;
  let petalWidth = size / 3;
  
  stroke(0);
  fill('#D9E4E6');
  push();
  translate(x, y);
  rotate(rotation);
  for (let i = 0; i < petalCount; i++) {
    push();
    rotate(TWO_PI / petalCount * i);
    ellipse(0, -size / 2, petalWidth, petalLength);
    pop();
  }
  pop();
  
  fill('#F2F2F2');
  noStroke();
  ellipse(x, y, size / 2);
}


function drawTulip(x, y, size, rotation) {
  let petalCount = 6;
  let petalWidth = size / 2;
  
  stroke(0);
  fill('#AEB7FE');
  push();
  translate(x, y);
  rotate(rotation);
  for (let i = 0; i < petalCount; i++) {
    push();
    rotate(TWO_PI / petalCount * i);
    ellipse(0, -size / 2, petalWidth, size);
    pop();
  }
  pop();
  
  fill('#EDEAE6');
  noStroke();
  ellipse(x, y, size / 3);
}

It made the design part of my work easier, but I initially struggled with making the flowers continuously generate as the mouse cursor moves and ensuring they didn’t cluster in one spot. To address this, I turned to various YouTube videos on Mouse Trails, such as the p5.js | Mouse Trail (Quick Code) tutorial. However, I needed to adapt some parameters and functions to fit my specific goals, which differed slightly from those demonstrated in the videos. This aspect of the project was particularly challenging as I had to first grasp the underlying logic before implementing the necessary code to achieve the desired functionality.

In mouseMoved() Function, I’m implementing the logic to generate new flowers as the mouse moves across the canvas. Here’s a breakdown:

function mouseMoved() {
  let currentTime = millis();
  if (currentTime - lastGenerationTime > flowerGenerationInterval) {
    lastGenerationTime = currentTime;
    generateFlowers(mouseX, mouseY);
  }
}

function updateFlower(flower) {
  let dx = mouseX - flower.x;
  let dy = mouseY - flower.y;
  let distance = dist(flower.x, flower.y, mouseX, mouseY);
  
  if (distance > 1) {
    flower.x += (dx / distance) * followSpeed;
    flower.y += (dy / distance) * followSpeed;
  }
  
  flower.x += random(-0.5, 0.5);
  flower.y += random(-0.5, 0.5);
}
I use millis() to get the current time in milliseconds since the sketch started. By comparing it to lastGenerationTime, I ensure that new flowers are only generated after a specified interval (flowerGenerationInterval). This prevents an overwhelming number of flowers from being created all at once.

 

When the time condition is met, I update lastGenerationTime and call generateFlowers() with the current mouse position (mouseX, mouseY). This function creates flowers around the mouse cursor, adding to the visual interest as I move the mouse.

Then, I calculate the distance between the flower and the mouse cursor using dx and dy. If the distance is greater than 1 pixel, the flower moves towards the mouse position. The followSpeed controls how quickly the flower moves. By dividing dx and dy by the distance, I ensure that the flower moves in the correct direction and scales its speed relative to the distance. While there might be other efficient ways to implement this, I chose this manual approach by carefully considering the underlying logic.

After watching the video “Eyeo2012 – Casey Reas,” I decided to introduce some randomness into the flower movement. I added the last two lines of code to make the movement more natural and less mechanical by including a small random offset to the flower’s position. This randomness results in a more organic and unpredictable motion, enhancing the overall visual appeal.

However, I was confused about which version to use as my final submission because each offers a unique vibe that I find appealing. To showcase the contrast, I am also including my first draft as a media file. This allows for a comparison between the two iterations, each with its distinct style and feel.

First Draw: 

Floral Artistry First Draft

 

Reflection and Ideas for Future Work or Improvements:

By using loops and arrays, I was able to efficiently generate and manage an infinite number of flowers, ensuring that the animation remained dynamic and engaging. The code successfully used these loops to update the positions of the flowers and arrays to store and retrieve flower properties, which was crucial for managing the large number of elements and their interactions. Here is what I think can be done in the future:

Performance Optimization: I encountered performance issues when increasing the number of flowers. For instance, trying to use infinitely many flowers caused the webpage to lag, highlighting the need for optimization to handle more elements smoothly. Addressing this performance challenge would help in making the animation more fluid and responsive.

Color Combinations: The current color scheme for the flowers and background could be improved. Some color combinations might not be as visually appealing or harmonious as they could be. Experimenting with different palettes could enhance the overall aesthetic and make the animation more striking.

User Interface and Interactivity: I initially experimented with aligning flowers based on input words, but this approach did not work as intended. Consequently, I simplified the project to focus solely on basic typography and the interactive aspect of the flowers following the mouse cursor. Future iterations could benefit from refining this concept or incorporating new interactive features to give users more control.

Conclusion: Despite these areas for improvement, I am pleased with the overall vibe of the project. The use of rotation to animate the flowers adds a dynamic touch, enhancing the visual interest of the animation. I enjoy the lively and engaging feel it provides and look forward to further refining and expanding upon this project in the future.

Week 02: Reading Response

As someone deeply interested in art and technology, I found Casey Reas’s works incredibly inspiring. His integration of algorithms, geometry, and art in design, interactive installations, and games has profoundly shifted my perspective on algorithms. Reas’s emphasis on the importance of randomness and chance operations in sparking imagination and pattern recognition truly resonated with me. Previously, algorithms seemed like rigid, rule-bound constructs, but Casey Reas demonstrated their creative potential through his projects. I also appreciated the historical context he provided on the juxtaposition of order and chaos in art and culture. He discussed how various artists, such as Jean Arp, Marcel Duchamp, John Cage, and Ellsworth Kelly, have employed chance and randomness in their work, showing how these concepts have evolved.

Reas’s ideas about randomness and chance reminded me of a photography project I’ve been working on, capturing different flowers from the NYUAD campus. I realized I could introduce more randomness into the process by experimenting with various camera settings and unconventional techniques. This approach could lead to more unexpected and intriguing results while maintaining cohesion and structure. I was also intrigued to learn about how military research into randomness has evolved into generative art. For example, his 10 PRINT algorithm, which generates unique and intricate patterns, fascinated me by showing how a simple algorithm can produce complex and beautiful results based on randomness. His work has motivated me to delve deeper into creative coding and explore the vast intersection of art and technology.

In general, a balance between randomness and control can be achieved by introducing elements of chance and unpredictability into a structured framework. This can help to create a sense of tension and surprise, while still maintaining a sense of coherence and direction. Casey’s use of the 10 PRINT algorithm to generate unique and intricate patterns is a great example of how a balance between randomness and control can be achieved. The algorithm provides a structured framework for generating patterns, while the random elements introduce a degree of unpredictability and surprise.

Self Portrait: Assignment_01

Concept: The concept comes from my favourite childhood cartoon Masha and Bear. I always loved how the bear’s room looked so cozy and peaceful in the cartoon. As I grew older, I noticed my younger brothers acting mischievously, much like Masha. In response, I found myself playing a protective role similar to the Bear’s in the series. So, I choose the bear as my concept for this assignment and tried to create a stylized portrait of the bear using basic 2D shapes in P5.js. While the result may not be an exact replica of the Bear, I tried to capture the essence of his character as seen in cartoon.

Highlight Code: One part of the code that I’m particularly proud of is the drawing of the bookshelf. It’s my first time working with Java and doing a full sketch on P5.js, so I had to go through quite a few tutorials from this channel to understand basics of the drawings. I tried to code the bookself using loops technique from this video. I calculated the positions of the shelves and books using variables and loops. This method allowed me to place multiple shelves and books at regular interval but I had to go through a lot of trials and errors so took me quite some time to figure out how to work with the perspective drawing in P5.js. On my first try, I tried to make a 2D bookshelf, but that didn’t look satisfactory so I added some depth in the drawing using beginShape(), vertex(), and endShape() functions to add some thickness on the sides and changed the fill() to create a contrast on the shadow and lighting. To express what I meant to portray I added one line of quote about books, I achieved this by using textStyle()and textAlign() functions.

Code:

function setup() {
  createCanvas(600, 500);
  setGradient(0, 0, width, height, color(255, 182, 193), color(255, 228, 225)); // Gradient background
  drawHelloKitty(); //function that I am using to draw the main character 
  drawBookshelf();
}

function drawHelloKitty() {
  noLoop();
  noStroke();

  // Draw face
  stroke(0);
  strokeWeight(0.2);
  fill("rgb(252,245,225)");   
  ellipse(200, 200, 180, 180); 

  // Draw ears
  fill('rgb(244,230,215)');
  ellipse(120, 130, 70, 70); // Left ear
  ellipse(280, 130, 70, 70); // Right ear

  //inner ear color

  fill(160, 82, 45); // Medium brown
  ellipse(120, 130, 50, 50); // Left inner ear
  ellipse(280, 130, 50, 50); // Right inner ear

  // eyes
  fill(0); 
  ellipse(170, 200, 22, 22); // Left eye
  ellipse(230, 200, 22, 22); // Right eye

  // Draw nose
  fill(160, 82, 45); // Medium brown
  ellipse(200, 230, 22, 18); // Nose

  // Draw mouth
  
  stroke(0);
  strokeWeight(2);
  noFill();
  arc(200, 245, 50, 25, 0, PI); // Mouth

  // Draw whiskers
  
  stroke(0);
  strokeWeight(2);
  line(100, 200, 50, 190); // Left whisker 1
  line(100, 210, 50, 210); //2
  line(100, 220, 50, 230);  //3

  line(300, 200, 350, 190); // Right whisker 1
  line(300, 210, 350, 210); //2
  line(300, 220, 350, 230); //3

  
  // Drawing body
  fill(255, 0, 0);
  stroke(0);
  strokeWeight(2);
 
  fill(160, 82, 45);
  ellipse(200, 480, 260, 400);
  stroke(0);
  strokeWeight(2);
  fill("rgb(250,204,109)")
  rect(155, 270, 90, 100);


  // hand details
  fill('rgb(252,245,225)'); 
  ellipse(140, 300, 70, 40); // Left hand
  ellipse(260, 300, 70, 40); // Right hand 
  
stroke(0); 
  
strokeWeight(2); 

  // lines for left hand
line(140, 295, 170, 290); // First finger
line(140, 305, 173, 305); 
line(140, 315, 165, 315); 
  
// lines for right hand
line(228, 295, 265, 297); // First finger
line(225, 305, 265, 305); 
line(240, 315, 266, 315); 
}

function drawBookshelf() {
  // Adjusted position of the bookshelf
  const shelfX = 400;
  const shelfY = 150; 
  const shelfWidth = 160;
  const shelfHeight = 360;
  const shelfThickness = 19;
  
  // Draw the bookshelf
  fill(150, 75, 0); // Wood color
  rect(shelfX, shelfY, shelfWidth, shelfHeight); // Main body of the bookshelf
  
  // Draw the shelves
  fill(120, 60, 0); // Slightly darker wood color
  for (let i = 1; i <= 5; i++) { // Adjusted to have more shelves
    rect(shelfX, shelfY + i * 60, shelfWidth, shelfThickness); // Horizontal shelves
  }
  
  // Draw the sides for depth
  fill(100, 50, 0); // Darker wood color for sides
  beginShape();
  vertex(shelfX, shelfY); // Top left corner
  vertex(shelfX + 10, shelfY - 10); // Slightly to the right and up
  vertex(shelfX + 10, shelfY + shelfHeight - 10); // Down to the bottom
  vertex(shelfX, shelfY + shelfHeight); // Back to the bottom left corner
  endShape(CLOSE);
  
  beginShape();
  vertex(shelfX + shelfWidth, shelfY); // Top right corner
  vertex(shelfX + shelfWidth + 10, shelfY - 10); // Slightly to the right and up
  vertex(shelfX + shelfWidth + 10, shelfY + shelfHeight - 10); // Down to the bottom
  vertex(shelfX + shelfWidth, shelfY + shelfHeight); // Back to the bottom right corner
  endShape(CLOSE);

  // Draw the books
  fill(200, 0, 0); // first row
  drawBook(shelfX + 10, shelfY + 60, 40, 80);
  drawBook(shelfX + 60, shelfY + 60, 40, 80);
  drawBook(shelfX + 110, shelfY + 60, 40, 80);
  
  fill(0, 200, 0); // second row
  drawBook(shelfX + 10, shelfY + 130, 40, 80);
  drawBook(shelfX + 60, shelfY + 130, 40, 80);
  drawBook(shelfX + 110, shelfY + 130, 40, 80);

  
  
  // a quote below the bookshelf
  fill(0); 
  textSize(18);
  stroke(0);
  strokeWeight(0);
  textFont('Arial');
  textStyle(NORMAL);
  textAlign(CENTER);
  
 // Text lines
let line1 = "Read,";
let line2 = "Masha!";

// position of the quote
text(line1, width / 3, 320); 
text(line2, width / 3, 340); 
  
}

function drawBook(x, y, w, h) {
  fill(255);
  rect(x, y, w, h); 
  fill(0); 
  rect(x + 10, y, 20, h); //the book spine

}

function setGradient(x, y, w, h, c1, c2) {
  noFill();
  for (let i = y; i <= y + h; i++) {
    let inter = map(i, y, y + h, 0, 1);
    let c = lerpColor(c1, c2, inter);
    stroke(c);
    line(x, i, x + w, i);
  }
}

P5.js Sketch:

Reflection and ideas for future work or improvements:

I felt as a beginner I learnt a lot while doing this basic shape based portrait. From brainstorming to implementing, I changed some ideas, colors and techniques but at the end, I am pretty happy with this little drawing. However, there are areas for improvement and future exploration.

The bear doesn’t look or match with the bear color from the cartoon, so it can be enhanced by using different color pallets from rgb colors. The face shape of the bear can be refined a bit more with some curves to give a bear like feature.  Interactivity can be added into this portrait to make it more appealing and fun. The color combination makes the brown color heavy on eyesight, it can be changed as well.