Week 11 – Fabric Cellular Automaton

Concept:

Inspiration

Throughout the semester, my coding skills have improved. Looking back at my first assignment and this one, I think there is a lot of improvement in making my ideas come to life.  For this week’s project, I used a dimensional Cellular Automaton to create a fabric-like pattern. My project is an interactive cellular automaton inspired by the RGB Cellular Automaton project. The randomized ruleset and adjustable cell size allow users to visualize infinite visual possibilities. In this project, each layer of cells builds on the previous to create a unique pattern. 

Highlight and Progress:

I began this journey by searching for inspiration because I felt there were a lot of constraints in the algorithmic design of the cellular automaton. I started experimenting with this project in hopes of creating a system where the color of the cells that are equivalent to one would change around the mouse; however, I faced so many issues, and the system would stop working all the time. I tried to fix it, but I had limited time to finish this assignment. As a result, I began experimenting with different shapes and sizes and even tried to create a new rule set each time the mouse was pressed.

Different Stages of the project
Different Stages of the project
Different Stages of the project
Different Stages of the project

 

Throughout the process, I began improving the project by adding layers to it, as you can see from the interface, I initially wanted it to feel like a website. I think what I like the most about the project is the visualization. I think it is interesting and engaging to see and experiment with. I am proud that I was able to tackle the ruleset to be able to change it. This is done by pressing the mouse. When the mouse is pressed, the for() loop goes through each index of the cells and sets the value to 0, resetting all cells. For each cell, the x and y positions are calculated based on trigonometric functions After resetting the grid, the center cell is set to 1, making it an active cell. This marks the starting point for the new pattern.

//  start at generation 0
let generation = 0;
// cell size
let w = 3;
let slider;
//state
let isComplete = false;

// starting rule
let ruleset = [0, 1, 1, 0, 1, 0, 1, 1];

function setup() {
  createCanvas(windowWidth, windowHeight);
  background(255);
  textFont("Arial");

  //   make slider to change cell size
  slider = createSlider(2, 20, w);
  slider.position(25, 25);
  slider.style("width", "100px");

  // save button
  saveButton = createButton("Save Canvas");
  saveButton.position(160, 5);
  saveButton.mousePressed(saveCanvasImage);

  // reset button
  resetButton = createButton("Reset");
  resetButton.position(290, 5); // Place it next to the save button
  resetButton.mousePressed(resetCanvas);

  //array of 0s and 1s
  cells = new Array(floor(width / w));
  for (let i = 0; i < cells.length; i++) {
    cells[i] = 0;
  }
  cells[floor(cells.length / 2)] = 1;
}

function draw() {
  //slider w value
  w = slider.value();
  fill(240);
  stroke(0);

//   for slider text 
  rect(25, 5, 104, 20,2);
  fill(0);
  stroke(255);
  textSize(13);
  textFont("Arial");
  text("Adjust Cell Size:", 30, 20);
  
//   for resitting ruleset text 
  fill(240);
  stroke(0);
  rect(385, 5, 240, 20,2);
  fill(0);
  stroke(255);
  textSize(13);
  textFont("Arial");
  text("Press Mouse to Generate a new pattern ", 390, 20);
  
  

  for (let i = 1; i < cells.length - 1; i++) {
    //drawing the cells with a state of 1
    if (cells[i] == 1) {
      noFill();
      stroke(random(180), random(180), random(250));
      //y-position according to the generation
      square(i * w, generation * w, random(w));
    }
  }

  //next generation.
  let nextgen = cells.slice();
  for (let i = 1; i < cells.length - 1; i++) {
    let left = cells[i - 1];
    let me = cells[i];
    let right = cells[i + 1];
    nextgen[i] = rules(left, me, right);
  }
  cells = nextgen;

  // gen + 1
  generation++;

  // stop when it reaches bottom of the canvas
  if (generation * w > height) {
    noLoop();
    isComplete = true;
  }
}

// a new state from the ruleset.
function rules(a, b, c) {
  let s = "" + a + b + c;
  let index = parseInt(s, 2);
  return ruleset[7 - index];
}

// mouse is pressed
function mousePressed() {
  // make new ruleset with random 0s and 1s
  ruleset = [];
  for (let i = 0; i < 8; i++) {
    ruleset.push(floor(random(2))); // Random 0 or
    //  fill(255);
    // square(i *w, generation * w, w);
  }
  //https://p5js.org/reference/p5/console/ for debugging
  console.log("New Ruleset: ", ruleset);

  // restart with the new ruleset
  cells = new Array(floor(width / w));
  for (let i = 0; i < cells.length; i++) {
    cells[i] = 0;
    noStroke();

    let x1 = 5 * i * cos(i);
    let y1 = i * sin(i);
    fill(205, 220, random(90, 155), 10);
    circle(x1, y1, w * random(150));
  }
  cells[floor(cells.length / 2)] = 1;
  generation = 0;
  loop();
}

// save image
function saveCanvasImage() {
  saveCanvas("cellularAutomata", "png");
}

// restart
function resetCanvas() {
  background(255);
  cells = new Array(floor(width / w));
  for (let i = 0; i < cells.length; i++) {
    cells[i] = 0;
  }
  cells[floor(cells.length / 2)] = 1;
  generation = 0;
  loop(); //
}

 

Embedded Sketch:


Future Work:

To improve this project in the future, it would be interesting to see how with a web camera, audiences can interrupt the pattern by making noise. Additionally, adding a system to choose a set of colors and maybe a layout for the different patterns would also be interesting to visualize. Improving this project to become a website where people can experiment with the endless possibilities using Cellular Automaton in terms of shape, size, color, and layout would be interesting to see. Perhaps this project would evolve into an educational tool, teaching concepts behind  Cellular Automaton and mathematical systems while reflecting the intersection of art, technology, and science in an interactive format.

Resources:

Wolfram Science and Stephen Wolfram’s “A New Kind of Science.” www.wolframscience.com/gallery-of-art/RGB-Elementary-Cellular-Automaton.html.

https://mathworld.wolfram.com/ElementaryCellularAutomaton.html

 

Weel 10 _ Magnets_Matters.js

