Generative text Output | Assignment 4 |Aadil

Concept 

Initially , I tried figuring out whether data visualization or generative text would be a cooler project to work on . I tried data visualization for precipitation for every city but I couldn’t find large clean datasets and had difficulty in setting the world map to scale as the background of my canvas .

Meanwhile , in the generative text side , I experimented with different fonts and different ways to display and adjust text . I came across the texttoPoints() method that creates an array of points from a text when using a specified font . I looked for cool implementations online and came across this video by Ed Cavett –

I thought this was amazing and decide to do something using this code and logic to create interactive generative text .

    1. First of all, I wanted to have dynamic text such that whatever the user would input in a text box would be displayed on the screen. Although I thought this would be easy, it was challenging for me to implement it as I had to understand the class lineMaker() created by Ed Cavett fully and had to change some aspects of it to ensure that text was generated dynamically.
    2. I wanted to add the words that were typed in the background too – this involved changing the position of the words in the background depending upon the textlength and using loops to fill the whole screen with the correct spacing . I also experimented with color and alpha values to see what suited the best .
    3. I wanted some moving letters based on what the user had typed . For each letter typed by the user, I created a letter object that would move in a circle of changing radius to give the effect that the circle was closing in to the center of the canvas . The letters would disappear as the text was changed .
    4. After reading about signifiers in this week’s reading , I had to make sure that the user knew how to change the text at the center of the canvas . So , I added a message next to the text box so that when the user presses enter, the message at the center is updated .
    5. I also wanted to add a ‘fire ‘ effect instead of a lightning effect to the letters at the center so I changed the color values for highlighting the text and the color of the ‘darts’ too .

Sketch

 

PLEASE ENTER TEXT IN THE TEXTBOX AND PRESS ENTER !! HAVE FUN !!

 

Challenges while implementing

    1. I had a lot of errors while implementing the code. The backspace functionality to remove any letters already on canvas was tricky to implement . It would duplicate the letters . I found a way to clear the array and regenerate it every frame to remove this error . Althouugh this is not the most efficient way to do it  (which would be to push and pop elements at the end of the array)
    2. The aligning of words typed to the background of canvas was another aspect . It took some time to get the alignment right. Initially , I had tried to use the array generated by textToPoints() function for drawing the words as points but the program would lag/crash due to the large number of points so I decided to stick with using text .
    3. The code to generate the darts and form the word in the center that is based on Ed Cavett’s code was fairly complicated for me and it took a lot of time understanding it . Thankfully , I did not have to change much and it was only  important to broadly understand what each segment does so that I could change the properties as intended .

Code that I am proud of 

This project took a lot of time and I am really proud of the fact that I was able to make a dynamic version of Ed Cavett’s text art . In addition , I implemented the background and the spiral letters as well as the Letter class . I am most proud of the code for letter class as follows :

class Letter {
  constructor(char) {
    this.char = char;
    this.x = random(width);
    this.y = random(height);
    this.angle = atan2(this.y - height / 2, this.x - width / 2); // Angle from center
    this.radius = 300;
  }

   update() {
    let angularSpeed = 0.05;
    
    // Increase or decrease radius based on current value
    if (this.radius >= 400) {
      this.radiusDecreasing = true; //boolean to determine whether circle should increase or decrease
    } else if (this.radius <= 200) {
      this.radiusDecreasing = false; //flase if radius is below 200
    }
    
    // Increment or decrement radius
    if (this.radiusDecreasing) {
      this.radius -= 1;
    } else {
      this.radius += 1;
    }
    
    // Update angle
    this.angle += angularSpeed;
    this.x = width / 2 + cos(this.angle) * this.radius;
    this.y = height / 2 + sin(this.angle) * this.radius;
  }

  display() {
    textSize(fontSize);
    fill(255,150,0);
    text(this.char, this.x, this.y);
  }
}

I also spent a lot of time implementing the alignment for the words in the background , this is the piece of code that does that :

// Word Decor
let charArray = input.value().split('');
word = input.value(); //implementing word decor

