Final Project – Understanding Deforestation

Fractals have always fascinated me. These captivating geometric patterns reveal infinite complexity as they scale, mimicking the beauty of nature in everything from clouds to plants. Initially, my project began with the goal of creating interactive fractals using p5.js, allowing users to engage with these mesmerizing patterns in real-time. However, as I delved deeper, I realized that I could harness the power of these visuals to raise awareness about a pressing environmental issue: deforestation. This journey led me to intertwine the elegance of fractals with the urgency of ecological conservation.

Project Overview

The core goal of this project has evolved from merely creating interactive fractals into crafting a visually engaging experience that emphasizes the importance of preserving our natural world. Here’s how I approached it:

Dynamic Complexity: Each mouse press increases the fractal’s depth, revealing intricate new layers, reminiscent of how our forests grow and expand.

Real-Time Updates: The instant changes in the fractals mimic the fragility of nature, responding to user interaction much like ecosystems react to human activity.

Immersive Visuals: By pairing fractals with vibrant green hues and subtle animations, I aimed to create a visually rich environment that invites exploration while reflecting the lushness of forests.

Fractal Designs

Throughout this journey, I considered various fractal types, each offering unique opportunities for interactivity while connecting back to the theme of nature:

  1. Sierpinski Triangle: This classic fractal, with its nested triangles, symbolizes the intricate balance of ecosystems, where each part contributes to the whole.
  2. Koch Snowflake: Beginning as a simple line, this fractal transforms into an elaborate shape, illustrating how deforestation can disrupt natural patterns.
  3. Tree Fractal: Inspired by the branching structures of trees, this fractal evolves into a dense network of “branches,” echoing the beauty of forests that are often threatened by deforestation.

Interactivity Features

I aimed to create an engaging user experience that encourages reflection on deforestation:

  • Mouse Press: Each click adjusts the fractal’s recursion depth, giving users control and encouraging them to explore the complexity of nature, much like understanding the complexity of ecosystems.
  • Mouse Position: As users move their mouse, aspects like color, size, and rotation change, creating an immersive experience that reflects the vibrant life found in forests.
  • Animation: Subtle movements in the fractals symbolize the dynamic nature of ecosystems, reinforcing the message that our natural world is alive and needs protection.

The goal was to create a space where the beauty of fractals serves as a reminder of the richness of nature and the urgent need to address deforestation.

The Journey to Deforestation Awareness

As my project evolved, the focus on deforestation emerged organically. The beauty of fractals, now pulsating with vibrant green particles, symbolizes lush forests and the interconnectedness of life, emphasizing what we stand to lose. This transformation has deepened my understanding of the importance of preserving our natural world.

(Draft Number 1)
The project now serves as a visual narrative, illustrating the delicate balance of our ecosystems and the threats they face. By engaging users in this interactive experience, I hope to inspire them to reflect on their relationship with nature and the impact of deforestation.

(Draft Number 2)

Project Features

The project includes several interactive elements that invite users to engage deeply with the theme of deforestation:

  • Start Screen: Users are greeted with the message, “Let’s understand Deforestation together!” along with a prominently displayed start button, creating a welcoming entry point into this vital conversation.
  • Dynamic Fractal Visualization: As users move their mouse, the fractal responds in real-time, dynamically adjusting to create a mesmerizing experience that underscores the importance of protecting our forests.
  • Earthy Background: The warm brown background symbolizes the earth, grounding the visual experience and reinforcing our connection to the land that sustains us.

Code Snippets

Setup Function

function setup() {
  createCanvas(800, 800);
  colorMode(HSB, 360, 255, 255);
  noStroke();
}

Drawing the Fractal

function draw() {
  background(139, 69, 19); // Warm brown to represent the earth
  particles = []; // Resetting particles for fresh display

  let cRe = map(mouseX, 0, width, -1.5, 1.5);
  let cIm = map(mouseY, 0, height, -1.5, 1.5);

  let juliaSet = new JuliaSet(cRe, cIm);
  juliaSet.createParticles();

  for (let particle of particles) {
    particle.update();
    particle.display();
  }
}

Particle Class

class Particle {
  constructor(x, y, cRe, cIm) {
    this.x = x;
    this.y = y;
    this.cRe = cRe;
    this.cIm = cIm;
    this.hue = random(100, 140); // Greenish tones for a natural vibe
  }

  update() {
    // Logic for particle movement and color based on iteration count
  }