Concept:

  For this project, I got inspired by the nature of codebook examples. Especially the ones about attraction forces and movers. I decided to create a particle system that is affected by attraction and repulsion forces. Through this class, I realized I am obsessed with particles and small shapes working together to create different artworks. I imagined that the particles would slowly get attracted to an attractor but then be repulsed quickly to create a dynamic of contradiction. 

Highlight and Process:

Matters.js library is very new to me. Because it is a physics-based engine, it has its own approach to defining bodies, forces, and interactions, as a result, it was a little different to me. I had to take some time to understand the syntax but more time to explore the possibilities it has. It is a very interesting library to delve into due to many limitations in terms of learning the library and its implementation. I think I did not have enough time to explore it enough to comfortably use its full potential in this assignment. 

For the sketch, I had an attractor class and a mover class. The main functionality of the attractor class is to attract the movers into a circular body via forces. Within the attractor class, there is the set position function which later sets the attracto to a new position when the mouse is pressed. The mover class creates a bouncy object made of 4 circles that is affected by air resistance. Making the movers responsive to forces the way I was imagining it was challenging, as I had to play around with a lot of the parameters.  In terms of interactivity, the attractor changes position to wherever the mouse is, and the movers follow. Additionally, I made a slider to increase or decrease the strength of the force toward the attractor. 

 

// inspo http://natureofcode.com

const { Engine, Bodies, Body, Composite, Vector } = Matter;

// A Mover and an Attractor
let movers = [];
let attractor;

let engine;

function setup() {
  createCanvas(700, 650);

  // matter engine
  engine = Engine.create();
  engine.gravity = Vector.create(0, 0);

  // slider to control gravitational constant as a way ti change the strength
 
  gravitySlider = createSlider(0.001, 1, 0.05, 0.001);
  gravitySlider.position(20, height - 40);
  gravitySlider.style("width", "90px");

 
    for (let i = 0; i < 800; i++) {
      movers[i] = new Mover(random(width), random(height), random(0.01, 0.5));
    }
    attractor = new Attractor(width / 2, height / 2);
    // attractor[2]  = new Attractor(width /8 , height / 8, 50);
    //attractor[3]  = new Attractor(width , height , 50);
  }


function draw() {
  background(0, 60);
  Engine.update(engine);

  // show slider on canvas
  // Display the current value of the slider
  fill(255);
  stroke(255);
  textFont('Courier');
  textSize(20);
  text("Change strength " + gravitySlider.value().toFixed(5), 25, height - 60);

  // if mouse is pressed, update the attractor position to follow the mouse
  if (mouseIsPressed) {
    attractor.setPosition(mouseX, mouseY);
  }

  // Apply the attractor force to all movers
  for (let mover of movers) {
    let force = attractor.attract(mover);
    mover.applyForce(force);
    mover.show();
  }
  //attractor.show();
}

 

Embedded Sketch:

Future Work:

In terms of this project, a lot can be improved, for instance, adding more attractions, making the movers more dynamic, and maybe reacting to one another. Additionally, adding more forces and constraints into the sketch. There is a lot to learn and discover about Mattaers.js and other libraries. I think exploring and experimenting more is the best way to learn. 

Resources:

The Coding Train. “5.17: Introduction to Matter.js – the Nature of Code.” YouTube, 7 Mar. 2017, www.youtube.com/watch?v=urR596FsU68.

Acresti, Louis. “Remove Items From Array While Iterating Over It. (Example).” Coderwall, 27 Sept. 2021, coderwall.com/p/prvrnw/remove-items-from-array-while-iterating-over-it.

Matter.Mouse Module – Matter.js Physics Engine API Docs – Matter-js 0.20.0. brm.io/matter-js/docs/classes/Mouse.html.

  1. Physics Libraries. nature-of-code-2nd-edition.netlify.app/physics-libraries/#adding-more-forces.

RGB Color Codes Chart 🎨. www.rapidtables.com/web/color/RGB_Color.html.

Week 9 _ Glass

Concept: 

For this project, I decided to make a visualization inspired by tension and release with an interactive element as the mouse hovers around the canvas. Initially, the flock is strictly constructed around an attractor in a circular shape, but over time, it changes and moves around the canvas. The mouse interaction enables the Boids to transform shape and size, adding layers to the sketch. The shape of the Boide is inspired by kite-like crystal shapes in the shade of light transparent blue symbolizing shattered glass. Resembling the sketch this way makes sense to me because it shows fragility and the Boids collective movement while remaining individual. This mirrors the sensitivity of how one Boids movement influences others.

Highlight of Code:

For this project, I reviewed the presentation and watched the videos to understand the concept of flocking. I wanted to bring into the sketch things we learned prior to the class, and I decided that having an attractor would be interesting. I initially started playing around with the code we did in class. I played a lot with different classes and ways to make it work. I added three main classes: an attractor, a Boid, and a flock. Even though I had so much debugging to do, I think I am close enough to the visualization I had in my head.

The attractor class pushed the Boids towards it through a force similar to orbiting. It was hard to figure out how to make it work, but as I played with the numbers, it started making sense. The orbiting force is simply rotating the force by 2. The Boid class is affected by flocking and attraction. This helps the body move in groups and respond to the attraction force. The methods used for the Boid class are froce, flock, attraction, update, borders, and the show functions. 

for (let i = 0; i < 400; i++) {
   //changing this part effects how the overall visulaization is 
   let angle = map(i, 0, 400, 0, TWO_PI);
   let boid = new Boid(
     width/1.7  + radious * cos(angle),
     height/1.7 + radious * sin(angle)
   );
   flock.addBoid(boid);
 }

The above code is the main effector of the visualization. It took me some time to create a visualization I though was interesting, and it was inspired by the midterm for this semester.

class Boid {
  constructor(x, y) {
    this.acceleration = createVector(0, 0);
    this.velocity = createVector(random(-0.01, 0.001), random(-0.01, 0.01));
    this.position = createVector(x, y);
    this.r = 3;
    //mass
    this.mass = 2;
    // Maximum speed
    this.maxSpeed = 0.7;
    // Maximum steering force
    this.maxForce = 5;
  }
  applyForce(force) {
    let f = p5.Vector.div(force, this.mass);
    //changed how it moves looks nice
    this.acceleration.sub(f);
    this.acceleration.mult(10);
    this.acceleration.mult(f);

  }