// Calculate text width
let text_Width = myFont.textBounds(word, 0, 0, fontSize).w;

// Calculate horizontal position for centering
let centerX = (width - text_Width)/2;

 
  // Draw points
  push();
  stroke(255);
  for (let i = 0; i < points.length; i++) {
    point(points[i].x, points[i].y);
  }
  pop();
}*/
for(let j=0;j<num_cols;j++){
  for(let i=0;i<num_lines;i++){
    fill(200,200,200,20);
    text(word,centerX,50+ i*fontSize);
  }
  translate(-text_Width,0);
}
//reset canvas
translate(text_Width*num_cols ,0);
  
for(let j=0;j<num_cols;j++){
  for(let i=0;i<num_lines;i++){
    fill(200,200,200,20);
    text(word,centerX,50+ i*fontSize);
  }
  translate(text_Width,0);
}
//resets canvas to OG position
translate(-text_Width*num_cols,0);

Here, the translate functions have been used to fill the whole canvas . I have made sure that I translate back to the original position once the drawing is done . Although looking back , I think using pop() in the right way would make it easier , this is the way I decided to implement it .

 

Reflection/Scope for improvement

I am very happy with what I achieved at the end . The project took a lot of time but I learnt a lot – especially while trying to understand the lineMaker() class  . However there are certain additional implementations that I had thought of which I would love to look into in the future but I couldn’t implement now because of errors that would take time to debug :

    1. Increase the number of letters in the circle – you will notice there is an unused variable called lettermultiplier in the beginning that is set to 5 as well as a commented out for loop in). This was for adding extra letters to the circle, however the for loop doesn’t work as expected for some reason and the letters attain complete randomness in motion instead of moving smoothly ( it probably has to do something with instantiating each letter object but I couldn’t pinpoint the source of the error) .
    2. Use different font (I have used Roboto which is fairly common , maybe a fancier font could look better)
    3. Experiment with different uses of texttoPoints() function . This function can be used in a variety of interesting ways . Although I have used it only for one such way , there seems to be endless possibilities with this function.

Reading Reflection – Week#4

In the chapter “The psychopathology of everyday things”, Don Norman brings up a multitude of examples of bad design that hinders human experience with the object of said design. He proposes the idea of human-centered design as the ultimate goal of a good design. I do not think that the bad design in some cases is a problem that desperately needs to be fixed. For instance, confusing buttons of a dryer-washing machine is an example the author gives for bad design choices. I agree that the design is not human-centered and can be improved, but I find the emphasis on it being a problem unnecessary. The sales of such machines are profitable and people still buy the complicated technology despite the bad design, otherwise the production of such machines would be terminated. This could indicate that human-centered design is not the primary concern of the businesses selling the complicated technology. This also might suggest that creating a machine with more of a human-centered design is not beneficial to the company. Moreover, consumer can use fridges and washing machines for decades and I think the designers bet on the longevity of the product – it can be confusing at first, but not after 10 years of using the device and it is my understanding that the consumers are attracted to the bursting variety of features rather than the ease of use.
That being said, the author mentions that the good design choices come at an expense and have their fair share of constraints, but it is only discussed in chapter 6, therefore to make a conclusive judgement of the topic I would need to read about what author has to say in chapter 6. I hope the author can prove me wrong by offering worthy alternatives that would benefit the consumer and be feasible for the producers. Disclaimer: my points mentioned in the previous paragraph would apply to consumable goods, but not necessarily other products with bad design.

Assignment # 4 Magnetic Effect Text

Concept

The magnetic effects and trails that I have come across a lot while looking for inspiration for this project served as my motivation. After doing some research, I came to Professor Aron’s website, which featured several interactive elements related to text manipulation. And it was these pictures on Pinterest that inspired me. The primary concept was kinetic typography, which used particles to smoothly transition between words to depict the unpredictable character of the natural world. It demonstrates how basic components can come together to create meaningful expressions when they are directed by physics and a little bit of randomness.

Code