  display() {
    fill(this.hue, this.saturation, this.brightness, 200);
    ellipse(this.x, this.y, 2, 2);
  }
}

Embedded Sketch and Project Outcome:

let particles = [];
const maxIterations = 100;
const numParticles = 30000; // Increased number of particles for visibility
let isStarted = false; // To track if the main visualization should start
let startButton; // Variable to hold the start button

function setup() {
  createCanvas(600, 600);
  colorMode(HSB, 360, 255, 255); // HSB for smooth natural gradients
  noStroke();

  // Create a button to start the visualization
  startButton = createButton("Start");
  startButton.position(width / 2 - 40, height / 2); // Center the button
  startButton.size(80, 40);
  startButton.style('font-size', '20px');
  startButton.style('background-color', 'white');
  startButton.mousePressed(startVisualization); // Start the visualization on press

  // Create initial particles
  for (let i = 0; i < numParticles; i++) {
    particles.push(new Particle(random(width), random(height)));
  }
}

function startVisualization() {
  isStarted = true; // Set the flag to start the main visualization
  startButton.hide(); // Hide the button after it is pressed
}

function draw() {
  if (!isStarted) {
    background(34, 139, 34); // Green background for the start page
    
    // Display the title text
    textSize(24);
    fill(255); // White text color
    textAlign(CENTER);
    text("Let's understand deforestation together!", width / 2, height / 2 - 70); // Title text
    
    // Instruction text
    textSize(20);
    text("Move around the mouse", width / 2, height / 2 + 120); // Instruction text
    return; // Stop further execution until the button is pressed
  }

  background(153, 101, 21, 200); // Brown background resembling the ground

  // Dynamically adjust Julia constants based on mouse position
  let cRe = map(mouseX, 0, width, -1.5, 1.5); // Map mouseX to real part
  let cIm = map(mouseY, 0, height, -1.5, 1.5); // Map mouseY to imaginary part

  // Update and display all particles
  for (let particle of particles) {
    particle.update(cRe, cIm);
    particle.display();
  }
}

class Particle {
  constructor(x, y) {
    this.x = x;
    this.y = y;
    this.iteration = 0; // To track the iteration count
  }

  update(cRe, cIm) {
    // Center the mapping so the fractal stays in the middle
    let zx = map(this.x, 0, width, -2, 2); // Map to complex plane
    let zy = map(this.y, 0, height, -2, 2);
    this.iteration = 0; // Reset iteration count for update

    // Iterate the Julia formula
    while (this.iteration < maxIterations) {
      let xtemp = zx * zx - zy * zy + cRe;
      zy = 2.0 * zx * zy + cIm;
      zx = xtemp;

      if (zx * zx + zy * zy > 4) break; // Escape condition
      this.iteration++;
    }
  }

  display() {
    // Use a green color palette for a microbiological feel
    let hueValue = map(this.iteration, 0, maxIterations, 100, 140); // Green to light green
    let brightness = this.iteration === maxIterations ? 0 : map(this.iteration, 0, maxIterations, 100, 255);
    let size = map(this.iteration, 0, maxIterations, 2, 6); // Increased size variation

    fill(hueValue, 255, brightness, 200); // Vibrant colors with increased opacity
    ellipse(this.x, this.y, size, size); // Dynamic particle size
  }
}

Project Reflections

Reflecting on this project brings me joy. The transformation from a simple fractal exploration to a tool for raising awareness about deforestation highlights the power of creativity in fostering understanding. It’s truly rewarding to see users engage with the project, contemplating its message and the vital need to protect our forests.

Challenges and Overcoming Them

Like any creative endeavor, this project came with its challenges. One significant hurdle was maintaining performance while increasing the number of particles in the fractal visualization. I had to optimize the code carefully to ensure a smooth user experience. Additionally, crafting a clear and impactful message about deforestation required thoughtful incorporation of user feedback, allowing me to communicate the project’s importance effectively.

Future Improvements

Looking ahead, I see several exciting possibilities for this project:

  • Educational Content: I envision adding layers of information about deforestation, including its causes and potential solutions, to deepen user understanding.
  • Enhanced Visuals: Exploring advanced graphics techniques could enrich the user experience even further.
  • Expanded Interactivity: Integrating animations or informative pop-ups could create an even more engaging learning environment.

Documentation, Images, Videos, and User Interactions

 

Final Project Draft #2