  run(boids, attractor) {
    this.flock(boids);
    this.applyAttraction(attractor);
    this.update();
    this.borders();
    this.show();
  }

  applyAttraction(attractor) {
    //pull boids
    attractor.attract(this);
  }

  // accumulate  acceleration each time based on three rules
  flock(boids) {
    let sep = this.separate(boids); // Separation
    let ali = this.align(boids); // Alignment
    let coh = this.cohere(boids); // Cohesion
    // Arbitrarily weight these forces
    sep.mult(1);
    ali.mult(1);
    coh.mult(1);
    // Add the force vectors to acceleration
    this.applyForce(sep);
    this.applyForce(ali);
    this.applyForce(coh);
  }

  // update location
  update() {
    //  velocity
    this.velocity.add(this.acceleration);
    // limit speed
    this.velocity.limit(this.maxSpeed);
    this.position.add(this.velocity);
    // reset accelertion to 0 each cycle
    this.acceleration.mult(0);
    
  }

  // calculate and apply a steering force towards a target
  // STEER = DESIRED MINUS VELOCITY
  seek(target) {
    // A vector pointing from the location to the target
    let desired = p5.Vector.sub(target, this.position);
    // Normalize desired and scale to maximum speed
    desired.normalize();
    desired.mult(this.maxSpeed);
    // Steering = Desired minus Velocity
    let steer = p5.Vector.sub(desired, this.velocity);
    steer.limit(this.maxForce); // Limit to maximum steering force
    return steer;
  }

  show() {
    // draw a kite shape
    let angle = this.velocity.heading();
    fill(random(90, 127), random(150, 200), random(180, 255), 150);
    stroke(random(100, 127), random(100, 200), random(200, 255));
    push();
    translate(this.position.x, this.position.y);
    rotate(angle);

    let freqX = map(mouseX, 0, width, 1, 15);
    let freqY = map(mouseY, 0, height, 1, 15);

    beginShape();

    let x = this.r * cos(10 * this.r) * freqX;
    let y = this.r * cos(10 * this.r) * freqY;
    //crystal like shape
    //right
    vertex(x * 2, 0);
    //top right
    vertex(x * 0.7, -y);
    //top left
    vertex(-x * 0.7, -y);
    //left
    vertex(-x * 2, 0);
    //bottom
    vertex(0, y);
    endShape(CLOSE);
    pop();
  }

  // wraparound
  borders() {
    if (this.position.x < -this.r) this.position.x = width + this.r;
    if (this.position.y < -this.r) this.position.y = height + this.r;
    if (this.position.x > width + this.r) this.position.x = -this.r;
    if (this.position.y > height + this.r) this.position.y = -this.r;
  }

  // separation itchecks for nearby boids and steers away
  separate(boids) {
    let desiredSeparation = 15;
    let steer = createVector(0, 0);
    let count = 0;
    // for every boid in the system, check if it's too close
    for (let i = 0; i < boids.length; i++) {
      let d = p5.Vector.dist(this.position, boids[i].position);
      // ff the distance is greater than 0 and less than an arbitrary amount (0 when you are yourself)
      if (d > 0 && d < desiredSeparation) {
        // calculate vector pointing away from neighbor
        let diff = p5.Vector.sub(this.position, boids[i].position);
        diff.normalize();
        // weight by distance
        diff.div(d);
        steer.add(diff);

        // keep track of how many
        count++;
      }
    }
    // average -- divide by how many
    if (count > 0) {
      steer.div(count);
    }

    // As long as the vector is greater than 0
    if (steer.mag() > 0) {
      // Implement Reynolds: Steering = Desired - Velocity
      steer.normalize();
      steer.mult(this.maxSpeed);
      steer.sub(this.velocity);
      steer.limit(this.maxForce);
    }
    return steer;
  }

  // Alignment
  // For every nearby boid in the system, calculate the average velocity

  align(boids) {
    let neighborDistance = 40;
    let sum = createVector(0, 0);
    let count = 0;
    for (let i = 0; i < boids.length; i++) {
      let d = p5.Vector.dist(this.position, boids[i].position);
      if (d > 0 && d < neighborDistance) {
        sum.add(boids[i].velocity);
        count++;
      }
    }
    if (count > 0) {
      sum.div(count);
      sum.normalize();
      sum.mult(this.maxSpeed);
      let steer = p5.Vector.sub(sum, this.velocity);
      steer.limit(this.maxForce);
      return steer;
    } else {
      return createVector(0, 0);
    }
  }

  // Cohesion
  // For the average location (i.e. center) of all nearby boids, calculate steering vector towards that location
  cohere(boids) {
    let neighborDistance = 40;
    let sum = createVector(0, 0); // Start with empty vector to accumulate all locations
    let count = 0;
    for (let i = 0; i < boids.length; i++) {
      let d = p5.Vector.dist(this.position, boids[i].position);
      if (d > 0 && d < neighborDistance) {
        sum.add(boids.position); // Add location
        count++;
      }
    }
    if (count > 0) {
      sum.div(count);
      return this.seek(sum); // Steer towards the location
    } else {
      return createVector(0, 0);
    }
  }
}

The Boid class was the most complicated. I had to do a lot of debugging to add the attractor into action so that the Boids are affected by it.

Embedded Sketch:


Future work:

I am satisfied with how the sketch turned out to be. Things I want to experiment further with is how the flock would move around the canvas by creating a path for them to create different shapes and interactions that are strictly influenced by tension and release. Further, I think having an audio sync into the work is also interesting to see. The audio could perhaps be in relation to the acceleration and speed of the flock’s movement. Further, if this could translate into an interactive project, perhaps I can add a motion sensor so that the audience can influence the behavior of the flock through that data.

Resources:

The Coding Train. “Coding Challenge 124: Flocking Simulation.” YouTube, 11 Dec. 2018, www.youtube.com/watch?v=mhjuuHl6qHM.

Arthur Facredyn. “Path Finding in p5.js # 1.” YouTube, 11 June 2018, www.youtube.com/watch?v=Ohcrstxcci4.

Murmuration – Robert Hodgin. roberthodgin.com/project/murmuration.

5. Autonomous Agents. nature-of-code-2nd-edition.netlify.app/autonomous-agents/#vehicles-and-steering.

 

 

