Midterm – A Living Network

Midterm Project Overview

For the midterm, I created a generative art system that explores the intersection of networks and organic motion. The core concept is artificial life simulation inspired by Craig Reynolds’ Boids, but also using lines between nodes as in Network Theory to generate unique paintbrush patterns.

The design goal was to make a system that feels alive rather than programmed. Rather than directly drawing static shapes, I implemented a set of biologically inspired rules such as, movement, proximity-based interactions, growth, and reproduction, allowing the artwork to emerge from these behaviors over time. The result is a visually rich, evolving network that evokes mycelial-network-like pathways, flocking organisms, and orbital motion depending on the selected mode.

The system also includes interactive features that allow the user to manipulate and explore the simulation in real-time:

  • Movement Modes: Wander, Flock, and Orbit, toggled with M, creating different emergent behaviors.

  • Color Palettes: Cycle through multiple palettes with C to alter the visual mood.

  • Growth Control: Nodes reproduce over time, with growth rate adjustable via a slider.

  • Movement Parameters: Sliders for speed, steering strength, and connection distance allow nuanced control over node behavior.

  • Mouse Interaction: Click to attract all nodes toward the cursor; press R to repel them.

  • Fullscreen and Export: Press F to toggle fullscreen, and S to save a PNG snapshot of the current artwork.

Implementation Details

Nodes Behavior:

  • Wander Mode: Nodes move using Perlin noise to simulate organic wandering.

  • Flock Mode: Nodes align and cohere with nearby nodes, creating emergent flocking patterns.

  • Orbit Mode: Nodes orbit around the canvas center with circular movement.

  • Mouse Influence: Nodes respond to attraction or repulsion forces for interactivity.

if (organisms.length < 400 && frameCount % growthSlider.value() === 0) {
  let parent = random(organisms);
  organisms.push(
    new BioNode(
      parent.pos.x + random(-20, 20),
      parent.pos.y + random(-20, 20)
    )
  );
}

I made it so that the number of organisms in the sketch grows to 400 (capped so the sketch doesn’t lag) at a rate determined by the user using the slider

Midterm Progress Milestone

At this point I was facing a problem where, for some reason, all the nodes would cluster at the top right corner, which left huge parts of the canvas uncovered. I discussed with professor Jack that it would probably be a good idea to allow the user to influence the sketch with the mouse to get better patterns, which is what I did below.

wander() {
    let n = noise(this.pos.x * 0.01, this.pos.y * 0.01, frameCount * 0.01);
    let angle = map(n, 0, 1, 0, TWO_PI * 2);
    let steer = p5.Vector.fromAngle(angle).mult(steerSlider.value());
    this.applyForce(steer);
  }
mouseInfluence() {
  if (mouseIsPressed) {
    let mouse = createVector(mouseX, mouseY);
    let dir = p5.Vector.sub(mouse, this.pos);
    dir.normalize();
    let strength = repelActive ? -0.3 : 0.3;
    this.applyForce(dir.mult(strength));
  }
}

This is how I made the nodes feel alive. These snippets show how I used noise to smoothly randomize movement and steering, but also allow for the user to intervene with external forces to influence the final piece.

for (let i = organisms.length - 1; i >= 0; i--) {
  let o = organisms[i];
  o.update();
  o.display();

  for (let j = i - 1; j >= 0; j--) {
    let other = organisms[j];
    let d = dist(o.pos.x, o.pos.y, other.pos.x, other.pos.y);

    if (d < connectSlider.value()) {
      stroke(o.color, map(d, 0, connectSlider.value(), 100, 0));
      strokeWeight(0.5);
      line(o.pos.x, o.pos.y, other.pos.x, other.pos.y);
    }
  }
}

Because I wanted to try Node-to-Node connections that I saw from my Computer Science classes, I used this nested loop to draw a line between nodes that are a certain distance from each other, depending on the user’s choice, which resulted interesting paintbrush patterns.

orbit() {
  let center = createVector(width / 2, height / 2);
  let dir = p5.Vector.sub(center, this.pos);
  let tangent = createVector(-dir.y, dir.x); // perpendicular to center vector
  tangent.setMag(steerSlider.value());
  this.applyForce(tangent);
}

This is the function the sketch switches to as one of the modes available, which I learned from The Nature of Code book. Since orbit is just a force pulling towards the center, you simply need to subtract the position vector and the center vector to get the direction of the force you need to apply.

