Final Project

My final project places plants, seeds, and a flock of birds together in a digital meadow influenced by user interactions and variations governed by physics laws.

In this scene, “grass” blades sway in the “wind”, seeds fall to the ground and sprout into new plants, and birds navigate the environment following flocking rules. As a viewer, you influence wind direction, create rain to accelerate plant growth, and steer the bird flock with your mouse. The goal was to create a piece that feels organic and alive.

 


Seeds drift down into the scene and, once landed, germinate over time. Grass blades grow taller, especially when nourished by “rain” created by the user. Wind vectors influence the direction and sway of grass blades. Seeds fall under gravity. Birds respond to flocking forces—separation, alignment, cohesion. Instead of scripting each bird, I use the Boids algorithm to define local rules.

User Interaction

    • Wind Control: Arrow keys change wind direction, subtly shifting the meadow’s behavior.
    • Rain Creation: Clicking and dragging creates raindrops, accelerating the growth of plants.
    • Bird Guidance: Moving your mouse influences the flock’s position, inviting them to avoid or explore your cursor’s location

Future Directions

  • Adding seasonal shifts or migration patterns for birds would add further complexity and opportunities for interaction.
  • More biodiversity could make the scene richer—different plant species, insects, or small mammals.
  • Dynamic sounds (birds chirping, wind rustling) could evolve based on user inputs and environmental states.

code

IM Showcase + Updated Final

Project Used in the IM Showcase 

Updated Official Final 

Link to Sketch

As seen above, I had to use a slightly modified code for the IM showcase, where I modified the placement of the trees, the size of the birds, and the speed of the birds. Some of the modifications were due to poor planning on my end, and the modification of the speed was to compensate for my code running much slower on the iMacs. I needed to modify the other elements because I forgot to make them dynamic to the window width and window height of the screen, so I needed to modify them so they would visually translate on the big iMacs correctly. This is something I will keep in mind next time I code.

For the updates on my final code (shown and linked above), I was able to make the birds interact with the tree by stopping on the tree when the slider was at it’s lowest point (0.5) and the tree is still, and flying off the tree when the slider is adjusted past its lowest point, as the “wind” picks up and the tree moves, making the birds “fly away”. I used many if statements and Boolean values to achieve this effect. I also created two fractal trees and overlapped them to make the tree look more full; this way, the birds looked more convincing when they “landed” on the tree. Lastly, I made slight adjustments to the bird’s flocking algorithm. I wanted to have slight cohesion among the birds so that when new birds are introduced, all of the birds naturally gravitate towards a flock and form a “V” formation.

User Testing and Interactions

Link to User Testing Video

The user testing was very informative both times I did it. The first time, my project was in its very early stages, and I received feedback that my project was too simple and needed more interaction. From there, I added more ways for the user to interact. When I watched users navigate my project at the IM showcase, I realized that one interactive element that I added was not clearly defined, but the users were able to figure it out eventually. This element was the trigger that makes the birds “fly away” from the tree. The users were quick to notice the condition for the birds to land on the tree, but much slower to figure out how the birds leave the tree. Some users tried clicking the screen or the tree, and some users wondered out loud what the slider is representing. To make this more clear, I’m considering adding text that says “wind” above the slider. All the other elements were navigated easily, and I was told the project looked aesthetic and peaceful, emulating the experience of bird watching.

Reflections

I am proud of the work I did, as well as being able to incorporate so many of the new things I learned from class. With more time, I would add more elements to the sketch, such as more trees, other types of birds, and different weather conditions. I would also add nature sounds, and an end button for the user to stop the simulation. I greatly enjoyed this class and its teachings!

Week 11 Assignment

These systems, where each cell in a grid evolves based on the state of its neighbors, have inspired mathematicians, artists, and technologists alike. From Conway’s Game of Life to modern generative art, cellular automata demonstrate how emergent behaviors can create visually captivating and intellectually intriguing works.

In this project, I designed an interactive cellular automata sketch using p5.js. The goal was to create a dynamic canvas where users can experiment with CA rules, colors, and patterns in real-time. The grid evolves based on Conway’s Game of Life rules: cells are born, survive, or die based on the number of active neighbors. However, the interactivity takes the experience further, allowing users to influence the automata by adding or removing cells, toggling simulation states, and randomizing initial conditions. This makes the system not only generative but also deeply personal and exploratory.