Week 8 _ Flame

Concept:

For this project, I wanted to explore a difficult reality in my life, reimagining it in a way that feels more approachable and symbolic. This past year and half I am living the mose painful realities of my life—the loss of my familys home and loved ones to the brutality of war. It’s a huge wound that I carry with me everyday, yet try to keep moving. Initially, I considered expressing this darkness without censorship using  a visual of rockets aimed toward a target , with an explosive effect, but I decided on a subtler approach that relies on symbolism. To bring the concept together, I used warm, fiery colors to create a cohesive and evocative experience. 

 

Highlights:

I began by reviewing the slides, reading the chapter, and watching the coding terrain videos to refresh my memory and better understand Autonomous Agents. I began by looking at the code we did in class and began to experiment with it. I decided to focus mainly on the seek and flee rather than pursue and evade because I thought they worked best with my idea.

Initial outcome

I  created an array of 3 targets equal in distance from one another in an angle and another array of 23 vehicles, keeping in mind the 0 index. I tried to figure out how to divide these vehicles towards their targets; I realized I could do this by first creating a target index and dividing ‘i’ by 8, the number of vehicles each target will get, and then passing the target with the target index. Further, I made the default of the program to seek a target until the mouse is pressed; it flees. I also added some transparency to the code, enhancing the flow of the points and the overall aesthetics. For the vehicle class, it was similar to the ones in class for flee and seek. I change them a little to make them work best for my visualization. For the flee functionality, I did not want the points to leave the canvas to fix this, so I created conditions to check the x and y edges of the canvas. 

let vehicles = [];
let targets = [];

function setup() {
  createCanvas(650, 650);
  
  // Initialize 3 main targets and place them near each other with equal distance 
  targets.push(createVector(width * 0.25, height / 2.5));
  targets.push(createVector(width * 0.5, height / 2));
  targets.push(createVector(width * 0.75, height / 1.5));

  // Create an array of vehicles that are grouped to each target
 
  for (let i = 0; i < 23; i++) {
    // Each 8 vehicles share the same target 0 to 17 its 18 so 8 
    let targetIndex = floor(i / 8); 
    //each vehicle has a random intial position and a target 
    let vehicle = new Vehicle(random(width), random(height), targets[targetIndex]);
    vehicles.push(vehicle);
  
  }
}

function draw() {
  background(0,15);


 
  for (let target of targets) {
    
    //draw targets as a circle for now
    //yellowish flamy like 
     fill(255,232,8);
    stroke(255,154,0);
    strokeWeight(2);
   circle(target.x, target.y, 10);
  }

  // Update and show  vehicles 
 for (let vehicle of vehicles) {
   if (mouseIsPressed){
  vehicle.flee(vehicle.target); 
    vehicle.update();
    vehicle.show();
  }else{
    vehicle.seek(vehicle.target);
    vehicle.update();
    vehicle.show();
  }
 }
  
}
class Vehicle {
  constructor(x, y, target) {
    //pass x & y the intial positions of the vehicles 
    this.position = createVector(x, y);
    this.velocity = createVector(0, 0);
    this.acceleration = createVector(0, 0);
    this.r = 10;
    this.maxspeed = 4;
    this.maxforce = 0.3;
    //make a buffer to move away from target when reach 
    //this.buffer = 100;
    // the 3 target the vehicles will seek
    this.target = target; 
    
    // //change the size as an indecator if its going away or towards target 
    // this.size= 15;
  }

  update() {
 this.velocity.add(this.acceleration);
    this.velocity.limit(this.maxspeed);
    this.position.add(this.velocity);
    
    //check edges for flee x
    if (this.position.x > width) {
      this.position.x = 0; 
    } else if (this.position.x < 0) {
      this.position.x = width; 
    }
    //check edges for flee y 
    if (this.position.y > height) {
      this.position.y = 0; 
      
    } else if (this.position.y < 0) {
      this.position.y = height; 
    }
    
    // reset acc
    this.acceleration.mult(0);
  }

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

  applyForce(force) {
    //effects speed and directio
    this.acceleration.add(force);
    
    
  }

  seek(target) {
    let desired = p5.Vector.sub(target, this.position);
    desired.setMag(this.maxspeed);
    let steer = p5.Vector.sub(desired, this.velocity);
    steer.limit(this.maxforce);
    this.applyForce(steer);
  }
    flee(target) {
    // Flee away when reaching buffer
    let desired = p5.Vector.sub(this.position, target); // Reverse direction
    desired.setMag(this.maxspeed);
    let steer = p5.Vector.sub(desired, this.velocity);
    steer.limit(this.maxforce);
    this.applyForce(steer);
  }


  show() {
    
    //dirc
    let angle = this.velocity.heading();
  fill(255,random(0,100),0);
 stroke(255,random(0,100),random(0,8));
    strokeWeight(5);
    push();
    translate(this.position.x, this.position.y);
   rotate(angle);
    beginShape();
    point(-this.r*4 , -this.r*2);
    point(this.r*4 , 0);
    vertex(-this.r * 4, this.r*2);
    endShape(CLOSE);
    pop();
  }
}

Sketch:


Future work:

I think there is a lot that can be done with this code, like adding more vocals and targets and experimenting with the different shapes and styles that can be implemented. Also, giving users more agency over the program where they can play around more and interrupt the decisions of the system. I also think it would be nice if I could try it out with the pursue and evade where I can make a buffer area to either evade or pursue the target. 

Resources:

The Coding Train. “5.2 Seeking A Target – the Nature of Code.” YouTube, 16 June 2021, www.youtube.com/watch?v=p1Ws1ZhG36g.

https://www.researchgate.net/publication/2495826_Steering_Behaviors_For_Autonomous_Characters

https://htmlcolorcodes.com/colors/shades-of-yellow/

https://nature-of-code-2nd-edition.netlify.app/autonomous-agents/

 

Week 8 _ REFLECTION _ MUJO

 

Listening to the Artists talk about MUJO gave me another perspective on how to see their work. The artist talk was helpful in two ways 1) it got me thinking of the work process from a professional perspective, not just projects for classes. It gave me an insight into how the workflow goes and how exciting it can be. 2) The talk opened my eyes to the artwork itself, the meaning behind each part, and how it all comes together to support and push a concept, it is like each part harmonizes to drive the concept forward. Additionally, the installation got me thinking of how concepts such as assembling and disassembling can be symbolically expressed through dance, sound, the dessert, and the sand.