After looking more into cellular automata, I have realized that it is very restrictive and my project won’t work with it. So, I have tried to use fractals instead.

This was the result:

 

At first, I wanted one fractal that expands depending on where the mouse is, but somehow that did not work and it came out like this. I like it because it gives the “nature” vibes and it makes me think of multiple things that we find in nature.

Week #11

Introduction
Cellular automata are fascinating systems where simple rules applied to cells in a grid lead to complex and often mesmerizing patterns. While 2D cellular automata like Conway’s Game of Life are well-known, extending the concept into 3D opens up a whole new dimension of possibilities—literally! In this project, I used p5.js to create an interactive 3D cellular automaton, combining computational elegance with visual appeal.

The Project

  1. The Grid
    The automaton uses a 3D array to represent the cells. Each cell is a small cube, and the entire grid is visualized in a 3D space using p5.js’s WEBGL mode.
  2. Random Initialization
    The grid starts with a random distribution of alive and dead cells, giving each simulation a unique starting point.
  3. Rule Application
    At each frame, the automaton calculates the next state of every cell based on its neighbors. The updated grid is then displayed.
  4. Interactivity
    Using p5.js’s orbitControl(), users can rotate and zoom into the 3D grid, exploring the automaton’s patterns from different perspectives.Code

    let grid, nextGrid;
    let cols = 10, rows = 10, layers = 10; // Grid dimensions
    let cellSize = 20;
    
    function setup() {
      createCanvas(600, 600, WEBGL);
      grid = create3DArray(cols, rows, layers);
      nextGrid = create3DArray(cols, rows, layers);
      randomizeGrid();
    }
    
    function draw() {
      background(30);
      orbitControl(); // Allows rotation and zoom with mouse
      
      // Center the grid
      translate(-cols * cellSize / 2, -rows * cellSize / 2, -layers * cellSize / 2);
      
      // Draw cells
      for (let x = 0; x < cols; x++) {
        for (let y = 0; y < rows; y++) {
          for (let z = 0; z < layers; z++) {
            if (grid[x][y][z] === 1) {
              push();
              translate(x * cellSize, y * cellSize, z * cellSize);
              fill(255);
              noStroke();
              box(cellSize * 0.9); // A slightly smaller cube for spacing
              pop();
            }
          }
        }
      }
      
      updateGrid(); // Update the grid for the next frame
    }
    
    // Create a 3D array
    function create3DArray(cols, rows, layers) {
      let arr = new Array(cols);
      for (let x = 0; x < cols; x++) {
        arr[x] = new Array(rows);
        for (let y = 0; y < rows; y++) {
          arr[x][y] = new Array(layers).fill(0);
        }
      }
      return arr;
    }
    
    // Randomize the initial state of the grid
    function randomizeGrid() {
      for (let x = 0; x < cols; x++) {
        for (let y = 0; y < rows; y++) {
          for (let z = 0; z < layers; z++) {
            grid[x][y][z] = random() > 0.7 ? 1 : 0; // 30% chance of being alive
          }
        }
      }
    }
    
    // Update the grid based on rules
    function updateGrid() {
      for (let x = 0; x < cols; x++) {
        for (let y = 0; y < rows; y++) {
          for (let z = 0; z < layers; z++) {
            let neighbors = countNeighbors(x, y, z);
            if (grid[x][y][z] === 1) {
              // Survival: A live cell stays alive with 4-6 neighbors
              nextGrid[x][y][z] = neighbors >= 4 && neighbors <= 6 ? 1 : 0;
            } else {
              // Birth: A dead cell becomes alive with exactly 5 neighbors
              nextGrid[x][y][z] = neighbors === 5 ? 1 : 0;
            }
          }
        }
      }
      // Swap grids
      let temp = grid;
      grid = nextGrid;
      nextGrid = temp;
    }
    
    // Count the alive neighbors of a cell
    function countNeighbors(x, y, z) {
      let count = 0;
      for (let dx = -1; dx <= 1; dx++) {
        for (let dy = -1; dy <= 1; dy++) {
          for (let dz = -1; dz <= 1; dz++) {
            if (dx === 0 && dy === 0 && dz === 0) continue; // Skip the cell itself
            let nx = (x + dx + cols) % cols;
            let ny = (y + dy + rows) % rows;
            let nz = (z + dz + layers) % layers;
            count += grid[nx][ny][nz];
          }
        }
      }
      return count;
    }
    

    Future Enhancements

    • Custom Rules: Experiment with different neighbor conditions to discover new behaviors.
    • Larger Grids: Scale up the grid size for more complex patterns (optimize for performance).
    • Color Variations: Assign colors based on neighbor count or generation age.
    • Save States: Let users save and reload interesting configurations.