What makes this project exciting is the ability to tweak parameters and immediately see their impact. Users can modify grid size, experiment with different initial states, and even create patterns manually by dragging their mouse. The vibrant, gradient-based color scheme adds an artistic dimension, transforming mathematical rules into dynamic visual expressions. Cellular automata are not just about abstract computation—they’re about discovering the unexpected beauty that emerges from simplicity.

Code

Week 10 Assignment

Using the Matter.js library, I built an interactive physics playground that explores two fundamental aspects of physics simulation: applying forces and handling collision events. This project not only demonstrates the core capabilities of Matter.js but also creates a visually engaging and interactive experience.

The centerpiece of the playground is a controllable cube that reacts to user input. Using the arrow keys, you can push this object in different directions, applying realistic forces that accelerate and decelerate its motion. Around this object, other bodies fall from above, bouncing off surfaces and interacting with the central body. These falling objects change color upon collision, visually signaling their interaction. Static boundaries define the edges of the world, ensuring objects remain within the canvas while responding dynamically to gravity, friction, and restitution.

What makes this project exciting is its interactive nature. By combining user-applied forces and collision events, it demonstrates the responsiveness of Matter.js and highlights how even simple rules can result in rich, emergent behaviors. The physics playground is more than just a demonstration—it’s an example of how computational physics can create engaging, real-time experiences that blend logic with creativity.

Code

 

Week 9 Flocking Assignment

Inspired by the works of Robert Hodgin and Ryoichi Kurokawa, I designed an evolving flocking system that transitions through three distinct phases: chaos, development, and harmony. Each phase reflects a shift in behavior and mood, creating a dynamic visual narrative that explores tension and release. The boids (agents) begin in disarray, move toward alignment, and ultimately flow as a unified, harmonious whole.

This evolution is driven by balancing three simple flocking behaviors: alignment, cohesion, and separation. During the chaotic phase, separation dominates, creating erratic and fragmented movement. As the system develops, the forces balance, bringing order and shared direction. In the final phase, alignment and cohesion prevail, resulting in fluid, synchronized motion. These transitions add rhythm to the system, reflecting larger themes of growth, collaboration, and transformation.

The aesthetic choices amplify the emotional arc. Colors evolve from bold reds to glowing cyan, trails highlight the continuity of motion, and forms shift from sharp to soft as the flock becomes more cohesive. Inspired by Hodgin’s focus on emergence, the system reveals how complexity and beauty arise from simplicity. It’s a meditation on movement, unity, and the harmony that comes from finding balance.

code

Final Project: H A V O C

Concept:

Rethinking this project, I moved away from one specific requirement of the assignment just to satisfy the artist in me. We were asked to give users the freedom of interactivity. However, as a strong believer that art always belongs to the artist and they have more right to control their art, I created H A V O C.

H A V O C is a project that starts by motivating the user to build large terrains, in this case, big mountains, hoping to create the illusion that the user has control. After 10 seconds, the terrain switches into chaos mode and ruins what the user created. This happens for three rounds. Once it’s done, a very short video plays as an open question or an open-ended discussion.

Coding Concepts:
  • WEBGL
  • Terrains: 
    • Object-Oriented Programming
    • Forces
    • Oscillation
    • Fractals
  • Sky:
    • Particle System
    • Noise-based

 

Highlight of my code

class Terrain {
  constructor(cols, rows, scl, w, h) {
    this.cols = cols;
    this.rows = rows;
    this.scl = scl;
    this.w = w;
    this.h = h;
    this.terrain = [];
    this.controlledTerrain = [];
    this.flying = 100;
    this.noiseFactor = 0.1;
    this.waveSpeed = 0.05;

    // Initialize both terrain heights to 0
    for (let x = 0; x < this.cols; x++) {
      this.terrain[x] = [];
      this.controlledTerrain[x] = [];
      for (let y = 0; y < this.rows; y++) {
        this.terrain[x][y] = 0; // Original terrain for water effect
        this.controlledTerrain[x][y] = 0; // Controlled terrain for user interaction
      }
    }
  }