Further, the curation of the project to me was interesting because it was site specific and had different ideas that both work well towards the concept. I think, often artists have to make decisions to change some parts of how the artwork is presented to adapt to the atmosphere and environment surrounding it.

References:

https://www.aaron-sherwood.com/works/mujo/

Midterm – Sonic Threads

Concept:

For this project, I wanted to use what we learned in data visualization and particle systems to visualize Palestinian folklore. I want to visualize sound waves through the lens of Palestinian embroidery. 

The song I picked has an Arabic version and an English Remix.  Thus, Palestinian folklore is known to be one of the vested tools to record events and phenomena. For the Palestinians, historically, it is women who sing these songs; they are a mirror of history and society and a contribution to crystallizing the higher goals of Palestinian society.  For this project, I wanted to use Tarweeda (a type of Palestinian folklore), which is slow singing governed by a specific rhythm that depends primarily on recitation, a melodious recitation that carries its own music. This type of music became vibrant in Palestine during the Ottoman occupation, where women would sing these songs after encoding the words and delivering secret messages to each other and to the resistance fighters. The encryption process happens by adding ‘lam” in the words at the end or final syllables. Through this lens, I want to embed the traditional visual language of Palestinian embroidery with  Palestinian Tarweed in an attempt to visualize the rhythms.

 

For the visualization aspect of the project, I looked into the Palestinian embroidery, which is usually a geometric pattern of shapes, flowers, and lines. The most famous embroidery colors are red threads on black fabric. 

Code and Process:

 

I have been inspired to work with sound waves for a while. I have been watching videos of how it works and how sound wave data can be visualized. Initially, it seemed very complex to me; however, when I watched more and more videos, I got the gist of it. I had to think of a couple of things first before I took this step because I was not certain I could do it. As a result, I decided to see if P5js can visualize data. I found p5.FFT, which is Fast Fourier Transform, is an analysis algorithm that can isolate audio frequencies within a waveform, which returns an array of the analyzed data between -1 and 1. Its waveform() represents the amplitude value at a specific time. The analyze() computes the amplitude along the frequency domain from low to high between 0 to 255, and the energy () measures the amplitude at specific frequencies or ranges. 

I began by preloading a small part of the song into P5js; I tested the P5js given example before doing my own iteration because the P5js sketch was confusing. I then created a variable that takes the p5.FFT data, and a wave data variable to take the waveform. Then, in an array of index numbers, I mapped the wave data for visual purposes and visualized it within a shape similar to what we did in class. When this part was working, I decided to add a Particle system class that is responsive to the sound. To do this, I analyzed the FFT data and read the amplitude of the data to see the numbers I needed to put into the energy to enable it to respond properly. This part took some time because I had to read a little more about the sound wave data and how to make it responsive. 

After everything was working fine, I began experimenting with the shape and how I wanted it to look. My goal was to make it look similar to the embroidery patterns, which are usually floral. I realized that I needed to take the Sine value of i for both the x and the y; I was initially doing Cos(i) for x and Sin(i) for y. Then, I added interaction by making frequency values for x and y, mapping them, and then adding them into the x and y formula for the shape.

Highlight:

let song;
let currentPlaySong = 1;

let song1;
let hasPlayed = false;
let hasPlayed1 = false;
let fft; //this is to generate the wave form Fast Fourier Transform

let r1;

//For Printing https://gokcetaskan.com/artofcode/high-quality-export
//  acording to this the size of my full screen sketch is good for high quality but the particles are not printing trying to fix this i will add them in a buffer actually realized its becuse the save condition was before the particle one

particles = [];

//button to start sketch
let button;
let started = false;

function preload() {
  song = loadSound("/sound.mp3");
  song1 = loadSound("/sound1.mp3");
}

function setup() {
  createCanvas(windowWidth, windowHeight);
  // createCanvas(windowWidth, windowHeight, SVG);
  angleMode(DEGREES);
  fft = new p5.FFT();

  //   button
  button = createButton("Start");
  button.position(width / 2, 530);
  button.mousePressed(startSketch);
}

function draw() {
  background(0);

  //added this before translate to position correctly
  if (!started) {
    text("1-move the curser to change the shape.", width / 2 - 130, 450);
    text(
      "2-Press A or a to listen to the Arabic version",
      width / 2 - 130,
      470
    );
    text("3-Press E or e to listen to the English Remix", width / 2 - 130, 490);
    text("4- Press R or r to Reset & S or s to Save", width / 2 - 130, 510);
    fill(255);
    textSize(15);
  }

  translate(width / 2, height / 2);
  //particles respond to frequencies
  fft.analyze();
  amp = fft.getEnergy(20, [200]);
  // shape

  let waveData = fft.waveform();

  if (started) {
    for (let t = -1; t <= 1; t += 0.09) {
      let freqX = map(mouseX, 0, width, 1, 10);
      let freqY = map(mouseY, 0, height, 1, 10);

      let offSet = map(mouseX, 0, width, 0, 150);
      beginShape();

      for (let i = 0; i < width; i++) {
        stroke(150, 0, 0);
        fill(0);
        strokeWeight(1);
        let index = floor(map(i, 0, 180, 0, waveData.length - 1));

        r1 = map(waveData[index], -1, 1, 100, 300);

        let x1 = r1 * sin(15 * i) * t * cos(i * freqX);
        let y1 = r1 * sin(15 * i) * sin(i * freqY);

        vertex(x1, y1);
      }
      endShape(CLOSE);

      //    if (key == 's'){
      //     save("mySVG.svg");
      //     noLoop();	}
      // }
    }

    let p = new particle();
    particles.push(p);

    for (let i = particles.length - 10; i >= 0; i--) {
      if (!particles[i].edges()) {
        particles[i].update(amp > 190);
        particles[i].show();
      } else {
        particles.splice(i, 0);
      }
    }
  }
}