Video Documentation
Reflection

The interactive simulation provides an engaging experience where users can explore emergent behaviors visually and through direct manipulation. Observing how small changes in parameters affect the system fosters a sense of discovery.

Future Improvements:

  • Smoother transitions between modes.

  • Adjustable node size and trail persistence for more visual variety.

  • Performance optimization for larger node counts.

References

Daniel Shiffman, The Nature of Code

Flocking simulation tutorial on YouTube by Daniel Shiffman

Craig Reynolds’ Boids

Inspirations:

    • Biological growth patterns, flocking birds, and orbital mechanics.

    • Creative coding projects on interactive generative art platforms.

  • AI Disclosure: I, of course, used AI to help me better understand and debug the complex parts of the code, especially flocking. I also asked ChatGPT to suggest the color palettes that the user can cycle between by clicking c since I am not very good at choosing colors. ChatGPT also did the html parts such as the sliders, labels, as well as the fullscreen feature.

Assignment 5

Overview and Concept 

For the midterm I want to make a generative art system that explores the intersection of networks and organic motion. The core concept is based on The Neural Kingdom: a simulation of synaptic connectivity where autonomous “cells” traverse a digital petri dish, leaving behind a complex web of connections.

The design goal was to create a system that feels “alive” rather than programmed. Instead of drawing shapes directly, I designed a set of biological rules—movement, proximity, and reproduction—and allowed the artwork to emerge from the interaction of these rules over time. The result is a series of intricate structures that resemble neural pathways.

The generator also allows cycling between different color modes by pressing ‘c’ and saves a png with ‘s’

The Technical Logic

The system is built on three primary programming pillars:

Autonomous Agents: Each cell is an object with its own mass, velocity, and “personality” (a unique noise seed). To simulate chemotaxis (movement in response to chemical gradients), I used Perlin Noise. This ensures the cells move in smooth, curving paths rather than random lines.

Proximity-Based Synapses: The “art” is generated by the space between the movers. Every frame, each cell looks for neighbors within a radius. If another cell is close enough, a line is drawn between them. I mapped the transparency of these lines to the distance, creating a sense of three-dimensional depth and tension.

Mitosis Growth Simulation: Rather than filling the screen at once, the sketch begins with a small “seed” cluster. New cells are added to the system via a mitosis timer, spawning near existing parent cells. This allows the colony to expand organically from a central core.

Current Milestone

Future Improvements:

I want to allow for different drawing modes by allowing the user to click different numbers. I also the user to be able to influence the art by interfering with external forces. This could also help with the issue of the movers not covering huge parts of the sketch, which I also intend to fix.

Assignment 4

Concept

Inspired by Memo Akten, my concept is to simulate a row of searchlights fixed along the bottom of the screen. Each light oscillates back and forth using a sine wave.

The “personality” of the piece comes from the Phase Offset. Because each light starts its swing a fraction of a second after the one next to it, the beams create a harmonic wave effect as they sweep across the sky. I used high-transparency triangles to mimic the look of light passing through fog or haze.

Memo Akten’s Simple Harmonic Motion #6 Performance proof of concept (2011)

The Sketch
  • Watch: The beams move in a synchronized wave.

  • Interact: Move the Mouse to change the “Spread” of the lights. Moving Right makes the wave more chaotic; moving Left brings them into perfect sync.

  • Click: To change the color palette from “Cold Searchlight” to “Warm Amber.”

Code Highlight

I am particularly proud of the display() function for the beams. Instead of drawing a simple line, I used a gradient-like approach with multiple triangles. This creates the “glow” effect where the light is brightest at the source and fades as it stretches into the dark.

display() {
    // We use a loop to draw layers of the beam for a glow effect
    for (let i = 8; i > 0; i--) {
      // As i gets smaller, the beam gets thinner and brighter
      let alpha = map(i, 1, 8, 15, 2); 
      let beamWidth = i * 4;
      
      fill(50, 20, 100, alpha); // Soft white/blue glow
      noStroke();
      
      push();
      translate(this.x, this.y);
      rotate(this.angle);
      
      // Draw the triangular beam of light
      triangle(0, 0, -beamWidth, -height, beamWidth, -height);
      pop();
    }
  }
Milestones