Idea for Final Project

Introduction
Fractals are captivating geometric patterns that reveal infinite complexity as they scale. These self-repeating designs occur naturally in clouds, plants, and even coastlines. But what if we could interact with these fractals in real-time? That’s the inspiration behind my project: creating fractals that evolve and respond dynamically using p5.js.

Project Overview
The goal of this project is to make fractals interactive, turning them from static patterns into a live, engaging experience. Here’s the core concept:
Dynamic Complexity: Each mouse press increases the fractal’s depth, unveiling new levels of detail.
Real-Time Updates:Changes occur instantly, making the fractals feel responsive and alive.
Immersive Visuals: By pairing the fractals with color changes or subtle animations, users can dive into a visually rich environment.

Fractal Designs
There are several fractal types I’m considering for this project, each offering unique possibilities for interactivity:
1. Sierpinski Triangle: A classic fractal where smaller triangles nest within a larger one, emphasizing geometric symmetry.
2. Koch Snowflake: A fractal that begins as a simple line but transforms into an intricate, snowflake-like shape through recursion.
3. Tree Fractal: Mimicking branching patterns found in nature, this fractal evolves into a dense network of “branches” with each click.

Interactivity Features
Here are some ways the fractals will interact with the user:
– Mouse Press: Adjusts the fractal’s recursion depth, allowing the user to control its complexity.
– Mouse Position: Alters aspects like color, size, or rotation based on where the user hovers, creating an immersive effect.
– Animation: Adding subtle movement, like rotating or growing fractals, to make them feel more dynamic.

The idea is to make fractals not only visually appealing but also engaging. With every mouse click, users get to explore a new layer of complexity, making the experience intuitive and playful. Future iterations might include:
– Dynamic Color Schemes: Colors change based on time or user input, adding vibrancy to the fractals.
– Sound Integration: Pairing fractal interactions with sound effects or ambient music for a multisensory experience.
– Multiple Fractals: Allowing users to toggle between different fractal types for variety.

Week 10 – Assignment

Concept & Inspiration

In this project, I aimed to combine p5.js with the physics simulation capabilities of Matter.js to create an interactive system where bouncing stars move within the canvas, reacting to boundaries. I wanted to explore how these two libraries could be integrated for simple but engaging visual experiences. The stars, initially inspired by the random and organic movements of celestial bodies, move around the canvas with random velocities, reacting to invisible boundaries.

My main focus was on creating something visually appealing while incorporating realistic physical dynamics. The stars move around, bounce off the walls, and continuously change their positions within the boundaries of the canvas. This combination of physics and aesthetics makes the piece both dynamic and fun to interact with.


Features

  1. Random Star Motion: The stars start with random velocities, making their motion unpredictable and engaging.
  2. Boundaries: Stars bounce off invisible walls placed around the canvas, ensuring that they stay within the visible area.
  3. Dynamic Shapes: Each star is drawn in the shape of a star polygon, and its size is randomized for variety.
  4. Physics with Matter.js: The use of Matter.js allows for a realistic simulation of bouncing behavior, including restitution (bounciness) and air resistance.

How It Works

The project uses Matter.js to handle physics simulation and p5.js for rendering and interaction. The following steps were followed to create this effect:

  • Physics Engine Setup: A Matter.js engine is created, and four boundary walls are added to the world to act as the invisible edges of the canvas.
  • Star Creation: 10 stars are generated with random sizes and positions on the canvas. They are assigned random velocities at the start, so they move in random directions.
  • Bounce Behavior: Each star is a circle in the physics world, and the restitution property gives them a bouncy behavior when they collide with walls or each other.
  • Custom Drawing: While the physics simulation treats them as circles, each star is visually represented by a custom star shape using p5.js.

Code

// Matter.js module aliases
let Engine = Matter.Engine,
    Render = Matter.Render,
    Runner = Matter.Runner,
    Bodies = Matter.Bodies,
    Composite = Matter.Composite,
    World = Matter.World,
    Body = Matter.Body; // To manipulate bodies

// Variables for engine and world
let engine, world;
let stars = [];
let walls = [];