//function to switch from arabic to english version of the song and reset
function keyPressed() {
  if (key === "a" || key === "A") {
    switchToSong1(); // play song 1 when 'a' is pressed
  } else if (key === "e" || key === "E") {
    switchToSong2(); // play song 2 when 'e' is pressed
  } else if (key === "r" || key === "r") {
    resetSongs(); // reset the songs when 'R' is pressed
  } else if (key === "s" || key === "S") {
    // Save the canvas as PNG without stopping the sound
    save("myPNG.png");
  }
}
//I need to add this because if i dont for some reason when I press a it does not play
function switchToSong1() {
  if (!song.isPlaying()) {
    song1.pause(); // pause song 2 if it's playing
    song.play(); // play song 1
    currentSong = 1;
  }
}

function switchToSong2() {
  if (!song1.isPlaying()) {
    song.pause(); // pause song 1 if it's playing
    song1.play(); // play song 2
    currentSong = 2;
  }
}

function resetSongs() {
  // stop both songs
  song.stop();
  song1.stop();

  // Reset to the first song
  currentSong = 1;
}

function startSketch() {
  started = true;
  button.hide(); // hide the button after starting the sketch
}
class particle {
  constructor(){

  this.pos = p5.Vector.random2D().mult(200);
  this.vel = createVector(0,0);
  this.acc = this.pos.copy().mult(random(0.001,0.0001));
    // this.r = 10;
    // this.lifetime = 255;
this.PW = random(1,5);
}
update(cond){
  this.vel.add(this.acc);
  this.pos.add(this.vel);
  
  if(cond){
    this.pos.add(this.vel);
    this.pos.add(this.vel);
    this.pos.add(this.vel);
    
  }
  
}
  //remove particles that were drawn 
  edges(){
    if(this.pos.x<-width/2 || this.pos.x>width/2 || this.pos.y <-height/2 || this.pos.y>height/2
     ){
       return true;
  } else {
    return false;
  }}
show(){
  noStroke();
   
    fill(204, 0, 0);
  ellipse(this.pos.x, this.pos.y,this.PW);
  
}}

 

In this project, I had a couple of challenges. Initially, the shape I created was reflected in particles because this is how waveforms are printed out. For some reason, even when I drew lines or other shapes to map, it did not look right. I kept experimenting with the numbers within the intended shape until I figured it out. Another challenge was saving an SVG file and a PNG file. Whenever I try to save a file, the whole system lags. According to some sources linked below, it might have been because a) it was saved before the whole sketch was drawn, which made the particles disappear, and b) having the no-loop was causing issues. Even when I had a function for saving, the sound stopped playing; as a result, I added the saving part to the KeyPressed function, which magically solved the problem. 

I am mostly proud that I was able to add other versions of the song into the system and allow the users to switch with songs, reset the whole system, and start again. I did this using conditions and pressing keys instead of the cursor interaction. Further, I create a start condition (state)  button designed in CSS to start sketch.

Pen plotting:

 

As mentioned above, I had challenges saving SVG files. The p5js version I was using was the most compatible with sound, and despite my attempts to integrate the SVG into the file, it kept lagging. Apparently, sound is computationally heavy on the system, and adding SVG was not working, but I managed to get some images and then commented on the code. Another issue was that when pen plotting, the pen would pass through a specific point so many times that the center got a little damaged. I think it adds to the aesthetics of the design. I tried pen plotting twice. The first one was a disaster, but the second was good, considering that it was a new system for me. 

PenPlotP5js

https://drive.google.com/file/d/1yTITCwDRO5CCQ72peazwVLUxmkqrV-G8/view?usp=drive_link

Sketch:

Link: https://editor.p5js.org/shn202/full/9AGLnP3I8

Future work and improvements 

I am satisfied with how my project turned out. However, I think it is always good to keep experimenting and trying new things. For future improvements, I would add more user interactions where users can play with the colors, more complex shapes, and even the particle system. I would also like to create an array of songs that can be played individually or together to create a chaotic experience. 

A3 print:

Resources:

“Creating a Sound Wave Using P5.js.” Stack Overflow, stackoverflow.com/questions/55943459/creating-a-sound-wave-using-p5-js.

Colorful Coding. “Code an Audio Visualizer in P5js (From Scratch) | Coding Project #17.” YouTube, 28 Feb. 2021, www.youtube.com/watch?v=uk96O7N1Yo0.

Interactive Encyclopedia of the Palestine Question – Palquest, www.palquest.org/en/highlight/14497/palestinian-embroidery.

Kazuki Umeda. “Beautiful Sound Visualization Using Polar Coordinates.” YouTube, 3 June 2021, www.youtube.com/watch?v=CY5aGEXsGDo.

Moussa, Ahmad. “Working With SVGs in P5JS.” Gorilla Sun, 3 May 2023, www.gorillasun.de/blog/working-with-svgs-in-p5js.

p5.FFT. p5js.org/reference/p5.sound/p5.FFT.

“Palestinian Embroidery.” Interactive Encyclopedia of the Palestine Question – Palquest, www.palquest.org/en/highlight/14497/palestinian-embroidery.

العربية, مجلة الموسيقى. الترويدة الفلسطينية &Quot;المولالاة&Quot; تراث عاد للظهور. 31 Dec. 2023, www.arabmusicmagazine.org/item/1538-2023-12-31-10-16-38.

https://www.w3schools.com/css/ css comLab class

Taskan, Gokce. Art of Code – Gokce Taskan. gokcetaskan.com/artofcode/high-quality-export.

 

Week 5 _ Visualizing Folklore

Concept 

We have learned so much about the nature of code and the physics behind it. For this project, I wanted to use what we learned in data visualization of Palestinian folklore. I wanted to visualize sound waves on this type of music and visualize it as similar to Palestinian embroidery. I think I aim to preserve cultural heritage digitally. Thus, I want to embed the traditional visual language of Palestinian embroidery with  Palestinian folklore.

Highlight 

 

There were a couple of challenges when mapping the data because, initially, I was getting dots and not lines, and they were not interesting or similar enough to embroidery. I spent a lot of time changing them into the concept I wanted.  I experimented with mathematical equations to reach the desired outcome. I have set the basic code for the project, but there is so much to do. I had to adjust the amplitude for the data and read it to add accurate numbers into the code so that it responds correctly. 

