Generative Text – Week 4

Concept

This code defines a sketch that displays the letters “Zaid” in a random and animated way on a black background. The code is creating a canvas and for manipulating visual elements on the canvas. The code defines a font named “Courier New” and arrays for holding the letters to be displayed and the colors of those letters. It also creates a slider to adjust the speed of the animation. In the setup function, the canvas is created, the background color is set to black, and the font and text size are set.

The draw function is where the animation of the letters takes place. It starts by setting a slightly transparent black background. Then, it gets the value of the speed slider and iterates through the letters array. For each letter, it randomly changes its position, rotation, and size based on noise functions and the current frame count. It then uses the fill function to set the color of the letter based on the corresponding color in the colors array, and finally draws the letter using the text function. Overall, the code creates a dynamic and visually interesting display my name “Zaid” that changes in real-time based on the slider value.

Code

// Define the font to be used
let font = "Courier New";

// Define arrays for the letters and colors
let letters = [];
let colors = [];

// Define an array with the letters to display
let zaidLetters = ["Z", "a", "i", "d"];

// Define a variable to hold a slider for adjusting the speed
let speedSlider;

// Set up the canvas and other variables
function setup() {
  createCanvas(400, 400); // create a canvas with a size of 400x400 pixels
  background(0); // set the background color to black
  textFont(font); // set the font to the one defined earlier
  textSize(120); // set the text size to 120
  textAlign(CENTER, CENTER); // center the text alignment
  for (let i = 0; i < zaidLetters.length; i++) {
    letters.push(zaidLetters[i]); // add the letter to the letters array
    colors.push(color(random(255), random(255), random(255))); // add a random color to the colors array
  }
  speedSlider = createSlider(1, 10, 5); // create a slider for controlling the speed with a default value of 5
  speedSlider.position(10, 10); // set the position of the slider
}

// Draw the letters and animate them
function draw() {
  background(0, 20); // set the background color to a slightly transparent black
  let speed = speedSlider.value(); // get the value of the speed slider
  for (let i = 0; i < letters.length; i++) {
    // Randomly change the position and rotation of each letter
    let x = map(noise(i + frameCount / (50 * speed)), 0, 1, 0, width);
    let y = map(noise(i + 1000 + frameCount / (50 * speed)), 0, 1, 0, height);
    let angle = noise(i + 2000 + frameCount / (100 * speed)) * TWO_PI;
    let size = map(sin(i + frameCount / (30 * speed)), -1, 1, 90, 120);

    push();
    translate(x, y);
    rotate(angle);
    fill(colors[i]);
    textSize(size);
    text(letters[i], 0, 0);
    pop();
  }
}

Reflection / Future Improvements

This code is a fun and creative way to display text in a dynamic and animated way. It uses a combination of noise functions and various mapping techniques to generate the position, rotation, and size of each letter, resulting in a unique display with each run of the sketch.

One possible improvement for this code could be to add user interactivity by allowing the user to input their own text and select their own color palette. This could make the sketch more engaging for the user and allow for a wider range of creative possibilities. Another possible improvement could be to add more animation effects, such as scaling, fading, or moving the letters in different directions. This could add more visual interest and complexity to the sketch, making it even more dynamic and engaging. With some additional improvements and modifications, it could be further developed into a fully-featured text animation tool. The project was very fun to work with but it took time to get the hang of how to use text in a rather creative and artistic way.

Week 4 – Text Waterfall

My inspiration for this project came from the art piece called “Text Rain” made by Camille Utterback in 1999 where on a projected screen, letters from the alphabet were falling randomly. Only, in Camille’s piece, people could stand in front of the projector and “catch” the letters in order to try and make a word.

Below you can find my own project:

and below you can find the code for this project:

let letters = []; // array to store falling letters
let slider; // slider to control letter frequency

function setup() {
  createCanvas(600, 600);
  slider = createSlider(0, 50, 10); // create slider with range from 0 to 50 and default value of 10
  slider.position(10, height + 10); // position slider below canvas
}