function setup() {
  createCanvas(800, 600);

  // Create Matter.js engine and world
  engine = Engine.create();
  world = engine.world;
  
  // Disable gravity (or you can set a lower value to simulate reduced gravity)
  engine.gravity.y = 0.0;

  // Create canvas boundaries (walls)
  let thickness = 50;
  let wallTop = Bodies.rectangle(width / 2, -thickness / 2, width, thickness, { isStatic: true });
  let wallBottom = Bodies.rectangle(width / 2, height + thickness / 2, width, thickness, { isStatic: true });
  let wallLeft = Bodies.rectangle(-thickness / 2, height / 2, thickness, height, { isStatic: true });
  let wallRight = Bodies.rectangle(width + thickness / 2, height / 2, thickness, height, { isStatic: true });

  // Add walls to the world
  World.add(world, [wallTop, wallBottom, wallLeft, wallRight]);

  // Create stars with random velocities
  for (let i = 0; i < 10; i++) {
    let x = random(100, width - 100);
    let y = random(100, height - 100);
    let star = Bodies.circle(x, y, random(20, 40), {
      restitution: 0.8,  // Make the stars bouncy
      frictionAir: 0.01   // Add some air resistance to make them slow down
    });
    stars.push(star);
    World.add(world, star);

    // Apply random initial velocity to each star
    Body.setVelocity(star, {
      x: random(-3, 3), // Random velocity on X axis
      y: random(-3, 3)  // Random velocity on Y axis
    });
  }
}

function draw() {
  background(0);

  // Update the Matter.js engine
  Engine.update(engine);

  // Display the stars
  fill(255, 204, 0);
  for (let star of stars) {
    drawStar(star.position.x, star.position.y, star.circleRadius);
  }
}

function drawStar(x, y, radius) {
  push();
  translate(x, y);
  beginShape();
  let angle = TWO_PI / 5;
  let halfAngle = angle / 2.0;
  for (let a = 0; a < TWO_PI; a += angle) {
    let sx = cos(a) * radius;
    let sy = sin(a) * radius;
    vertex(sx, sy);
    sx = cos(a + halfAngle) * (radius / 2);
    sy = sin(a + halfAngle) * (radius / 2);
    vertex(sx, sy);
  }
  endShape(CLOSE);
  pop();
}

Reflection and Future Work

This project demonstrates a basic integration of p5.js and Matter.js, with the stars floating and bouncing within the defined space. Although the interaction is basic, it opens up various possibilities for future improvements:

  • Interactivity: Adding mouse or keyboard interaction could allow users to influence the stars’ motion or add new stars dynamically.
  • Forces: Introducing different types of forces like gravity or wind could add more complexity to the motion.
  • Collisions: Currently, stars only collide with the walls, but enabling star-star collision effects could add more dynamics to the system.

Week 9 – Assignment

CONCEPT

This project visualizes the development and evolution of a dynamic flocking system, inspired by the tension and release seen in the works of artists like Ryoichi Kurokawa and Robert Hodgin. The flock of boids represents the fragmentation and cohesion of elements in a system, evolving through three distinct phases: fragmentation, merging, and dissolution. Each phase builds visual and motion-based tension, leading to moments of release as the boids transition from chaotic fragmentation to harmonious merging and, finally, the serene dissolution of the system. Through the use of interactive elements like the shards, the system explores the balance between autonomy and influence, creating a dynamic visual narrative.

 CODE HIGHLIGHT

A part of the code I’m particularly proud of is how I managed the phases of transformation within the system. The boids’ behavior is influenced by the shards scattered across the canvas, which attract them during the fragmentation phase and guide their motion as they merge and dissolve. The `attractedByShards` method allows the boids to interact with these shards, adding layers of complexity to their motion. This method introduces variability and visual tension, as the boids are both influenced by the flocking rules and the external force of the shards. As the system evolves, the shards merge and disappear, leading to the dissolution phase, where boids slowly scatter and fade.

 

Here’s a snippet of the code that handles boid interaction with the shards and phase transitions:

flock(boids, shards) {
let alignment = this.align(boids);
let separation = this.separate(boids);
let shardPull = this.attractedByShards(shards);

this.acceleration.add(alignment);
this.acceleration.add(separation);
this.acceleration.add(shardPull); // Attracted to nearby shards
}

attractedByShards(shards) {
let steering = createVector();
for (let shard of shards) {
let d = dist(this.position.x, this.position.y, shard.position.x, shard.position.y);
if (d < shard.radius) {
let force = p5.Vector.sub(shard.position, this.position);
force.setMag(0.1);
steering.add(force);
}
}
return steering;
}