  updateTerrain(isChaosMode) {
    if (isChaosMode) {
      this.waveSpeed = 0.01; // Faster wave speed in chaos mode
    } else {
      this.waveSpeed = 0.008; // Slower wave speed in normal mode
    }

    this.flying -= this.waveSpeed; // Animate the noise with speed for chaos effect
    let yoff = this.flying;

    // Generate noise-based water effect (waving surface) with more momentum and bigger waves in chaos mode
    for (let y = 0; y < this.rows; y++) {
      let xoff = 0;
      for (let x = 0; x < this.cols; x++) {
        let noiseValue = noise(xoff, yoff);

        if (isChaosMode) {
          this.terrain[x][y] = map(noiseValue, 0, 1, -120, 200); // Increase amplitude for bigger waves
        } else {
          this.terrain[x][y] = map(noiseValue, 0, 1, -30, 60); // Calm small waves
        }

        // Destruction logic: Collision of large waves with high terrain
        let waveHeight = this.terrain[x][y];
        if (waveHeight > 50 && this.controlledTerrain[x][y] > 50) { // When both wave and terrain are high
          // Destroy or lower the terrain if wave height is large
          this.controlledTerrain[x][y] = lerp(this.controlledTerrain[x][y], waveHeight, 0.05); // Smooth destruction
        }

        xoff += this.noiseFactor;
      }
      yoff += 0.05; // Adjust frequency for both modes
    }

    if (isChaosMode) {
      // Drop the land terrain in chaos mode (simulating destruction or sudden collapse)
      for (let x = 0; x < this.cols; x++) {
        for (let y = 0; y < this.rows; y++) {
          if (this.controlledTerrain[x][y] > 0) {
            this.controlledTerrain[x][y] -= 0.5; // Gradually drop terrain height
          }
        }
      }
    }
  }

  displayTerrain(isChaosMode) {
    // Loop through the terrain and apply colors based on height
    rotateX(PI / 3);
    translate(-this.w / 2, -this.h / 2);

    // Draw the original terrain with colors based on height
    for (let y = 0; y < this.rows - 1; y++) {
      beginShape(TRIANGLE_STRIP);
      for (let x = 0; x < this.cols; x++) {
        let height = this.terrain[x][y];
        let colorValue;

        if (isChaosMode) {
          // Chaos mode - Apply bigger waves and faster momentum
          colorValue = height > 0 ? lerpColor(color(54, 54, 117), color(11, 11, 71), map(height, 0, 100, 0, 1)) : color(30, 30, 80); // Dark blue for water
        } else {
          // Normal mode colors
          colorValue = height > 0 ? lerpColor(color(19, 90, 197), color(34, 139, 34), map(height, 0, 100, 0, 1)) : color(19, 90, 197); // Blue for water
        }

        fill(colorValue);
        stroke(0);  // Keep the terrain strokes black
        strokeWeight(1); // Set stroke thickness
        vertex(x * this.scl, y * this.scl, this.terrain[x][y]);
        vertex(x * this.scl, (y + 1) * this.scl, this.terrain[x][y + 1]);
      }
      endShape();
    }
  }

  updateControlledTerrain(xIndex, yIndex) {
    if (xIndex >= 0 && xIndex < this.cols && yIndex >= 0 && yIndex < this.rows) {
      this.controlledTerrain[xIndex][yIndex] += 7; // Increase height when dragging
      this.controlledTerrain[xIndex][yIndex] = constrain(this.controlledTerrain[xIndex][yIndex], -100, 200); // Constrain height
    }
  }

  displayControlledTerrain(isChaosMode) {
    for (let y = 0; y < this.rows - 1; y++) {
      beginShape(TRIANGLE_STRIP);
      for (let x = 0; x < this.cols; x++) {
        let height = this.controlledTerrain[x][y];
        let colorValue;

        if (isChaosMode) {
          colorValue = lerpColor(color(255, 204, 0), color(255, 165, 0), map(height, 0, 100, 0, 1)); // Yellowish-orange for controlled land
        } else {
          colorValue = color(34, 139, 34); // Green for controlled land
        }

        fill(colorValue);
        stroke(0);  // Keep the terrain strokes black
        strokeWeight(1); // Set stroke thickness
        vertex(x * this.scl, y * this.scl, this.controlledTerrain[x][y]);
        vertex(x * this.scl, (y + 1) * this.scl, this.controlledTerrain[x][y + 1]);
      }
      endShape();
    }
  }
}

 

Embedded sketch:

 

Sketch link:

https://editor.p5js.org/mariamalkhoori/sketches/TSxf20kTR