The part of the assignment I’m most proud of is the creation of the Vehicle class. This piece of code is special because it makes each dot on the screen move like it has its mind, heading towards a goal. It combines simple rules of movement and direction to bring letters on the screen. What makes this class stand out is how it turns basic coding concepts into something that feels alive and interactive.

let font;
let vehicles = [];
let originalPoints = [];
let cycleDuration = 5000; // Duration of one cycle in milliseconds
let lastResetTime = 0; // Tracks the last time the positions were reset
let words = ["SHOK", "SHAK", "SHOK"]; // Array of words to cycle through
let currentWordIndex = 0; // Index of the current word in the array

function preload() {
  font = loadFont('myfont.otf');
}

function setup() {
  createCanvas(800, 300);
  background(255);
  textFont(font);
  textSize(192);
  setupWord(words[currentWordIndex]);
}

function draw() {
  let currentTime = millis();
  if (currentTime - lastResetTime > cycleDuration) {
    // Move to the next word in the array, cycling back to the start if necessary
    currentWordIndex = (currentWordIndex + 1) % words.length;
    setupWord(words[currentWordIndex]); // Setup points for the new word
    resetPositions();
    lastResetTime = currentTime;
  } else {
    background(255, 20); // Semi-transparent background for trail effect
  }

  for (let v of vehicles) {
    let noiseForce = getNoise(v.pos.x, v.pos.y);
    v.applyForce(noiseForce);
    v.update();
    v.show();
  }
}

function setupWord(word) {
  vehicles = []; // Clear the current vehicles
  let bounds = font.textBounds(word, 0, 0, 192);
  let posX = width / 2 - bounds.w / 2;
  let posY = height / 2 + bounds.h / 4;

  let points = font.textToPoints(word, posX, posY, 192, {
    sampleFactor: 0.5
  });

  originalPoints = points;

  for (let pt of points) {
    let vehicle = new Vehicle(pt.x, pt.y);
    vehicles.push(vehicle);
  }
}

function getNoise(x, y) {
  let noiseVal = noise(x * 0.01, y * 0.01);
  let angle = map(noiseVal, 0, 1, 0, TWO_PI);
  let force = p5.Vector.fromAngle(angle);
  force.mult(0.1);
  return force;
}

function resetPositions() {
  for (let i = 0; i < vehicles.length; i++) {
    vehicles[i].pos = createVector(originalPoints[i].x, originalPoints[i].y);
    vehicles[i].vel = p5.Vector.random2D().mult(0); // Reset velocity
  }
}

class Vehicle {
  constructor(x, y) {
    this.pos = createVector(random(width), random(height)); // Start with random positions
    this.target = createVector(x, y);
    this.vel = p5.Vector.random2D();
    this.acc = createVector();
    this.r = 4;
    this.maxspeed = 4;
    this.maxforce = 1;
  }

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

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

  show() {
    stroke(0);
    strokeWeight(this.r);
    point(this.pos.x, this.pos.y);
  }
}

Embedded Sketch

Reflection and ideas for future work or improvements

Reflecting on this assignment, I’m considering the introduction of interactivity, allowing viewers to influence the flow and form of the animation in real-time. Additionally, refining the transitions between words to be even smoother would elevate the visual fluidity, creating a seamless blend from one word to the next. These improvements aim to turn the animation into a more immersive one.

Week 4 Reading Reflection

I enjoyed reading Don Normam’s “The Design of Everyday Things.” After reading Crawford’s definition’s of interactivity in “The Art of Interactive Design,” it was necessary to understand the fundamentals of interactions themselves, which go beyond the definition and importance. To me, the fundamental principles of interactivity were very well put in Don Norman’s book. I liked the text, probably because the examples were very grounded in our day-to-day interactions.  The way Norman advocated for human-centric design seemed very similar to Myron Krueger’s philosophy that technology should serve human needs and desires (What Should You Wear to an Artificial Reality?). This also prompted the benchmark of calling a design good or bad.  The reading really helped me to understand the importance of intuitive design. I always wondered why all the cars, phones, and computers looked similar.  Norman’s examples of the learning curves of the users and intuitive design pretty much answered that question. However, I felt the examples lacked the presence of age groups. It would have been interesting to explore how intuitive design changes through time and generations. Norman’s illustration of the distinction between affordances and signifiers was very thought-provoking for me, as I had never explored the dimension of interactivity this much.

 