This part of the code ensures that boids not only follow flocking behaviors like alignment and separation but also respond to the external influence of shards, simulating the tension between independent and collective motion.

You can see the embedded sketch of the project here. It shows the flock of boids interacting with the shards and transitioning through phases of fragmentation, merging, and dissolution. The colors and behaviors of the boids change over time, reflecting the evolving state of the system.

 FUTURE ITERATIONS

In future iterations, I’d love to explore how external user input could influence the phase transitions. Perhaps users could trigger the start of a new phase or manipulate the positions of the shards. Another direction is to introduce sound to accompany each phase, further enhancing the experience of tension and release.

Additionally, I could refine the motion of the boids to create more fluid transitions, adding more intricacies to their behavior as they respond to the system’s changing environment. Another idea is to allow the flock to represent different emotional states or stories, visualized through movement and interaction with external forces like the shards.

MUJO – Reflection

From a different perspective, I truly liked how Mujo created a serene yet stimulating environment. I felt as though I could directly relate to an abstract idea like impermanence because of the piece’s tactile quality. I felt as like I was beginning something new each time I touched the surface, but I knew it wouldn’t last, which gave the entire experience a subtle sense of fragility.

This time, I found the process to be really soothing. A contemplative environment was produced by the combination of the ambient sound and the gentle pictures. It helped me realise that interactive art doesn’t have to be overpowering or high-energy; it can be delicate and captivate you gradually, which is precisely its strongest point. Mujo seemed to be encouraging me to take a moment to consider how change, even when it seems gradual, is always there. Since it made the experience more personal and reflective for me, I appreciate that feeling of calm and would love to apply it to my own work.

FlightRadar 2.0

CONCEPT

This project visualizes my travel itineraries between home (Bucharest) and NYU Abu Dhabi. Since I’ve taken this journey multiple times, I wanted to find a way to represent these flights visually, capturing the routes on a map and animating the flights in a way that reflects their random and varied nature. Using p5.js, I created a sketch where autonomous planes travel between Bucharest and Abu Dhabi, tracing their paths as they move. The project reflects my personal connection to these two cities and the significant journeys I make between them.

CODE HIGHLIGHT

One part of the code I’m particularly proud of is the way I handled the autonomous flight paths. The planes move between two coordinates (Bucharest and Abu Dhabi) with a subtle randomness. They don’t all fly in the same direction at the same time, which mimics the variability of real-life flight schedules. I also implemented a smooth transition using p5.Vector.lerp, and each flight leaves behind a trace as it travels, symbolizing the many paths I’ve taken.

Here’s a snippet of the code that makes the flights smooth and random:

class Flight {
  constructor(x1, y1, x2, y2, delay) {
    this.start = createVector(x1, y1);
    this.end = createVector(x2, y2);
    this.pos = createVector(x1, y1);
    this.speed = random(0.001, 0.002); // Slower speed for each flight
    this.t = 0; // Parameter for lerp
    this.angle = atan2(this.end.y - this.start.y, this.end.x - this.start.x); 
    this.path = []; 
    this.delay = delay; 
    this.hasStarted = false;
  }

  update() {
    if (frameCount > this.delay) {
      this.hasStarted = true;
    }
    
    if (this.hasStarted) {
      this.t += this.speed;
      if (this.t > 1) {
        this.t = 0;
      }
      this.pos = p5.Vector.lerp(this.start, this.end, this.t);
      this.path.push(this.pos.copy());
      paths.push(this.path);
    }
  }

  display() {
    if (this.hasStarted) {
      push();
      translate(this.pos.x, this.pos.y);
      rotate(this.angle); 
      imageMode(CENTER);
      image(planeImg, 0, 0, 40, 40); 
      pop();
    }
  }
}

This part of the code ensures that each plane follows its route independently, with a delay that makes the animations feel more organic and less synchronized.

You can see the embedded sketch of the project here. It shows multiple flights moving between Bucharest and Abu Dhabi at random intervals, leaving traces behind them on the map.

This project captures a very personal aspect of my life—the regular trips I take between Bucharest and NYU Abu Dhabi. In future iterations, I’d love to add more cities to the map to reflect layovers or other places I’ve visited. I’m also considering adding more interaction, allowing users to click on a city and start a flight from there or adjust the speed and appearance of the planes.