This was my first attempt. I successfully mapped the Sine wave to the rotation of a line, but it felt very mechanical and flat. There was no phase offset yet, so every light moved in perfect, boring unison.

In this version, I moved away from the basic “sticks” and made it actually look like a light. By drawing multiple overlapping triangles with very low opacity, I create the illusion of light scattering through a hazy or smoky sky. All that’s left is the interaction!

Reflection and Future Work

This assignment highlighted how much “mood” can be created with very little code. By using Simple Harmonic Motion to drive rotation and layering simple shapes with transparency, I created a complex, atmospheric environment. Future improvements:

  • Particle Haze: I would like to add tiny “dust” particles that catch the light only when the beam passes over them.

  • Sound-Reactive: Having the beams “grow” or “shrink” in length based on the volume of a music track.

  • 3D Volumetric: Moving this into a 3D space where the beams can rotate on two axes, allowing them to point toward or away from the viewer.

Assignment 3

Concept

In class, we mostly looked at one object following one attractor. For this assignment, I wanted to see what happens when multiple attractors and repulsors fight for control over thousands of tiny particles.

  • The Magnets: I placed 3 invisible “charges” on the screen. Some pull (Attractors) and some push (Repulsors).

  • The “Weavers”: These are hundreds of tiny particles that spawn, trace the invisible lines of force for a short time, and then vanish.

  • The Turbulence: To stop the pattern from looking too perfect and mathematical, I added wind using Perlin Noise. This adds a slight wobble to the particles, making the final drawing look like organic flow—like muscle fibersl

  • Interaction: Clicking the mouse adds a massive “Disruptor” force (Repulsion) that pushes the flow away from your cursor, carving a hole in the pattern.

Code Highlight

I am particularly proud of the applyBehaviors function. This is the engine that drives the movement. It calculates the “Net Force” for every particle by summing up three different influences: the structural pull of the magnets using vector subtraction, the organic flow of the Perlin noise, and the interactive push of the mouse.

applyBehaviors(magnets) {
    let netForce = createVector(0, 0);

    // A. FORCES FROM MAGNETS
    for (let i = 0; i < magnets.length; i++) {
      let m = magnets[i];
      let force = p5.Vector.sub(m.pos, this.pos);
      let dist = force.mag();
      
      // Avoid extreme forces when very close
      dist = constrain(dist, 10, 100); 
      
      // Physics: Force = Strength / Distance
      force.setMag(m.strength / dist);
      netForce.add(force);
    }

    // B. MOUSE REPULSION (Interaction)
    if (mouseIsPressed) {
      let mousePos = createVector(mouseX, mouseY);
      let mouseForce = p5.Vector.sub(this.pos, mousePos); // Points AWAY
      let dist = mouseForce.mag();
      
      if (dist < 150) {
         mouseForce.setMag(5); // Strong push
         netForce.add(mouseForce);
      }
    }

    // C. TURBULENCE (Perlin Noise)
    // Makes the lines look like wood grain or flowing water
    let n = noise(this.pos.x * 0.005, this.pos.y * 0.005);
    let angle = map(n, 0, 1, 0, TWO_PI);
    let wind = p5.Vector.fromAngle(angle);
    wind.mult(0.5); // Add just a little bit of organic chaos
    netForce.add(wind);

    this.applyForce(netForce);
  }
Milestones & Challenges

I first started experimenting with how to make a magnetic field in p5, so I made a sketch with a bunch of rods that follow the mouse which acts as a magnet. They rods created a nice magnetic field pattern which inspired the final piece.

I learned how to do this from this sketch.

After that, I began playing around with different colors and ended up deciding on this neon purple one with a dark background. Here, the mouse was the magnet, but I noticed that after some time following the mouse, the particles would all be on top of each other and show as one. Because of this, I decided to put multiple attractors, which made no two particles follow the same path.

Reflection & Future Work

This project felt like a significant step up because I was simulating a system of forces rather than just a single interaction. The result looks less like a computer simulation and more like a generative painting.

Future Ideas:

  1. Color Mapping: Map the color of the line to the stress (force magnitude) at that point. High-stress areas (near magnets) could be red; low-stress areas could be blue.

  2. Moving Magnets: Have the magnets slowly drift around the screen using sine waves, causing the pattern to shift and morph over time.

Assignment 2

Concept

