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.

Week 4 – Assignment

My goal was to use p5.js to build a dynamic visualisation of Simple Harmonic Motion (SHM), inspired by Memo Akten. Vibrant circles oscillate vertically in the sketch, adding to its visual attractiveness with a trace effect. The intention was to play with colour and motion while capturing the essence of oscillation’s beauty.

//Assignment 4 - SP
let numCircles = 15;
let circles = [];
let amplitude = 50; // maximum displacement
let frequency = 0.08; // speed of the motion

function setup() {
  createCanvas(400, 400);
  background(0); //
  for (let i = 0; i < numCircles; i++) {
    circles.push({
      x: width / 2 + (i - numCircles / 2) * 30,
      angle: random(TWO_PI),
      color: color(random(255), random(255), random(255)),
    });
  }
}

function draw() {
  fill(0, 20); // semi-transparent black
  rect(0, 0, width, height);

  for (let circle of circles) {
    // calculate y position using sine function
    let y = height / 2 + amplitude * sin(circle.angle);

    fill(circle.color);
    noStroke();
    ellipse(circle.x, y, 30, 30);

    // increment the angle to create motion
    circle.angle += frequency;
  }
}

Highlighted Code:

One part I’m particularly proud of is the fading background, which enhances the trace effect. The use of fill(0, 50) allows for gradual fading and creates a more immersive experience as the circles move.

Reflection:

This project taught me a lot about using p5.js for visualizations. I encountered challenges with managing the transparency and ensuring the circles maintained their vibrancy against the fading background.

For future iterations, I’d like to:

  • Experiment with varying the amplitude and frequency based on user input.
  • Add interactivity, such as changing colors or shapes when the mouse hovers over the circles.
  • Explore other wave patterns beyond simple harmonic motion.

Week 3 – Assignment

Visualise slow, sine-wave courses being travelled by unseen attractors, creating faint forces comparable to whirlpools that attract and release visible particles (movers) floating around the canvas. These particles leave behind soft glowing trails, and the closer they are to the attractors, the more varied their colours are. The entire landscape has the appearance of a serene, lively garden with petals or lights floating in a mild wind.

Breakdown of the Sketch

Invisible Attractors:  These generate soft swirling forces as they follow sine-wave pathways.

The movers (or particles): Are softly shimmering as they leave traces behind and swirl around the attractors. The invisible attractors affect their movement, and the closer they are to the attractors, the more their colour changes.

Visual Appeal: The doodle has a soothing and attractive appearance thanks to its soft gradients, shimmering particles, vibrant colours, and delicate trails.

let particles = [];
let attractors = [];
let numAttractors = 3;
let numParticles = 200;

function setup() {
  createCanvas(800, 800);
  noStroke();
  
  // Create particles
  for (let i = 0; i < numParticles; i++) {
    particles.push(new Particle(random(width), random(height)));
  }
  
  // Create attractors (moving along sine waves)
  for (let i = 0; i < numAttractors; i++) {
    attractors.push(new Attractor(i * TWO_PI / numAttractors));
  }
}

function draw() {
  background(10, 10, 30, 25); // Fading background for trails
  
  // Update and display attractors
  for (let attractor of attractors) {
    attractor.move();
    attractor.display();
  }
  
  // Update and display particles
  for (let particle of particles) {
    particle.applyAttractors(attractors);
    particle.update();
    particle.display();
  }
}

// Particle class representing each mover
class Particle {
  constructor(x, y) {
    this.pos = createVector(x, y);
    this.vel = createVector(0, 0);
    this.acc = createVector(0, 0);
    this.maxSpeed = 2;
  }
  
  applyAttractors(attractors) {
    for (let attractor of attractors) {
      let force = attractor.getAttractionForce(this.pos);
      if (force) {
        this.acc.add(force);
      }
    }
  }
  
  update() {
    this.vel.add(this.acc);
    this.vel.limit(this.maxSpeed);
    this.pos.add(this.vel);
    this.acc.mult(0);
  }
  
  display() {
    let glow = map(this.vel.mag(), 0, this.maxSpeed, 150, 255);
    fill(100, 150, 255, glow);
    ellipse(this.pos.x, this.pos.y, 8, 8);
  }
}

// Attractor class
class Attractor {
  constructor(offset) {
    this.offset = offset;  // Offset for sine wave motion
    this.pos = createVector(0, 0);
    this.speed = 0.005;
  }
  
  move() {
    let time = frameCount * this.speed;
    this.pos.x = width / 2 + sin(time + this.offset) * 300;
    this.pos.y = height / 2 + cos(time + this.offset) * 300;
  }
  
  getAttractionForce(particlePos) {
    let force = p5.Vector.sub(this.pos, particlePos);
    let distance = force.mag();
    
    if (distance < 150) { // Attraction radius
      let strength = map(distance, 0, 150, 0.1, 0);
      force.setMag(strength);
      return force;
    }
    return null; // No attraction if too far away
  }
  
  display() {
    noStroke();
    fill(255, 50, 50, 50);
    ellipse(this.pos.x, this.pos.y, 20, 20); // Faint circle for visualization (optional)
  }
}

Highlights
Smooth Attractor Motion + Dynamic Glow: The sketch has a softer, more natural flow since the moving attractors follow a smooth sine-wave pattern. A lovely, gentle visual impression is produced by the glowing effect on the particles, where their brightness varies according to their velocity.

The particles’ colour changes create a mystical atmosphere as they float around, while the invisible attractors generate the whirling forces that draw the particles in and out of their orbits.