Reading Reflection | Week 4 | Aadil

In the design of everyday things, Don Norman discussed many interesting ideas and perspectives of design thinking. His example of doors – was quite thought provoking. Last week’s reading had this idea of ‘form and function’ in interactivity design. Thinking about something as simple as doors made this concept clear to me – both form and function are essential to develop a good design.

Norman’s idea of Human Centered Design that focuses on making machines discoverable and understandable was quite interesting too. He stated several things that I relate with – particularly about us taking shortcuts when a product is too complex. Thinking about this, I thought that maybe there is an ideal level of complexity for all products and the designer has to think about how much complexity is enough? In general, I would say that there should be as little complexity as possible and as much complexity as necessary.

The ideas of affordance, signifiers, constraints, mapping, feedback were all new to me. I had heard about affordance previously but never though t of it as a “relationship” between people and objects. This reading compelled me to think about some designs I encounter in everyday life (such as a lift or a chair). I hope to integrate these principles into any future design projects that I undertake , so that I can find solutions that are user-friendly and intuitive.

Assignment 4: Generative Text Output

Concept

The concept I aimed to replicate the captivating streams of 1s and 0s that are frequently shown in movies or GIFs, which represent the fundamentals of digital computing and communication. I wanted to achieve something similar to seeing a complex dance of binary digits, therefore I represented these streams with random characters.

Inspiration 

Sketch


Code

function draw() {
  background(0); 
  
  // Display and animate characters
  for (let x = 0; x < cols; x++) {
    for (let y = 0; y < rows; y++) {
      let yOffset = (millis() * speed + x * 50 + y * 50) % (height + 200) - 100;
      
      if (grid[x][y].bright) {
        // Set bright neon blue color for highlighted characters
        fill(0, 255, 255); 
      } else {
        // Set light neon blue color for other characters
        fill(0, 150, 255); 
      }
      
      text(grid[x][y].char, x * charSize, y * charSize + yOffset);
    }
  }
}

In the draw() method, I have a nested loop that iterates over each cell of the 2D grid array. The location of the characters are calculated and its appearance based on its coordinates and a time-based offset (yOffset). This offset is computed given the cell coordinates, a predetermined speed value, and the current value of millis(), which indicates the milliseconds since the sketch began operating.

if (grid[x][y].bright) {
        // Set bright neon blue color for highlighted characters
        fill(0, 255, 255); 
      } else {
        // Set light neon blue color for other characters
        fill(0, 150, 255); 
      }

Each character’s color is chosen according to the entry that corresponds to it in the grid array. fill() method applies a bright neon blue color to a cell if its bright attribute is set to true. Otherwise, ordinary characters are shown in a lighter neon blue hue.

Full Code

// 2D array to display the characters
let grid;
// the font size of the characters
let charSize = 20;
// columns and rows for the 2D array (grid)
let cols, rows;
// speed of the characters falling
let speed = 0.2;

function setup() {
  print(windowWidth,windowHeight)
  createCanvas(windowWidth, windowHeight);
  
  //creating the 2D array
  cols = floor(width / charSize);
  rows = floor(height / charSize);
  
  grid = create2DArray(cols, rows);
  //  initializing the characters font size 
  textSize(charSize);
  
  // Initialize grid with random characters
  for (let x = 0; x < cols; x++) {
    for (let y = 0; y < rows; y++) {
      grid[x][y] = {
        // calling the characters randomly      
        char: randomChar(),
        // Randomly determine if the character should be brighter or not
        bright: random(1) > 0.8 
      };
    }
  }
}