class particle {
  constructor(){

  this.pos = p5.Vector.random2D().mult(200);
  this.vel = createVector(0,0);
  this.acc = this.pos.copy().mult(random(0.001,0.0001));
    // this.r = 10;
    // this.lifetime = 255;
this.PW = random(1,5);
}
update(cond){
  this.vel.add(this.acc);
  this.pos.add(this.vel);
  
  if(cond){
    this.pos.add(this.vel);
    this.pos.add(this.vel);
    this.pos.add(this.vel);
    
  }
  
}
  //remove particles that were drawn 
  edges(){
    if(this.pos.x<-width/2 || this.pos.x>width/2 || this.pos.y <-height/2 || this.pos.y>height/2
     ){
       return true;
  } else {
    return false;
  }}
show(){
  noStroke();
   
    fill(204, 0, 0);
  ellipse(this.pos.x, this.pos.y,this.PW);
  
}}

Further, I made a particular class that also reacts to the sound by reading the fft energy and then moving according to the amplitude. The particles that are out of frame are deleted to avoid breaks and breakdowns.

Sketch 


Future Work 

I want to make buttons to allow the audience to pick a song from a preloaded set of songs, but I also need to work more on the aesthetics and final design of the work. I also want to add some complexity to the visualization, maybe allowing the audience to change the density of the strokes or play with textures and shades.

 

Week 4 – Dancing Wave

Concept:

For this project, I was inspired by the Memo Akten Simple Harmonic Motion project. What stood out to me in this project was the sound and how particles would change if they approached a different line or object. For this project, I decided to experiment with a Dancing wave that would function similarly.

Highlight of code:

I had so many challenges in this code and debugging and figuring out the logic behind the functionality took me some time. There are many parts that I am proud of in this code. I am particularly the flow I created of the animated video.

rotate(PI / 3);

  for (let i = 0; i < angles.length; i++) {
    beginShape();
    for (let i = 0; i < angles.length; i++) {
      let y = map(sin(angles[i]), -1, 1, -150, 150);
      let x = map(i, 0, angles.length, -350, 350);

      let particleSize = r * 0.9; // Default particle size

      // Check if the particle touches the line (y is close to 0)
      if (abs(y) < 5) {
        //draw the shape gradually on canvas

        fill(touchedLineColor); // Change color when touching
        particleSize = r * 2.5; // Increase the size of the particle
      } else {
        fill(getWaveColor(y)); // Get color based on y value
      }

      stroke(getWaveColor(y));
      strokeWeight(1);
      line(cos(x), 0, x, y); // Draw line from middle to particle
      noStroke();
      circle(x, y, particleSize); // Draw the particle
      noFill();
      vertex(x, y);

      endShape();
    }
 angleV[i] = constrain(angleV[i], -0.01, 0.5);
    angles[i] -= angleV[i];
    angleV[i] += angleA[i];
  }
}

The video starts by drawing a simple line that has a sort of random noise. This line is the point of transformation of the color of particles when they touch it. Then, I drew the wave, which gradually increases its acceleration over time. I initially wanted the wave to appear on the canvas slowly before the animation started. I tried so many ways to do that, for instance, making a static wave, then adding the animated one on it, and creating nested loops; however, none of the ways worked, so I decided to improve on it in future work.
Additionally, I am proud of being able to check if the particles are close to the invisible line so that they change color and size. Even though, though it has a simple logic, figuring it out took some time.

Embedded Sketch:


Future Work:
For future work, I want to play more with the wave’s acceleration so that it would accelerate and decelerate when I want it. I also want to experiment with different waves to see the different esthetics I can create, and finally, adding the wave gradually into the canvas would be a strong attribute to the design of this animation.

Reference:

Colorful Coding. (2021, January 22). Simple sine wave animation in p5.js | Coding Project #11 [Video]. YouTube. https://www.youtube.com/watch?v=ktPnruyC6cc

Colorful Coding. (2021b, January 30). Circular perlin noise in p5.js | Coding Project #12 [Video]. YouTube. https://www.youtube.com/watch?v=0YvPgYDR1oM

The Coding Train. (2021a, January 26). 3.1 Angles and rotation – Nature of code [Video],3.1-3.9 . YouTube. https://www.youtube.com/watch?v=DMg-WRfNB60

Memo Akten, Mehmet Selim Akten, The Mega Super Awesome Visuals Company. (n.d.-b). Simple Harmonic Motion (2011-). Memo Akten | Mehmet Selim Akten | the Mega Super Awesome Visuals Company. https://www.memo.tv/works/simple-harmonic-motion/

https://p5js.org/reference/

 

week3 – attractors and movers

Concept:

The concept for this project was to be able to create a recognizable pattern while also using movers and attractors. I was inspired by the flower-dotted patterns as well as Dan Gries’s work. In this project, I try to integrate what we learned in class to recreate such a pattern. However, I decided to add more forces that can disturb the pattern as if it’s almost a shadow.

Highlight of some code:

For this project, I decided to build upon the practice we had in class. I initially wrote a code similar to what we did in class and then watched the nature of the code video and added elements to that. I played around with the names to see what patterns I got. After that, I decided to create some turbulence by creating a bouncing effect when the balls reached the borders of the canvas and by creating gravity between some of the particles. I had so many issues with debugging the code because I was experimenting with it mostly to create a pattern I liked. I think the most challenging part was making the different bodies work relatively in response to one another while also creating interesting shapes as they moved around the canvas. 

These images are different iterations at different stages of my code until I reach the desired result.

class Mover {
  constructor(x, y, m) {
    this.pos = createVector(x, y);
    this.vel = p5.Vector.random2D();
     // this.vel.mult(5);
    //     the acceleration will change when forces act on it
    this.acc = createVector(0, 0);
    this.mass = m;//this store mass m of mover []
    this.r = sqrt(this.mass) * 0.15; //r=sqr m * a constant this is for the radious 
  }

  applyForce(force) {
    let f = p5.Vector.div(force, this.mass);
    this.acc.add(f); //add the forces to acceleration 
  }
//   attract to one another gravity btw diff objects
  TwoAttract(mover) {
    //this.pos-mover=vector(from mover to another mover)
    let force = p5.Vector.sub(this.pos, mover.pos); // 
    let distanceSq = constrain(force.magSq(), 1000, 10000); //limit btwn 1000 and 10000
    let G = 0.2; //gravatational constant 
    let strength = (G * (this.mass * mover.mass)) / distanceSq; //f=G*(m1m2)/d'2 law of gravity 
    force.setMag(strength); //set the force and mag as calculated 
    mover.applyForce(force); //apply calculated to mover 
  }