User Testing Feedback:

Both people’s comment were that that they were a little confused with the interactions and its laggy on their device.

 

IM Showcase:

https://youtube.com/shorts/pRWZ8Rjyd3c

Parts I’m proud of:

Switch between normal and chaos mode

if (isChaosMode) {
  this.waveSpeed = 0.01;
  this.terrain[x][y] = map(noiseValue, 0, 1, -120, 200);  // Larger waves
} else {
  this.waveSpeed = 0.008;
  this.terrain[x][y] = map(noiseValue, 0, 1, -30, 60);  // Calm waves
}

 

Challenges and Areas for improvement:

Challenges:
This was my first time working with 3D using WebGL, and it was challenging to incorporate 2D elements, such as text, into the 3D environment.
Additionally, adding the video at the end was difficult, and I faced issues with the timer, which kept running continuously.

Improvements:
I had plans to add sound effects, but the complexity of the particle system made it difficult to implement.
I also wanted to create a more polished display screen for better user interaction.
The timer should start when the start button is pressed, but it only begins when the page refreshes.
Finally, I wanted the “Return” button to restart the entire sketch, but this functionality wasn’t working as expected.

References:
  • https://www.youtube.com/watch?app=desktop&v=ELpZW62HGVs&t=3100s&ab_channel=TheCodingTrain
  • https://www.youtube.com/watch?v=fN0Wa9mvT60&ab_channel=PattVira
  • https://editor.p5js.org/dlatolley/sketches/Bu3JRNqgF

Final Project

Concept and Artistic Vision

For my final assignment, I knew I wanted to incorporate many of the concepts we learned in class. I decided to use Perlin noise for the background to create realistic moving clouds, and use a class to create birds and control their movement using flocking methods such as alignment and cohesion. Additionally, I used fractals to create a tree.

To create the birds, I was greatly inspired by the image above. I used quadraticVertex to create a simple, but effective, silhouette. From here, I created a changing sky to make the sketch more dynamic and interactive. For the tree, I changed the radians value to make it more asymmetrical and realistic. Next, I applied a wind force to the birds, sky, and tree. When the user moves the slider towards the right, the clouds move faster, the birds flap more and fly slower, showing that they are struggling against the wind. Additionally, the tree sways slightly. there is a gradual change in all three elements when the slider is moved to the left, and when it is all the way left, the tree is completely still, the clouds move slowly, and the birds “soar” through the air. Finally, I figured out a way to make the birds interact with the tree. When the birds flying into the tree they “land” and “sit” on the tree. I added a “Birds” button for the user to introduce new birds into the environment.

Here is the inspiration, taken from a previous sketch I did for this class, for the background.

Embedded Sketches

Draft 1

Draft 2

Final Result 

User Testing

I asked a friend to test the second draft I linked in this post, and they found it to be a bit simple. When they were testing, they thought the birds would interact with the tree. From their feedback, I implemented the interaction with the tree. Other than that, I was told the buttons and the slider were very straightforward. However, since making the final version, I think the slider may be less accessible because the user has to click to generate more birds and also use the slider to modify the birds. I think a fix for this could be making a key-bind for the bird generation.

C0ding Translation and Logic

class Bird {
  constructor(x, y, birdSize, birdSpeed) {
    this.x = x;
    this.y = y;
    this.birdSize = birdSize;
    this.baseSpeed = birdSpeed;
    this.flapAngle = 0;
    this.flapSpeed = random(0.16, 0.18);
    this.stopped = false;
    this.flockingRadius = 100; // Radius within which birds influence each other
    this.wingOffset = random(0, TWO_PI); // Add an offset to stagger the wing flap
  }

  update(windFactor) {
    if (!this.stopped) {
      this.x -= this.baseSpeed / windFactor; // Birds move left
      if (this.x <= 100) {
        this.stopped = true; // Stop the bird when it reaches the tree
        // Add a small random offset to the x position to make birds stop at varied locations
        this.x = 100 + random(0, 70);
      }
      this.flapAngle += this.flapSpeed * windFactor;

      // Apply subtle flocking behavior if there are multiple birds
      if (birds.length > 1) {
        let cohesionForce = createVector(0, 0);
        let count = 0;

        for (let otherBird of birds) {
          if (otherBird !== this) {
            let d = dist(this.x, this.y, otherBird.x, otherBird.y);
            if (d < this.flockingRadius) {
              cohesionForce.add(createVector(otherBird.x - this.x, otherBird.y - this.y));
              count++;
            }
          }
        }

        if (count > 0) {
          cohesionForce.div(count); // Average position of nearby birds
          cohesionForce.setMag(0.3); // Increase the magnitude for more noticeable movement
          this.x += cohesionForce.x;
          this.y += cohesionForce.y;
        }
      }
    }
  }