fill(100, 150, 255, glow);
ellipse(this.pos.x, this.pos.y, 8, 8);

Thoughts and Suggestions for Future Improvements:

This sketch produces a lovely, lively image that is evocative of drifting petals in a peaceful garden or fireflies. A serene image with flowing patterns is produced by the interaction of the gently lighting particles and the smooth attractor motion.

Future Improvements:

To produce a wider variety of swirling patterns, introduce more intricate movement for the attractor, such as spiral or circular routes.
To provide even more visual depth, adjust the particle’s size and glow intensity according to its distance from the attractor and speed.
For an ethereal, more immersive effect, add minor backdrop gradient transitions (from dark blue to purple, for example).

Difficulties: The task at hand involved harmonising the visual components by achieving a smooth and glowing particle motion without causing visual disarray.

End Result:

 

Week 2 – Assignment

Concept:

For this week’s assigment, I have decided to get inspired by my favorite season: WINTER (kind of ironic giving the fact that we live in Abu Dhabi). I found myself reminiscing about the snowflakes that I get to see outside my window every time I go back home.

I found this YouTube video for inspiration:

After working for a bit on it, this was the final result:

This is the code:

//SNOWFLAKES ASSIGNMENT 2
//by SP

let snowflakes = [];
let panePadding = 20;

function setup() {
  createCanvas(400, 400);
  noStroke();
}

function draw() {
  setGradient(0, 0, width, height, color(20, 29, 68), color(100, 150, 255));

  // new snowflakes
  let t = frameCount / 60;
  if (random() < 0.05) {
    snowflakes.push(new Snowflake());
  }

  for (let flake of snowflakes) {
    flake.update(t);
    flake.display();
  }

  // remove snowflakes that are off the screen
  snowflakes = snowflakes.filter((flake) => !flake.offScreen());

  drawWindowFrame();
}

class Snowflake {
  constructor() {
    this.posX = random(width);
    this.posY = random(-50, 0); // start slightly off-screen
    this.size = random(5, 12); // size of the snowflake
    this.speed = random(1, 3); // falling speed
    this.drift = random(-0.5, 0.5); // slight drift left or right
    this.angle = random(TWO_PI); // random rotation angle
    this.rotationSpeed = random(-0.01, 0.01); // slight rotation
  }

  update(time) {
    this.posY += this.speed;
    this.posX += this.drift;
    this.angle += this.rotationSpeed;
  }

  display() {
    push();
    translate(this.posX, this.posY);
    rotate(this.angle);
    drawSnowflake(this.size);
    pop();
  }

  offScreen() {
    return this.posY > height;
  }
}

function drawSnowflake(size) {
  stroke(255);
  strokeWeight(2);
  noFill();

  // star-like shapes with lines to represent a snowflakes
  beginShape();
  for (let i = 0; i < 6; i++) {
    let x = cos((TWO_PI * i) / 6) * size;
    let y = sin((TWO_PI * i) / 6) * size;
    line(0, 0, x, y);

    // small branches for each main line
    for (let j = 0.4; j < 1; j += 0.4) {
      let branchX = cos((TWO_PI * i) / 6) * size * j;
      let branchY = sin((TWO_PI * i) / 6) * size * j;
      line(
        branchX,
        branchY,
        branchX + cos((TWO_PI * (i + 0.5)) / 6) * size * 0.2,
        branchY + sin((TWO_PI * (i + 0.5)) / 6) * size * 0.2
      );
      line(
        branchX,
        branchY,
        branchX + cos((TWO_PI * (i - 0.5)) / 6) * size * 0.2,
        branchY + sin((TWO_PI * (i - 0.5)) / 6) * size * 0.2
      );
    }
  }
  endShape(CLOSE);
}

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

// draw the window frame
function drawWindowFrame() {
  fill(80, 60, 40);
  noStroke();

  let paneWidth = (width - panePadding * 3) / 2;
  let paneHeight = (height - panePadding * 3) / 2;

  rect(0, height / 2 - panePadding / 2, width, panePadding);
  rect(width / 2 - panePadding / 2, 0, panePadding, height);

  rect(0, 0, panePadding, height); // left
  rect(width - panePadding, 0, panePadding, height); // right
  rect(0, 0, width, panePadding); // top
  rect(0, height - panePadding, width, panePadding); // bottom
}

The code that I am most proud of is the one for the snowflakes. In the beginning, I just created small circles but then I decided to make them look more realistic. It took me a long time but it was worth it because it all worked out in the end.

Reflection and ideas for future work or improvements:

Overall, I think I did a good job. Of course it is not super realistic but we are slowly getting there. For the future, I would like to recreate the entire scene and add a table next to the window.

Week 1 – Reading Response

I think that Flake’s take on how simple things can generate complex outcomes is an interesting point of view which applies to evolution, learning, and adaptation. These small adjustments can eventually adapt and can result in substantial evolutionary changes in the natural world.

I think this shows that adaptation isn’t just something living creatures do. It’s a basic part of how any system works, whether that’s in nature, a society, or even in computer programs. For example, it is the same way people adjust to new technologies and learn how to use them even if they haven’t interacted with them before.

Here’s how I see it: there is a reciprocal interaction between an adaptive system and its surroundings. The system’s modifications are influenced by the environment, and those changes may have an impact on the environment as well. This reciprocal relationship is what enables everything to change and evolve throughout time, including technology, society, and the natural world.