function draw() {
  background(0); 
  
  // Display and animate characters
  for (let x = 0; x < cols; x++) {
    for (let y = 0; y < rows; y++) {
      let yOffset = (millis() * speed + x * 50 + y * 50) % (height + 200) - 100;
      
      if (grid[x][y].bright) {
        // Set bright neon blue color for highlighted characters
        fill(0, 255, 255); 
      } else {
        // Set light neon blue color for other characters
        fill(0, 150, 255); 
      }
      
      text(grid[x][y].char, x * charSize, y * charSize + yOffset);
    }
  }
}

function windowResized() {
  resizeCanvas(windowWidth, windowHeight);
  cols = floor(width / charSize);
  rows = floor(height / charSize);
  
  grid = create2DArray(cols, rows);
  
  // Reinitialize grid with random characters
  for (let x = 0; x < cols; x++) {
    for (let y = 0; y < rows; y++) {
      grid[x][y] = {
        char: randomChar(),
        // Randomly determine if the character should be brighter or not
        bright: random(1) > 0.8 
      };
    }
  }
}

// function to create the 2D array grid
function create2DArray(cols, rows) {
  let arr = new Array(cols);
  for (let i = 0; i < arr.length; i++) {
    arr[i] = new Array(rows);
  }
  return arr;
}

// function to generate a random character
function randomChar() {
  return String.fromCharCode(floor(random(65, 91)));
}

 

Challenges

The significant challenge was how to keep the characters’ animation in a fluid and synced motion along with the shifting grid placements while maintaining readability and visual consistency.

Another challenge was assigning different brightness levels to different characters. This required careful coordination to make sure the highlighted characters shone out without overshadowing other characters or creating visual clutter.

Improvements

There is one improvement that I would like to implement in the future. I would want to improve the animation algorithm’s efficiency that may result in more fluid and flawless visual transitions, particularly when working with bigger grids or faster animation rates.

Another improvement could be the inclusion of user interactivity. The user could disrupt the falling characters using mouse hover and the characters trying to get back into the original stream.

Reading Reflection 4 – Pavly Halim

Reflecting on Chapter One of “The Psychopathology of Everyday Things,” the text really made me think differently about the stuff we use every day. It’s like, we’ve all been there, struggling with something that’s supposed to be simple, like a door that doesn’t make it clear if you should push or pull. The author talks about how these design fails aren’t just annoying, but they actually show a more significant problem: designers sometimes focus more on making things look cool or high-tech instead of making them easy for us to use.

It made me question why design often misses the mark on user-friendliness. Is it because designers are trying too hard to impress other designers instead of thinking about regular people? Reading this has definitely changed how I look at things around me. Now, I catch myself wondering how something could be designed better to avoid confusion or frustration. It’s interesting to think about how much smoother our day could be if more things were designed with the user in mind. This chapter has sparked a lot of questions for me about what else in my daily life could be improved with better design.

Assignment 4 – Pavly Halim

The Concept:
My project is centered around the visualization of stock market data, specifically designed to help users visually track the fluctuations in stock prices over time. The idea was to create an interactive graph that could display a moving window of stock prices, allowing users to see trends and changes as they happen.

A Highlight of the Code:
One snippet of code that stands out to me for its simplicity and effectiveness is the drawGraph function. This function renders the graph on the screen, mapping the stock prices to visual elements that can be easily understood.