Additionally, I could incorporate real-time data, like actual flight paths or distances traveled, to make the animation even more realistic. Another idea is to create a log of these trips, showing statistics like the number of flights, total distance traveled, or the amount of time spent in the air, adding an informative layer to the artistic representation.

Midterm Progress #2 “Dynamic Hearts” – Stefania Petre

Concept

The Dynamic Hearts project aims to create an engaging visual experience through the generation of heart-shaped forms that interact with each other. This project draws inspiration from the ebb and flow of life, where hearts symbolize love, connection, and emotional depth. The hearts dynamically change in size, color, and motion based on Perlin noise, simulating a dance of souls interacting with one another, occasionally colliding and sometimes drifting apart, creating a mesmerizing display of movement and color.

Design

1. Shapes: The sketch generates multiple heart shapes whose sizes and positions are influenced by Perlin noise, resulting in smooth and organic motions. Each heart is drawn in the center of the canvas, enhancing the visual symmetry and creating a harmonious composition.

2. Color Palette: Colors are assigned dynamically based on the index of each heart, producing a gradient effect across the shapes. This choice evokes a sense of depth, movement, and emotional resonance, as colors shift and blend seamlessly.

3. Interactivity: The motion of the hearts is dictated by a combination of Perlin noise and an orbiting pattern. This feature adds a layer of interactivity, allowing viewers to experience the visual output as a living, breathing entity that shifts with the rhythm of the noise, mimicking the spontaneity of life itself.

States and Variations

The sketch can exhibit different states based on its parameters:
– The number of heart shapes can be adjusted to create a denser or sparser visual.
– Modifying the radius and orbit settings can lead to variations in the hearts’ motions and interactions, resulting in diverse visual patterns.
– The introduction of additional interactive elements, such as changing behavior through keyboard inputs or dynamically adjusting colors based on viewer interactions, can further enrich the visual experience.

Identified Risks

One of the more complex aspects of this project is managing the performance and responsiveness of the visual output, especially with a larger number of hearts. Ensuring the sketch runs smoothly without lag is essential for providing a seamless user experience.

Risk Reduction Strategies

To minimize potential risks:
– The number of heart shapes is set to a manageable level (15) to maintain performance while allowing for a visually rich experience.
– The use of `noFill()` and `strokeWeight()` enhances rendering efficiency while preserving visual quality.
– Performance will be tested across different devices to ensure responsiveness, with adjustments made based on testing results.

Process and Evolution

Starting from my initial concept of creating dynamic spirals, I aimed to design a captivating visual that responded to user input. The initial draft featured spiraling lines that changed in size and color based on mouse position. However, through a process of exploration and experimentation, I was inspired to shift my focus to heart shapes, which felt more resonant with the themes of connection and emotional depth.

The transition from spirals to hearts involved redefining the visual language of the project. I began by adapting the existing code, replacing the spirals with heart shapes that could interact in an engaging manner. By leveraging Perlin noise, I was able to create fluid and organic movements that echoed the unpredictability of human emotions and relationships. The resulting composition features hearts that move like souls, sometimes colliding and other times drifting apart, providing a poignant metaphor for our connections in life.

Final Product

In conclusion, the Dynamic Hearts project represents a culmination of my explorations in interactive art, showcasing how shapes can convey emotional narratives and foster a sense of connection through visual interaction. The final product has evolved significantly from the initial draft, transforming into a rich and engaging experience that reflects the complexity of life and love.

 

 

//Dynamic Hearts - Midterm by SP

let numShapes = 15; // Number of shapes
let shapeRadius = 100; // Distance from center
let maxRadius = 1000; // Maximum size for shapes
let angleStep = 0.02; // Speed of rotation

let noiseOffsetX1 = 0; // X-offset for Perlin noise (Group 1)
let noiseOffsetY1 = 1000; // Y-offset for Perlin noise (Group 1)

let noiseOffsetX2 = 5000; // X-offset for Perlin noise (Group 2)
let noiseOffsetY2 = 6000; // Y-offset for Perlin noise (Group 2)

let orbitRadius = 200; // Distance between the two groups