  show() {
    push();
    translate(this.x, this.y);
    scale(this.birdSize);
    fill(0, 0, 0, 220);
    if (this.stopped) {
      this.drawSittingBird();
    } else {
      this.drawFlyingBird(sin(this.flapAngle + this.wingOffset));
    }
    pop();
  }

  drawFlyingBird(curveFactor) {
    let wingY = map(curveFactor, -1, 1, 0, -35);
    let wingCurve = map(curveFactor, -1, 1, -25, -50);

    // Left wing
    beginShape();
    vertex(0, 0);
    quadraticVertex(-25, -10, -35, wingY);
    quadraticVertex(-25, wingCurve, 0, 0);
    endShape(CLOSE);

    // Right wing
    beginShape();
    vertex(0, 0);
    quadraticVertex(40, -15, 70, wingY);
    quadraticVertex(40, wingCurve, 0, 0);
    endShape(CLOSE);
  }

  drawSittingBird() {
    // Only draw the right wing in the sitting position
    let wingY = 35; // Downward position (mirroring the wings up)
    let wingCurve = 50; // Gentle downward curve (mirroring wings up)

    // Right wing
    beginShape();
    vertex(0, 0);
    quadraticVertex(40, 15, 70, wingY);
    quadraticVertex(40, wingCurve, 0, 0);
    endShape(CLOSE);
  }
}

The Bird class initializes the bird’s properties, such as its position, size , and base movement speed . The bird’s wings are animated through the flapAngle, which starts at 0, and flapSpeed, which is randomly set between 0.16 and 0.18, simulating the rate of wing flapping. The flockingRadius defines how close other birds need to be for the bird to be influenced by their positions, and wingOffset staggers the wing flapping of different birds, creating a more natural look in a group of birds. The update method updates the birds position as it moves depending on the windFactor, which is calculated through the slider. I added cohesionForce so if the birds are in a certain radius of one another, the birds begin to group. In the show method, I used quadratic Bezier curves, giving the wings a smooth, realistic shape. For when the birds reach the tree, only their right wing is rendered to create a sitting position.

Challenges

I struggled with handling all the different elements of my code, and with certain elements clashing/interfering with each other. This meant I had to debug very slowly with the console log. The biggest challenge was creating the birds and their movements, as I wanted them to look as realistic as possible.

Future Improvements 

In the future, I would have perfected parts of the code more. The birds just stop when they reach the tree, so when the tree is moving, the birds do not move with it. I wasn’t sure how to approach troubleshooting this, but I think I would have to do much more complicated boundary definitions for the tree. Additionally, I tried to create a “stop watching” button to stop the simulation and go back to the start screen, but this was interfering with different elements of my code, so I deleted it. I also tried really hard for one of the buttons, such as the “day” or “night” button to cause the birds in the tree to resume flying. Nothing I tried worked. I feel like as the simulation goes on, the birds are too clustered on the tree. Another improvement I would make is add music or nature sounds to the background.

Final Project – Pearls

Concept:

My concept is to create a playful pearl diving experience where the user is collecting pearls with their index finger.

Sketch:

Code Logic:

  • Background: I created a gradient animated background to give an underwater sea effect.
  • Flock: I added a school of fish to make the sketch more vibrant and dynamic.
  • Pearls: The pearls are randomly added to any position in the canvas, the contain function is used to check whether the finger has touched it or not so that it can pop it and place another one.

Concepts used from class:

  • Particle systems (flow field)
  • Oscillation
  • Autonomous agents (flocking)

Challenges:

I faced some challenges as I initially wanted the background to just be some waves created by the flow field, but everything else was also fading out along with it so it did not work out as I wanted. Instead, I created the gradient background to show a more underwater sea effect.