For this project, I wanted to simulate how moths fly around a porch light. I noticed that real bugs are chaotic. They don’t fly in straight lines, and they are all different sizes. I created a system where every moth tries to fly toward the mouse (the light), but they are constantly pushed around by a “jitter” force that makes them flutter. I also gave each moth a specific weight where big moths are heavy and slow to turn, while small moths are hyperactive and fast.

Code Highlight

I chose this function because it acts as the “brain” of the moth. It uses a physics formula to calculate exactly how to steer toward the light. When the user clicks, the code multiplies the allowed acceleration by 5, instantly transforming the moth from a lazy flyer into a chaotic one.

seek(target) {
    // 1. Find the direction pointing to the light
    let desired = p5.Vector.sub(target, this.pos);
    desired.normalize();
    
    // 2. Set the speed: If clicking, double the top speed!
    let speedLimit = mouseIsPressed ? this.maxSpeed * 2 : this.maxSpeed;
    desired.mult(speedLimit);

    // 3. Calculate steering (Desired velocity minus Current velocity)
    let steer = p5.Vector.sub(desired, this.vel);
    
    // 4. Limit the turning power: If clicking, allow 5x more force
    let currentForceLimit = mouseIsPressed ? this.maxForce * 5 : this.maxForce;
    steer.limit(currentForceLimit); 
    
    // 5. Apply this steering force 
    this.applyForce(steer);
  }

 

Reflection and Future Work

I was surprised by how realistic the movement felt, especially when the moths reach the light source and begin orbiting around it. It created a very organic, feeling without complex animation code. For future improvements, I would like somehow make them bump into each other so they don’t overlap.

 

Mohammed – Assignment 1

Concept

My concept is “Nervous Fireflies” that implements a Levy Flight. Most of the time, the firefly hovers in a small area (small steps), but occasionally it gets startled and zooms to a new location (a very large step).

To visualize this, I mapped the “step size” to the size of the circle. When it’s hovering, it is a tiny dot. When it zooms, it expands into a large burst of light.

Code Highlight

I am proud of this section because it creates the Lévy Flight behavior without using complicated math formulas. I simply used random(100) like rolling a 100-sided die. If I roll a 1 (a 1% chance), the firefly takes a huge jump. Otherwise, it takes a tiny step.

I was also proud of the trigonometry part which is particularly thematic in situations where you want to convert an angle into cartesian coordinates.

step() {
    // CHOOSE DIRECTION
    let angle = random(TWO_PI);

    // CHOOSE DISTANCE (The Lévy Flight Logic)
    // Pick a random number between 0 and 100
    let r = random(100);

    // 2% chance of a BIG jump
    if (r < 2) {
      this.currentStepSize = random(50, 150);
    } 
    // 98% chance of a small step
    else {
      this.currentStepSize = random(2, 5);
    }

    // MOVE
    // convert angle/distance into X and Y
    let xStep = cos(angle) * this.currentStepSize;
    let yStep = sin(angle) * this.currentStepSize;
    
    this.x += xStep;
    this.y += yStep;

 

Reflection & Future Improvements

I realized that “random” doesn’t have to mean “even.” By checking if random(100) < 2, I was able to create a behavior that feels much more organic and alive than just moving randomly every frame.

For the future, I could implement:

Mouse Interaction: I want to make the firefly scared of the mouse, so if the mouse gets too close, it forces a “big jump” immediately.

Interaction Between Fireflies: Right now, the fireflies behave in isolation. It would be cool to have fireflies interact with one another using object to object communication.

Reading Reflection – Week 1

It really blew my mind to read about how scientists usually try to figure things out by breaking them into tiny pieces like atoms or cells. The author explains that you can study a single ant all you want but you will never understand how the whole colony works just by looking at that one bug. It makes me think that the connections between things are actually more important than the things themselves. I used to think science was just about memorizing parts of a system but this chapter makes it seem like the real secret is seeing how simple stuff comes together to make complex patterns.

I also think it is super interesting how the book compares nature to a computer program. It is weird to realize that totally different subjects like biology and physics are actually similar because they both follow these basic rules of logic. The idea that nature is actually kind of lazy and reuses the same simple tricks for everything from snowflakes to economic markets is a really cool way to look at the world. It makes me feel like everything is connected through these hidden patterns that we can finally see now that we have computers to simulate them.