function setup() {
    createCanvas(windowWidth, windowHeight);
    noFill();
    strokeWeight(2);
}

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

    // Calculate the central orbit angle based on Perlin noise
    let orbitAngle1 = noise(noiseOffsetX1) * TWO_PI; // Group 1 orbit angle
    let orbitAngle2 = noise(noiseOffsetX2) * TWO_PI; // Group 2 orbit angle

    // Group 1 position based on orbit
    let centerX1 = orbitRadius * cos(orbitAngle1);
    let centerY1 = orbitRadius * sin(orbitAngle1);
    
    // Group 2 position based on orbit, opposite direction
    let centerX2 = orbitRadius * cos(orbitAngle2 + PI);
    let centerY2 = orbitRadius * sin(orbitAngle2 + PI);

    // Draw first group of hearts
    push();
    translate(width / 2 + centerX1, height / 2 + centerY1);
    drawShapeGroup(numShapes, noiseOffsetX1, noiseOffsetY1, shapeRadius);
    pop();

    // Draw second group of hearts
    push();
    translate(width / 2 + centerX2, height / 2 + centerY2);
    drawShapeGroup(numShapes, noiseOffsetX2, noiseOffsetY2, shapeRadius);
    pop();

    // Update Perlin noise offsets for more fluid motion
    noiseOffsetX1 += 0.01;
    noiseOffsetY1 += 0.01;
    noiseOffsetX2 += 0.01;
    noiseOffsetY2 += 0.01;
}

// Function to draw a group of hearts
function drawShapeGroup(num, noiseX, noiseY, radius) {
    for (let i = 0; i < num; i++) {
        // Dynamic position based on Perlin noise
        let noiseFactorX = noise(noiseX + i * 0.1) * 2 - 1;
        let noiseFactorY = noise(noiseY + i * 0.1) * 2 - 1;
        let xOffset = radius * noiseFactorX;
        let yOffset = radius * noiseFactorY;
        
        drawHeart(xOffset, yOffset, i);
    }
}

// Function to draw a heart shape
function drawHeart(x, y, index) {
    stroke(map(index, 0, numShapes, 100, 255), 100, 255, 150); // Dynamic color
    beginShape();
    for (let t = 0; t < TWO_PI; t += 0.1) {
        // Heart shape parametric equations with scaling factor for size
        let scaleFactor = 4; // Adjust this factor for size (increased for larger hearts)
        let xPos = x + scaleFactor * (16 * pow(sin(t), 3));
        let yPos = y - scaleFactor * (13 * cos(t) - 5 * cos(2 * t) - 2 * cos(3 * t) - cos(4 * t));
        vertex(xPos, yPos);
    }
    endShape(CLOSE);
}

// Adjust canvas size on window resize
function windowResized() {
    resizeCanvas(windowWidth, windowHeight);
}

 

Midterm Draft #1 – Stefania Petre

Concept:
The Dynamic Spirals project creates an engaging visual experience through the generation of spiraling lines that react to user input. The spirals dynamically change in size, color, and motion based on the mouse position, creating a captivating display of colors and forms.

First Draft:

 

Design:

  • Spirals: The sketch generates multiple spirals whose radii are modulated by a sine function, resulting in smooth wave-like motions. Each spiral is drawn around the center of the canvas, enhancing the visual symmetry.
  • Color Palette: Colors are assigned based on the index of each spiral, producing a gradient effect across the spirals. This choice aims to evoke a sense of depth and movement.
  • Interactivity: The maximum radius of the spirals varies according to the mouse’s X position. This feature adds a layer of interactivity, allowing users to influence the visual output directly.

States and Variations:
The sketch can exhibit different states based on user interaction:

  • The number of spirals can be adjusted to create a more dense or sparse visual.
  • Modifying the angleOffset value can lead to variations in the spirals’ animations, resulting in different visual patterns.
  • Introducing additional mouse interactions (e.g., controlling color or speed) can further diversify the visual experience.

Identified Risks:
One of the more complex aspects of this project is managing the performance and responsiveness of the visual output, especially with a larger number of spirals. Ensuring the sketch runs smoothly without lag is essential for a good user experience.

Risk Reduction Strategies:
To minimize potential risks:

  • The number of spirals is set to a manageable level (10) to maintain performance.
  • The use of noFill() and strokeWeight() is applied to enhance rendering efficiency while preserving visual quality.
  • Performance will be tested across different devices to ensure responsiveness, and adjustments will be made based on testing results.

Next Steps

  • Explore the introduction of additional interactive elements, such as changing spiral behavior with keyboard inputs or varying the colors dynamically based on user actions.
  • Experiment with adding background patterns or effects to enhance visual depth.
  • Document findings and user feedback to improve the generative art experience continuously.