function draw() {
  background(220);
  // generate new letters based on slider value
  for (let i = 0; i < slider.value(); i++) {
    letters.push(new Letter(random(width), -20, random(65, 91))); // add new letter with random position and uppercase letter code
  }
  // draw and update all letters
  for (let letter of letters) {
    letter.draw();
    letter.update();
  }
}

class Letter {
  constructor(x, y, code) {
    this.x = x;
    this.y = y;
    this.code = code;
    this.speed = random(1, 5);
  }
  
  draw() {
    text(String.fromCharCode(this.code), this.x, this.y); // draw letter at current position
  }
  
  update() {
    this.y += this.speed; // move letter down based on speed
    if (this.y > height) {
      // remove letter from array when it goes off screen
      let index = letters.indexOf(this);
      letters.splice(index, 1);
    }
  }
}

While obviously I am nowhere near the level to add Camille’s interactivity nor do I have the resources to do so, I took inspiration from this piece and tried to recreate it on my own. While her letters are a bit more scattered in order to make them easier to catch, I wasn’t limited by this feature and so tried to create a waterfall like effect in order to create a bombardment of letters. My reason for this is because one day in class, professor Mang mentioned something interesting about randomness and infinity in where say if letters keep falling randomly and for an infinite amount of time, eventually, they will end up spelling out a word. The thought of such a possibility intrigued hence why I wanted to create a waterfall in order to try and maximize that chance.

I’m especially proud of my implementation with the slider allowing the user to control how many letters should fell if it feels over or underwhelming for them. You can turn the slider to 0 if you are tired of the letters or max it out though I wouldn’t maybe recommend that if you have an especially old laptop.

I could definitely improve on this by adding more color to the background or adding some audio such as maybe a waterfall audio or such. It would add more personality and touch to the project though I guess you could say the simplicity if this project adds to it as well.

Weekly Assignment 4

Concept:

Initially I had some difficulty in choosing the data because I wasn’t sure what kind of data I should look for. Eventually, for my dataset, I chose a csv file that contained data regarding stars. The information contained in the csv are: temperature, luminosity, radius, absolute magnitude, star type, star color, and the spectral class. Of these categories, I used the radius, star type, and star color. Even after I chose my dataset, I wasn’t sure how to exactly visualize it since there wasn’t a category that specifically translated into x and y axis positions. So in the end I just ended up arranging the stars in order they had from the csv but separated by their type.

Coding:

My project consists of 6 different windows that can be navigated by the buttons on the top where each button switches the canvas to show the corresponding star type. The stars are aligned in the center and are arranged in order of the csv data and dare drawn by the color according to the data stored in the csv. Determining the radius of the stars was a bit tricky because the discrepancy between the smallest and the largest radius was too huge so I had to scale the radius for bigger and smaller stars.

switch(int(this.starType)){
      case 0:
        this.radius *= 100;
        break;
      case 1:
        this.radius *= 50;
        break;
      case 2:
        this.radius *= 1000;
        break;
      case 3:
        this.radius *= 20;
        break;
      case 4:
        this.radius *= 1;
        break;
      case 5:
        this.radius /= 10;
        break;
    }

So this is how I scaled the star sizes. For the bigger stars, since the radius is greater than the distance between the stars, the stars overlap and create a shape which is what I somewhat expected/aimed to achieve to create some sort of art. For the switching menus, I just created a separate drawing function that ignored all the stars that are not the current target to be drawn on the canvas.

Challenge:

My initial challenge for this assignment was deciding what kind of data to be used and how to visualize the chosen data. I couldn’t find an appropriate data and be creative with the data I have chosen so I basically had to compromise with what I found. Once I was working with the data set for my current final project, a challenge I ran into was that the data for each stars weren’t arranged by their types. So for example, after 10 type 0 stars, there would be 10 type 1 stars and later on after cycling through all the types there would be 10 type 0 stars again and so on. This was a problem for me since I wanted to arrange the stars based on the star types and align them in order. So I came up with a mechanism where I literally stored the x coordinates for all the stars in a 2d array where the columns were the star types and the rows are the arrays for actual x coordinates. And then in the instantiation of the star instances, for each star that is being instantiated, the counter for that star’s type will be increased and therefore the star’s x coordinate will be directed to the according x coordinate being stored in the 2d array. I know I could’ve took a different approach by sorting the array in a certain format, but I feel particularly proud with my problem solving skills even though it may not be the most efficient method.