// Gradient Background
  for (let i = 0; i < height; i++) {
    let inter = map(i, 0, height, 0, 1);
    let c = lerpColor(color(0, 30, 100), color(0, 155, 255), inter);
    // Add slight bg with the 'sin' function based on time
    c = lerpColor(c, color(0, 0, 100), sin(bg * 0.01 + i * 0.05) * 0.1);
    stroke(c);
    line(0, i, width, i);
  }

  // Create an ocean floor wave pattern that moves over time
  stroke(255, 255, 255, 50);
  beginShape();
  for (let x = 0; x < width; x++) {
    let y = height * 0.75 + sin(TWO_PI * 0.002 * x + bg * 0.05) * 10;
    // vertex(x, y);
  }
  endShape();

  // Increment time for the bg
  bg += 1;

User testing:

Most of the comments I got were regarding the scale of everything. As I was working on a 400×400 canvas, I had not realized everything looked so small. When it was set to fullscreen I had to make some changes to the pearl size and the finger circle. I also made a few more changes when I saw it on the iMac since they still appeared small. Overall, the users really enjoyed playing the game and kept going to make a new high score for themselves.

IM Showcase:

IMG_6642

IMG_6639

References:

Future Improvements:

For future improvements, I would add more sea elements to enhance the visuals and some pearl shells that open and close to make the game more challenging.

Final Post – Digital Bonsai

Generating Digital Bonsai: A Journey into Procedural Art Generation

Digital art has always fascinated me, especially when it intersects with the delicate world of generative design. Thus, I want to share my journey of creating a procedural bonsai tree generation system that blends computational creativity with the subtle art of nature.

The Concept: Algorithmic Nature

The project began with a simple yet ambitious goal: to create a digital bonsai that could be generated with a single mouse click. Unlike traditional digital art methods, this approach uses complex algorithms to simulate the organic growth and intricate details of a living tree.

Key Algorithmic Innovations

  1. Bezier Curve Branch Generation: The heart of the system lies in using Bezier curves to create naturally curved branches. By carefully calculating control points, we simulate the unpredictable growth patterns of real trees.
  2. Recursive Branch Creation: Each branch can spawn child branches with decreasing complexity, mimicking how real trees develop their intricate structures.
  3. Procedural Leaf Placement: Leaves aren’t simply placed arbitrarily. The algorithm ensures they’re distributed naturally, with minimal overlap and variation in size and angle.

Technical Challenges

Creating a realistic tree generation system was no small feat. Some of the most significant challenges included:

  • Performance Optimization: Generating complex tree structures with multiple branches and leaves requires careful computational management.
  • Natural Randomness: Ensuring that randomness feels organic and not mechanically generated.
  • Detailed Rendering: Creating subtle details like wood grain, branch tapering, and leaf variations.

Notable Features

Sophisticated Branch Rendering

The Branch class is a marvel of computational design. It doesn’t just draw lines but creates:

  • Gradient-based wood coloration
  • Varying branch widths
  • Subtle wood texture details
  • Smooth, organic branch curves

Environmental Context

The program doesn’t just generate a tree in isolation. It creates a complete scene with:

  • A textured mountain background
  • A detailed ceramic pot
  • A noise-based background that adds depth and atmosphere

Interactive Generation

With a simple mouse click, users can generate entirely unique bonsai trees, making each creation a one-of-a-kind digital artwork.

The Beauty of Procedural Art

What makes this project special is how it bridges computational thinking with artistic expression. Each generated tree is unique, yet follows rules that make it feel natural and alive.

The code uses techniques like:

  • Noise-based color generation
  • Bezier curve mathematics
  • Recursive algorithms
  • Randomness with controlled constraints

Technical Highlights

// Example of branch generation logic 
generateChildBranches(depth) { 
if (depth <= 0) { 
this.generateLeaves(); return; 
} // Probabilistic branch creation 
const rand = random(1); 
const numBranches = rand < 0.02 ? 3 : rand < 0.16 ? 2 : rand < 0.4 ? 1 : 0; 
}

 

This snippet shows how even branch creation follows probabilistic rules, ensuring variety without complete randomness.

Conclusion

Procedural art generation is more than just creating images—it’s about encoding the complexity of natural systems into mathematical and computational frameworks. This bonsai generation system is a testament to how code can capture the essence of organic growth.

Whether you’re a programmer, an artist, or simply someone who appreciates the intersection of technology and nature, I hope this journey through algorithmic bonsai generation has inspired you to see code as a creative medium.