function drawGraph(startIndex) {
  const graphWidth = 700;
  const graphHeight = 300;
  const startY = height - graphHeight - 50;
  const endY = startY + graphHeight;
  const startX = (width - graphWidth) / 2;
  const endX = startX + graphWidth;

  //Calculate the subset of prices to display
  let displayedPrices = prices.slice(startIndex, Math.min(startIndex + windowSize, prices.length));
  const minValue = Math.min(...displayedPrices);
  const maxValue = Math.max(...displayedPrices);
  const latestPrice = displayedPrices[displayedPrices.length - 1]; // The last price in the displayed subset

  push();
  stroke(0);
  noFill();
  rect(startX - 10, startY - 10, graphWidth + 20, graphHeight + 20);
  pop();

  push();
  stroke(25, 118, 210);
  strokeWeight(2);
  noFill();
  beginShape();
  for (let i = 0; i < displayedPrices.length; i++) {
    let x = map(i, 0, displayedPrices.length - 1, startX, endX);
    let y = map(displayedPrices[i], minValue, maxValue, endY, startY);
    vertex(x, y);
  }
  endShape();

Embedded Sketch:

Showcasing the dynamic visualization of stock prices over time

 

Code:

let stockData;
let currentIndex = 0;
let windowSize = 15; //Data points to display
let prices = [];

function preload() {
  stockData = loadTable('stock_prices.csv', 'csv', 'header');
}

function setup() {
  createCanvas(800, 400);
  frameRate(1); // Framerate 
  loadPrices(); // Load data
}

function draw() {
  background(255);
  drawGraph(currentIndex);
  currentIndex++; // Move the window forward
  if (currentIndex > prices.length - windowSize) currentIndex = 0; // Loop back if we reach the end
}

function loadPrices() {
  //Extract prices and store it in array
  for (let r = 0; r < stockData.getRowCount(); r++) {
    let price = stockData.getNum(r, 'price');
    prices.push(price);
  }
}

function drawGraph(startIndex) {
  const graphWidth = 700;
  const graphHeight = 300;
  const startY = height - graphHeight - 50;
  const endY = startY + graphHeight;
  const startX = (width - graphWidth) / 2;
  const endX = startX + graphWidth;

  //Calculate the subset of prices to display
  let displayedPrices = prices.slice(startIndex, Math.min(startIndex + windowSize, prices.length));
  const minValue = Math.min(...displayedPrices);
  const maxValue = Math.max(...displayedPrices);
  const latestPrice = displayedPrices[displayedPrices.length - 1]; // The last price in the displayed subset

  push();
  stroke(0);
  noFill();
  rect(startX - 10, startY - 10, graphWidth + 20, graphHeight + 20);
  pop();

  push();
  stroke(25, 118, 210);
  strokeWeight(2);
  noFill();
  beginShape();
  for (let i = 0; i < displayedPrices.length; i++) {
    let x = map(i, 0, displayedPrices.length - 1, startX, endX);
    let y = map(displayedPrices[i], minValue, maxValue, endY, startY);
    vertex(x, y);
  }
  endShape();
  pop();
 push();
  textSize(17);
  fill(0);
  noStroke();
  textAlign(CENTER);
  text(`Current Price: $${latestPrice.toFixed(2)}`, width / 2, startY - 25);
  pop();
}

Reflection and Future Work:
This project has allowed me to refine my skills in processing and presenting data in an informative and aesthetically pleasing way. The feedback loop provided by visually tracking the effectiveness of the code in real time has been incredibly rewarding.

Moving forward, I see several avenues for improvement and expansion. For instance, integrating real-time data feeds to allow the graph to update with live market data would significantly enhance its utility. Additionally, incorporating user input to select different stocks or adjust the window size and time frame could make the tool more versatile and user-friendly.

Week 4: Reading response

Norman’s chapter on the design of everyday things was very relatable to me as I completely understand his frustration of poorly designed daily objects, especially doors that don’t clearly indicate a way of opening them. On the topic of doors, I also think that they should have some sort of design that one can immediately tell how they function. One thing I kept looking forward to in this reading was for Norman to propose a solution to this, by showing us a prototype or just writing down a way in which designers can make doors better. However, he never gives us a clear idea of how he would want a door to be designed like.

On another note, I also understand his struggle with instruction manuals as first of all, humans usually try to mess around with the object rather than actually reading what is written in the manual. Secondly, most instruction manuals are not really that clear. Overall, I agree with a lot of what Norman is telling us in this chapter.

Week 4: Generative Text

For this assignment, I decided to explore generating text in p5.js. I created a CSV file that contains words that are part of a meal such as the topping, side, drink and main. The blackboard generates a new and random “special meal” on every mouse click.

A part that I struggled with is making every meal generate randomly, which I tackled by writing the code below:

Row = split(strings[int(random(strings.length))], ',');

For improvements, I want to add more visuals. In addition, I want the topping to be randomized for every part of the meal rather than having the same topping for the main, side and drink.