A
Thanh Quach
Project
PROJECT BRIEF

Create a natural system simulation, considering environment, agents, and development over time. Use classes, vectors, and randomness for representation and control.

IDEAL

In my country, we call this flower Portulaca grandiflora, also known as the "10 o'clock flower" because it typically blooms most vibrantly at 10 a.m.

For my project, I want to explore how to capture this behavior by creating an interaction based on the day-night cycle. In the morning, the flower will bloom, and at night, it will close or wither. This cycle will guide the experience and reflect the natural rhythm of the flower.

PROCESS

In setting up this code, I created an interactive environment where a flower opens and closes its petals based on the mouse position, and bugs and stars respond to the time of day. I used Perlin noise to add realistic, random movement to the bugs and swaying oval, which makes everything feel more lifelike. One of the biggest challenges was getting the day-to-night transitions to look smooth. I spent a lot of time tweaking the lerpColor and transition values to make the gradient shift naturally between colors as the scene changes from day to night.

At the end, I have to ask my assistant, which the CHAT GPT to help me with this.

CODE
  let numPetals = 15; // Initial number of petals
  let radius = 50;       
  let angleOffset = 0;    

  let oval;            // Oval object
  let bugs = [];       // Array to hold bug objects
  let stars = [];      // Array to hold star objects
  let transition = 0;  // Transition value for smooth effect
  let wind;           // Wind force

  function setup() {
    const p5Container = document.querySelector("#p5-container");
    let w = p5Container.clientWidth;
    let h = p5Container.clientHeight;
    let cnv = createCanvas(w, h);
    cnv.parent(p5Container);
    cnv.style("position", "absolute");
    cnv.style("inset", 0);
    cnv.style("z-index", -1);
    noStroke();  

    // Initialize the oval object
    oval = new Oval(width / 2, height / 2);

    // Initialize a slower wind force
    wind = new Force(0.1, 0); // Reduced horizontal wind force

    // Create stars and initialize their positions and alpha values
    for (let i = 0; i < 100; i++) {
      stars.push(new Star(random(width), random(height), random(100, 255)));
    }

    // Create bugs with random initial positions
    for (let i = 0; i < 10; i++) {
      bugs.push(new Bug(random(width), random(height)));
    }
  }

  function draw() {
    // Background colors
    let bgColor1 = color(0, 102, 204); // Blue color for day
    let bgColor2 = color(0, 0, 139); // Dark blue color for night
    let yellow = color(255, 255, 0); // Yellow color for blend
    let blendedColor = lerpColor(bgColor1, yellow, transition); // Blend between blue and yellow

    // Set gradient background blending light blue, yellow, and dark blue for night
    setGradient(0, 0, width, height, lerpColor(blendedColor, bgColor2, transition), bgColor2);

    stroke(0, 128, 0);  
    strokeWeight(7);   
    line(width / 2, height / 2, width / 2, height);  

    // Central Circle
    let centerX = width / 2;
    let centerY = height / 2;
    fill(255, 204, 0);    
    noStroke();          
    ellipse(centerX, centerY, 100, 100);  

    // Calculate number of petals based on mouse position
    let mouseYNormalized = map(mouseY, 0, height, 0, 1); // Normalized mouseY value between 0 and 1
    numPetals = 15 - floor(mouseYNormalized * 15); // Reduce petals based on mouseY

    // Draw the main flower
    let flower = new Flower(centerX, centerY, numPetals, radius);
    flower.display();

    // Draw stars if it's night
    if (transition >= 1) {
      for (let star of stars) {
        star.display(); // Display each star
      }
    }

    // Update the oval's forces
    oval.applyForce(wind); // Apply wind force
    oval.update(); // Update position
    oval.display(); // Display the oval

    // Update and display each bug
    for (let bug of bugs) {
      bug.update();
      bug.display();
    }

    angleOffset += 0.03; // Update angle for flower petals

    // Update transition value based on mouse position
    if (mouseY < height / 2) {
      transition = constrain(transition - 0.01, 0, 1);  // Day transition
    } else {
      transition = constrain(transition + 0.01, 0, 1);  // Night transition
    }
  }

  // Flower class
  class Flower {
    constructor(x, y, numPetals, flowerSize) {
      this.x = x;
      this.y = y;
      this.numPetals = numPetals;
      this.flowerSize = flowerSize;
    }

    display() {
      for (let i = 0; i < this.numPetals; i++) {
        let angle = TWO_PI / this.numPetals * i + angleOffset;  
        let petalX = this.x + cos(angle) * this.flowerSize;             
        let petalY = this.y + sin(angle) * this.flowerSize;             
        fill(255, 255, 0); // Bright yellow 
        ellipse(petalX, petalY, 30, 30); // small circle (petals)
      }
    }
  }
  // Star class
  class Star {
    constructor(x, y, alpha) {
      this.position = createVector(x, y);
      this.alpha = alpha;
    }

    display() {
      fill(255, this.alpha);
      ellipse(this.position.x, this.position.y, 3, 3); // Draw each star

      // Randomly adjust the star's alpha for a twinkling effect
      if (random() < 0.02) { // 2% chance to change each frame
        this.alpha = random(100, 255); // Set a new random alpha value
      }
    }
  }

  // Bug class
  class Bug {
    constructor(x, y) {
      this.position = createVector(x, y);
      this.noiseOffsetX = random(100); // Random offset for horizontal noise
      this.noiseOffsetY = random(100); // Random offset for vertical noise
      this.size = random(5, 10); // Random size for the bug
    }

    update() {
      // Use Perlin noise for smooth movement
      let noiseValueX = map(noise(this.noiseOffsetX), 0, 1, -2, 2); // Horizontal movement
      let noiseValueY = map(noise(this.noiseOffsetY), 0, 1, -2, 2); // Vertical movement

      this.position.x += noiseValueX; // Update x position
      this.position.y += noiseValueY; // Update y position

      // Increment noise offsets
      this.noiseOffsetX += 0.01; // Adjust speed of horizontal movement
      this.noiseOffsetY += 0.01; // Adjust speed of vertical movement

      // Wrap around the edges of the canvas
      this.position.x = constrain(this.position.x, 0, width);
      this.position.y = constrain(this.position.y, 0, height);
    }

    display() {
      fill(0, 255, 0); // Green color for the bug
      noStroke();
      ellipse(this.position.x, this.position.y, this.size, this.size); // Draw the bug
    }
  }

  // Oval class
  class Oval {
    constructor(x, y) {
      this.position = createVector(x, y);
      this.angle = 0; // Initial angle for swaying
      this.amplitude = 5; // Amplitude of swaying
      this.frequency = 0.05; // Frequency of swaying
      this.noiseOffset = random(100); // Random offset for noise
    }

    applyForce(force) {
      this.position.add(force); // Apply force to the position
    }

    update() {
      // Use Perlin noise for smooth swaying
      let noiseValue = noise(this.noiseOffset) * this.amplitude;
      this.position.y += noiseValue; // Adjust the y position using noise

      // Increment noiseOffset to create animation
      this.noiseOffset += 0.01;

      // Keep the oval within the canvas boundaries
      this.position.x = constrain(this.position.x, 0, width);
      this.position.y = constrain(this.position.y, 0, height);
    }

    display() {
      fill(139, 69, 19);  // Brown color
      noStroke();          // No stroke for the oval
      ellipse(this.position.x, this.position.y, 15, 10);  
    }
  }

  // Force class
  class Force {
    constructor(x, y) {
      this.vector = createVector(x, y); // Vector representing the force
    }

    get() {
      return this.vector; // Return the force vector
    }
  }

  // Function for drawing gradient background
  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);
    }
  }
Use the mouse in the Y-axis to control
the day-night cycle.