  update() {
    this.vel.add(this.acc);
    this.pos.add(this.vel);
    this.acc.set(0, 0);
    
        // Boundary checks 
    if (this.pos.x > width) {
      this.pos.x = width;
      this.vel.x *= -1; // Reverse horizontal velocity when hitting right edge
    } else if (this.pos.x < 0) {
      this.pos.x = 0;
      this.vel.x *= -0.1; // Reverse horizontal velocity when hitting left edge
    }
    
    if (this.pos.y > height) {
      this.pos.y = height;
      this.vel.y *= -0.2; // Reverse vertical velocity when hitting bottom edge
    } else if (this.pos.y < 0) {
      this.pos.y = 0;
      this.vel.y *= -0.2; // Reverse vertical velocity when hitting top edge
    }
    // Reset acceleration to 0 after each frame
    this.acc.mult(0);
    
    
  }
  RandomColors(){
  //     colors function
   
   let R = random(90, 100); // r is a random number between 0 - 255
   let G = random(100, 120); // g is a random number betwen 100 - 200
   let B = random(150, 200); // b is a random number between 0 - 100
  return color(R,G,B);
}

  show() {
     strokeWeight(0);
    fill(this.RandomColors());
    ellipse(this.pos.x, this.pos.y, this.r * 2);
  }
}
//class to attract objects to the attractor to show how gravity is btwn 2 objects the attractor and the objects 
class Attractor {
  constructor(x, y, m) {
    this.pos = createVector(x, y);
    this.mass = m;
    this.r = sqrt(this.mass) *115;
  }

  attract(mover) {
    let force = p5.Vector.sub(this.pos, mover.pos);
    let distanceSq = constrain(force.magSq(), 1000, 1500);
    let G = 4;
    let strength = (G * (this.mass * mover.mass)) / distanceSq;
    force.setMag(strength);
    mover.applyForce(force);
  }

  show() {
  
  }
}
let movers = [];
let twoattract;
let attractor;

function setup() {
  createCanvas(600, 600);
  //movers attracted to attracor
  for (let i = 0; i < 6; i++) {
    let x = random(width / 2);
    let y = random(height / 2);
    let m = random(50, 100);
    movers[i] = new Mover(x, y, m);
  }
  attractor = new Attractor(width / 2.5, height / 2, 100);

  background(0);
}

function draw() {
  //3 movers attracted to each other
  for (let t = 0; t < 4; t++) {
    movers[t].update();
    movers[t].show();
    //attractor applies force to each other
    attractor.attract(movers[t]);

    for (let u = 0; u < 6; u++) {
      for (let other of movers) {
        if (t !== u) {
          movers[t].TwoAttract(movers[u]);
        }
      }
    }
  }

  attractor.show();
}

 

Embedded sketch:

Reflection and Future Work:

I am happy with the result of this project; however, I think there is room for improvement.  I think the forces acting on the objects and the attraction more coherent with one another to make them feel a little more pattern would create even more pleasing results. Further, I want to experiment with other shapes rather than circles maybe lines or triangles that would potential;;y create other patterns as they move.

Resources:

https://www.researchgate.net/publication/258499541_Classification_of_symmetry_groups_for_planar_n-body_choreographies

https://dangries.com/rectangleworld/demos/nBody/

https://github.com/nature-of-code/noc-book-2/tree/main/content/examples/01_vectors/example_1_9_motion_101_velocity_and_random_acceleration

https://thecodingtrain.com/tracks/the-nature-of-code-2/noc/2-forces/6-mutual-attraction

Week 2 – Rain

Your concept:

Rain always brings me peace; therefore, for this project, I created a scene of rainfall to reflect the feeling I get when it rains. I had some ideas before I settled into this one, such as sunset and sunrise, moving waves, and birds. However, replicating rainfall spoke to me the most. 

I began this assignment by reviewing the materials for this week and watching inspirational videos of things that move. Then, I read a little about the physics behind rainfall, its average acceleration relative to its size, and velocity. I had to do some conversions and basic math to make it logical on a computer screen. For the scene, I used a PNG image for the mountains, a gradient for the sky, rain audio, and some stars to enhance the theme.

Highlight of code:

I tried to make my code neat and clean for this project by creating a different class file and functions whenever possible. The most challenging part of this project was figuring out logical numbers for the line length, acceleration, and velocity so that the rain looks similar to real life. I also had some trouble figuring out how to make sure the lines were straight and not move in any other direction, so I also experimented with it.

class RainDrop {
  constructor(x, y) {
    this.position = createVector(x, y);
    // make the numbers more logical when it comes to rain in pixcels
    this.velocity = createVector(0, random(0.6, 0.9));
    this.acceleration = createVector(0.001, 0.9);
    this.length = random(10, 20);
    this.Colorstrength = random(20, 255); //form od depth
  }
  show() {
    stroke(200, this.Colorstrength);
    //     subtracting x from the length to create a vertical line if i dont it looks weird
    line(
      this.position.x,
      this.position.y,
      this.position.x,
      this.position.y - this.length
    );
  }

  update() {
    this.velocity.add(this.acceleration);
    this.position.add(this.velocity);
  }
}

 

Embedded sketch:

Future work:

While this project satisfies what I have learned so far, in the future, I want to add other factors that affect the acceleration of a raindrop, like the force of air and its density. I also want to add more depth so that the sketch resembles real life and to enhance the experience. Furthermore, I want to loop the sound and create a button for the user to have the power to play and stop the audio.

Resources:

https://hypertextbook.com/facts/2007/EvanKaplan.shtml#:~:text=When%20a%20raindrop%20falls%20to,as%20would%20any%20falling%20body.

https://hypertextbook.com/facts/2007/EvanKaplan.shtml#:~:text=When%20a%20raindrop%20falls%20to,as%20would%20any%20falling%20body.

https://p5js.org/reference/p5.Element/show/

https://p5js.org/reference/p5/lerpColor/