let xCoArr = []
let xCoCounterArr = [0, 0, 0, 0, 0, 0];
   
... 

for(let i = 0; i < 6; i++){
  let xCo = 10; //the first x coordinate
  for(let j = 0; j < 40; j++){
    xCoRow.push(xCo); //Stores the x coordinate positions in the array
    xCo += 17; //the distance between the stars
  }
  xCoArr.push(xCoRow); //pushes each x coordinate array per star type
}

...

this.xCo = xCoArr[this.starType][xCoCounterArr[this.starType]];
xCoCounterArr[this.starType]++;

 

Reflection:

This wasn’t my particular favorite nor best assignment because I was really not sure what exactly to do till the last minute, but it still was a good experience in dealing with csv and using it to visualize through p5js. I think my best takeaway from this assignment is the problem solving experience with the arranging of the stars.

A game of Madlibs – Hana

I decided to use the word randomizer task as a way to make a fun game of madlibs.

I loaded all words from a .csv file, and categorized them appropriately. Then I printed the text onto the canvas, leaving spaces for the words which will be chosen by the program and distinguishing them with a red font.

Here is an example of how the specific words are chosen randomly:

//function which randomly chooses and returns a verb through the use of random integers
function chooseVerb() {
  let num = int(random(verbs.length));
  return verbs[num];
}

This same function is repeated for nouns and adjectives as well. This function itself returns the word and I use the returned value to print onto the canvas:

//putting the paragraph together and printing it on the canvas
function printMadLibs() {
  text("Summer is all about the beach! During the daytime, you can ", 10, 20);
  fill("red");
  text(chooseVerb(), 335, 20);
  fill("black");
  text("in the lake or sea and collect seashells and ", 10, 40);
  fill("red");
  text(chooseNoun(), 245, 40);
.
.
.

This was a very fun program to work on since it is based on the popular game of Madlibs. Each time you refresh it will have a new set of words chosen randomly, and sometimes they are very funny, other times they make no sense.

This was the first time I have done any sort of file manipulation in JavaScript, so I am happy to have learned how to do it. I mainly used the class notes and P5 reference page to figure things out.

Assignment – Data Visualization

Concept

For this assignment, I selected the data from the London DataStore by King’s College London about “London Average Air Quality Levels”. The data shows background average readings for Nitric Oxide, Nitrogen Dioxide, Oxides of Nitrogen, Ozone, Particulate Matter (PM10 and PM2.5), and Sulphur Dioxide over the course of 9 years, from January 2010 to Aug 2019. I wanted to represent the readings in the form of particles of varying sizes and colors with a background color of sky to show how these air pollutants exist around us and the level of impact they make. 

Code

I started off by representing the pollutants’ readings using points on the normal canvas size but as the number of categories for pollutants compared to the number of months(rows) was small, I used map( ) to space out the particles across the y and x axis. I used ±random(1, -1) to cause a vibrating impact for the particles to kind of show them as molecules vibrating in the air. I also used map( ) to set the size of particles according to their recorded quantity in the air. For the other display, I displayed data as a continuous vertex which formed a diagonal line in the middle to display the net decrease in the quantity of pollutants in the air.

[Click mouse to change data representation and move mouse for date display]

The values increase top to bottom and that’s why the size of particles at the bottom is greater than that at the top. The size of particles depicts the magnitude of the readings, larger particles represent more quantity of a certain pollutant in the air. All seven pollutants that are recorded have different colors as well. The month and year is displayed along the bottom when the mouse moves within the column range of one data value.

Reflection

This particular assignment was fairly easy to work with because the majority of the tasks were performed by already existing table functions. It was interesting to see how simple data could be used to create such visual representations and experimenting with the same data caused different results. I think I would want to make the display more aesthetically pleasing and interactive next time.

Week 4 – Text and Data Visualization

Concept

I wanted to combine both text and data visualization for this assignment so I decided to import a dataset containing countries with their respective latitude and longitudes. After importing these, I split each row of information by commas and saved the country name, longitude, and latitude in variables. I then used these variables to create objects for each country and saved their name along with their x and y positions. The initial x and y positions are the country’s longitude and latitude mapped onto the canvas and then these countries are made to move along the canvas while their original locations (represented by blinking points) stay static. The background is an image of the world map.

This piece represents the dynamism that exists between countries today. The world is super interlinked with countries staying connected, be it through trade, technology, or politics. Thus, this piece aims to represent our dynamic, ever-evolving, globalized world. Moreover, for interactivity, the mouse can be pressed to pause and play the motion of the countries.

Sketch and Code

For this sketch, I used the following as can be seen in my code:

  • Image
  • Data visualization (country dataset)
  • Text
  • OOP to create country objects
  • Mapping
  • Loops
  • Randomization
//declare global variables
let img;
let lat;
let lon;
let country;
let countries =[];
let minSpeedX =-1;
let maxSpeedX =1;
let minSpeedY =-2;
let maxSpeedY = 2;
let updateScreen =true;

//preload images and csv data to avoid errors/undefined values
function preload() {
  img = loadImage('image/3.png');//2
  // The text from the file is loaded into an array.
  strings = loadStrings("world.csv");
}

//set up a canvas
function setup() {
    createCanvas(600, 500);
    smooth();
    background(220);  
    image(img, 0, 0);
    //frameRate(50);
  // Top-left corner of the img is at (0, 0)
  // Width and height are the img's original width and height
  
  if (strings == null) {
    print("failed to load the file, stopping here");
    // this is an endless loop; it's a common way
    // to prevent a program from continuing when
    // something is so wrong that there is no sense
    // in continuing
    while (true) {}
  }
  print(
    "strings loaded from file successfully, read " + strings.length + " lines"
  );
  
  //use countries data from csv to find name, latitude, and longitude of each country and save each into country objects
  findcountryLocation();
 
}
function draw()
{
  //user can press mouse to pause and play the motion of the country names
   if(mouseIsPressed){
  if (updateScreen == true)
    {
      updateScreen = false;
    }
     else {
       if (updateScreen == false)
         {
           updateScreen = true;
         }
     }
}
  //whenever update screen is true, allow the countries to update/move
  if (updateScreen==true){
     image(img, 0, 0);
    for (let i = 0; i < countries.length; i++)
    {
      countries[i].update();
    
    }
  }
}
  //use countries data from csv to find name, latitude, and longitude of each country and save each into country objects
function findcountryLocation(){
  let row =[];
  // loop over each row in the file
  for (let csvRowNumber = 1; csvRowNumber < strings.length; csvRowNumber++) {
    // get a single row and split that row
    // into individual words
    row = split(strings[csvRowNumber], ",");
    lat = row[1];
    lon = row[2];
    country = row[3];
    //print(country, ' ', lat,' ', lon);
  
    //map the country's longitude and latitude within the limits of our canvas
    ypos = map(lat, -90, 90, 0, height);
    xpos = map(lon, -180,180, 0, width);
    
    //create country object to save name, x and y position in an array of countries
    newCountry = new my_country(country, xpos, ypos, random(minSpeedX, maxSpeedX), random(minSpeedY, maxSpeedY));
    countries.push(newCountry);
  } 
}
//class to save data of countries
class my_country {
  constructor(country, x, y, speedX, speedY){
    this.country = country;
    this.x = x;
    this.y = y; 
    this.speedX=speedX;
    this.speedY=speedY;
    this.pointX =x;
    this.pointY =y;
  }
  
  //function to move the country names
  update(){
    //move/translate the blinking points to center them on canvas
    push();
    translate(0,-80);
    fill(random(255));
    noStroke();
    ellipse(this.pointX,this.pointY,3,3);
    pop();
    
    //write the country name at x and y and update x and y to move the names
    //if the names touch the canvas boundary, change their direction so they bounce back
    fill('white');
    textSize(8)
    text(this.country, this.x, this.y);
    this.x+=this.speedX;
    this.y+=this.speedY;
    if (this.x > width || this.x < 0){
      this.speedX = -this.speedX
    }
    if (this.y > height || this.y < 0)
      {
        this.speedY = -this.speedY
      }
  }
}

Reflection and Further Improvements

I had a lot of fun working with both text and datasets as I loved how such a simple data import allowed for a random and intricate design to be created. I also enjoyed representing our world and its dynamism through this piece and something I realized that this type of art can help us do is convey a message or raise awareness. In future works, I would love to use other data sets to represent a problem in our world such as carbon emissions data or poverty rates, or convey a message/concept as I did for this piece. Something I can improve is the mapping of longitudes and latitudes as I felt the points did not perfectly map on the image and this is something I can further look into to improve.

Assignment 4: Music Visualization

This code uses a circular pattern to represent sound. It plays a song using the p5 library and then plots the song’s amplitude in a circular pattern. The draw method in the code first sets up the canvas and the Amplitude object before preloading the sound file and drawing the visualization.

The circular pattern is produced by the code using the p5.js methods translate, beginShape, vertex, and endShape. The map and random functions are also used by the code to produce the visualization’s design. The amplitude data is likewise stored in an array. The circular pattern is then produced using the amplitude data.

It took some time to get the code to function correctly. I had to confirm that both the sound playback and the amplitude data storage were accurate. I wanted to confirm that the visualization was being rendered properly as well. The toughest issue was appropriately storing the amplitude data in the array.

I was able to add certain design components, such as the random fill color, and the way the sounds circle around the mouse after the code was functioning well. This gives the visualization a beautiful visual component.

I want to enhance the visualization in the future by including more design components. Also, I’d want to provide a mechanism for regulating the visualization’s pace. The user would be able to decide how quickly or slowly the visualization changes thanks to this. The visualization’s size and form may now be adjusted, I’d like to mention. The user would then be able to tailor the display to their preferences.

Week 4: Data Visualisation (Udemy Course Data)

Concept

I found a dataset on Udemy courses on the Kaggle database, which stored the data such as course rating, course duration, course price, and many more. However, I was focused on three of these variables. I quickly browsed through the data and decided to use it to create a scatterplot or three variables. One of them would represent the x-axis, while another one would populate the y-axis, and for the third variable, it would be plotted as the size of the point. This would essentially create a 3-dimensional scatter plot in a 2D graph.

The scatter plot can then be used to find correlation between the three variables and all the six combinations of two variables from three. Some of the observations that can be made from this data visualisation is discussed below.

Procedure

There are a series of steps I followed to reach my end result. Note that: Each blob is an object and has its own attributes.

  1. Data Collection/Retrieval: I got the data set titled “Udemy Courses – Top 5000 Course 2022” from Kaggle. However, I only used about 2000 rows of data as it was faster to load, the code can be scaled to utilise all 5000 rows of data, if necessary.
  2. Data Cleaning: In this phase, I looked at the columns of data that the plot would concern with, namely reviews_avg (course rating), course_duration, and main_price. All of theses columns were in a string format with embedded numbers in them. For instance, the course rating was stored as “Rating: 4.6 out of 5.0“. I split each data point, and extracted the relevant number.
    function getRating(ratings) {
      // stores ratings in floating numbers
      let rating = [];
      max_rating = 0;
      
      // cleaning the data to extract numbers from strings
      for (let i = 0; i < len; i++) {
        let rating_array = ratings[i].split(" ");
        rating[i] = parseFloat(rating_array[1]);
        
        // extracting the maximum rating
        if (max_rating < rating[i]) {
          max_rating = rating[i];
        }
      }
    
      return rating;
    }
    
    function getDuration(durations) {
      // stores duration in floating numbers
      let duration = [];
      max_duration = 0;
      
      // cleaning the data to extract numbers from strings
      for (let i = 0; i < len; i++) {
        let duration_array = durations[i].split(" ");
        duration[i] = parseFloat(duration_array[0]);
        
        // extracting the maximum duration
        if (max_duration < duration[i]) {
          max_duration = duration[i];
        }
      }
      
      return duration;
    }
    
    function getPrice(prices) {
      // stores duration in floating numbers
      let price = [];
      max_price = 0;
      
      // cleaning the data to extract numbers from strings
      for (let i = 0; i < len; i++) {
        let price_array = prices[i].split(" ");
        price[i] = price_array[2];
        
        // ignoring any values that is not present
        if (price[i] == undefined) {
          continue;
        }
        
        // the number in string had comma, e.g. 1,399.99. This portion removes the comma and converts the string into a floating number
        let temp = price[i].slice(2).split(",");
        if (temp.length == 2) {
          // the price was only in thousands, so there is only one comma in each price data
          price[i] = parseFloat(temp[0])*1000 + parseFloat(temp[1]);
        } else {
          price[i] = parseFloat(temp[0]);
        }
        
        // extracting the maximum price
        if (max_price < price[i]) {
          max_price = price[i];
        }
      }
    
      return price;
    }

    The above code shows how the data cleaning was done.

  3. Normalised Values: I normalised the value in each data variable to fit the canvas. This was achieved by comparing each data with its respective maximum data for each variable and multiplying by the some factor of width, height and size. The portion of the code used to normalise the values is as follows:
    // normalizing the data to fit into the canvas
        
    // Price is used on the x-axis, while rating is used on the y-axis
    let xPos = this.price / max_price * (width/1.1);
    let yPos = this.rating / max_rating * (height*2) - height*1.1;
        
    // the duration of the course determines the diameter of the circle
    let diameter = this.duration / max_duration * 200;
  4. Displaying: Notice from the above code snippet, that the prices is used as the x-coordinate, rating is used as the y-coordinate, while duration is used as the diameter for the blob/circle that would be plotted on the canvas. This decision for the axises was made with hit-and-trial as I tried various combination, and chose which appeared to be the most aesthetic.
  5. Interactivity: As part of the interactivity, I added in a hover function to the blob, such that the name, price, rating and duration of the course gets displayed when you hover over any of the blobs in the plot. I implemented this using show_description() function in the class as such:
    show_description() {
      // displays the information about the course when hovered on the blob
      if ((mouseX <= this.xPos + this.diameter/2 && mouseX >= this.xPos - this.diameter/2) && (mouseY < this.yPos + this.diameter/2 && mouseY > this.yPos - this.diameter/2)) {
        if (mouseX > width/2) {
          textAlign(RIGHT, CENTER);
          fill(123);
          rect(mouseX - 520, mouseY - 10, 520, 80);
        } else {
          textAlign(LEFT, CENTER);
          fill(123);
          rect(mouseX - 20, mouseY - 10, 520, 80);
        }
    
        fill("#eeeeee")
        text(this.name, mouseX - 10, mouseY);
        text("Price: "+this.price+ " USD", mouseX - 10, mouseY + 20);
        text("Rating: "+this.rating + "/5.0", mouseX - 10, mouseY + 40);
        text("Duration: "+this.duration + " hours", mouseX - 10, mouseY + 60);
      }
    }

Observations that can be made

There are a couple of interesting observations we can make from the data:

  1. There appears to be no apparent correlation between the price of the course and the course rating. It is interesting there is not much information to conclude that as the course rating increasing, the price increases with it, which is supposedly a common belief.
  2. Notice that the biggest blobs are mostly on the bottom right of the graph, suggesting that the course with have a longer duration also are more costly and also have a higher rating.
  3. However, most of the courses that are short have lower price and a rating that is distributed.

Future Improvements

It appears that the data set stores Udemy Courses have a high average rating which might make the observations on the data biased towards higher rated courses and might not provide definite conclusion about the entirety of Udemy course libraries. So, an improvement would be on the data collection where we can randomly sample a few thousand courses from the entire Udemy course library to maintain a proper distribution of the entire population/sample space.

Homework 04 – Data visualization – “Notable people”

Sketch

Concept

I wanted to do a data visualisation that would show the world in an interesting way. Since I’ve been working on a project which created a dataset of two million notable people in history, I decided to use that dataset. Since this dataset is huge (the whole .csv file is 1 GB), I decided to filter it so that I only look at people in Europe with a notability index as defined in the paper of 23.5 or more. This led to a dataset with 52000 observations and a file size of 4.3 MB.

Each observation (row) in the dataset, after filtering and selecting only the columns I wanted to use, has the birth year, birth location and death location (latitude and longitude) of a notable person in history, as defined by the paper. Therefore I decided to make my visualisation a timeline, from 3500 BC to 2020 AD, where for every year I draw the birth and death locations of all people born at that year. Moreover, the more notable the person as defined by their notability index, the bigger their circle would be. I draw the people as translucent circles, so that if many people were born near the same location, the colors will add up. I picked the color green to represent a birth location, and red to represent a death location.

Code highlight

I’m particularly proud of the part of my code that converts a person’s latitude and longitude into (x, y) coordinates which I use to draw the person later. Latitude and longitude are coordinates on a sphere, while x and y are coordinates on a two-dimensional plane. I essentially needed to figure out a map projection. There are many of these, such as the Mercator projection, Robinson projection, etc. However, I managed to find a projection for which the code is incredibly simple: the Equirectangular projection. This formula is very simple: we map latitude to x and longitude to y. However, this produces a map rotated 90 degrees clockwise, so we also rotate it by using a bit of linear algebra.

function getCoordsFromLatLon(lat, lon) {
  // Equi-rectangular projection.
  // Formula from:
  // https://www.marksmath.org/classes/common/MapProjection.pdf
  // (lat, lon) -> (x, y)
  // But we also rotate 90 degrees:
  // (x, y) -> (y, -x)
  return {x: lat, y: -lon};
}

Note that changing this mapping function will result in different interesting visualizations.

Reflection

The final visualization looks good, especially after tuning a few of the parameters, such as the colors, radii, and transparency of the circles. The dataset I used is huge and has many parameters besides the ones I used. For example, instead of coloring circles based on the person’s birth and death, we can instead use data such as a person’s occupation (discovery/science, culture, leadership, sports/games, other), which might show interesting effects over different locations. Finally, we can can even create a 3D visualization where we place the points on an actual sphere that we might move around. However, the slow performance of p5.js might make this difficult with more than a few thousand data points.

Assignment 4 – Generative Text

Concept

Recently, I have been playing through the story mode of UBISOFT’s Assassin’s Creed: Valhalla. This experience, paired with the recent trip to UBISOFT, inspired me to create this piece. I decided to use the slogan of the brotherhood from the Assassin’s Creed lore, and additionally I used an online translator to convert the slogan from latin to elder futhark scripture. This was done to tribute the Viking culture and their language with respect to the game.

Code 

For this assignment, I experimented a bit with using external media such as images and sound. I would say that the biggest part of my creative process for this assignment was choosing the right image on which the text displayed would be comprehensible.

Additionally, what proved to be a challenge was determining all the relative dimensions which determined the amount of text being displayed.  In order to not hard code them, I spent quite some time defining them to get what I wanted. The main goal was to have the mouse X-coordinate control the amount of text being displayed. This was done through the following code:

textSize(32);
textAlign(CENTER, BOTTOM);
let middle1 = sourceText1.length / 2;
let left1 = middle1 - ((mouseX / width) * middle1);
let right1 = middle1 + ((mouseX / width) * middle1);
//Draw a number of characters from the string depending on the mouse X coordinate
text(
  sourceText1.substring(left1, right1+1),
  width/2, height/10.01);

Eventually I got the following sketch as the final version for this assignment:

Future Improvements

All in all, I am pretty satisfied with the way this assignment turned out for me. I was able to implement things we learnt in class and relate them to a lot of interests that I have. In the future, I would like to focus more on maybe composing an image similar to this one with OOP.