Week 3 – Supernova

Inspiration

The inspiration for this sketch is a supernova. A supernova is a powerful and luminous explosion that occurs at the end of a star’s life cycle. It marks the catastrophic death of a star, resulting in the release of an immense amount of energy and light, often outshining entire galaxies for a brief period.

 

 

 

 

Sketch

Highlight of Code

let stars = [];
let numStars = 5000; 
let attractor; 
let state = 'attract'; 
let attractionStrength = 0.1; 
let explosionSpeed = 2; 
let explosionStartFrame; 
let damping = 0.95;

// array of possible attractor positions
let attractorPositions = [];
let attractorIndex = 0; 

function setup() {
  createCanvas(800, 800);
  
  // define the attractor positions: center, 1/4, and 3/4
  attractorPositions = [
    createVector(width / 2, height / 2), // center
    createVector(width / 4, height / 4), // top-left (1/4)
    createVector(3 * width / 4, 3 * height / 4) // bottom-right (3/4)
  ];
  attractor = attractorPositions[attractorIndex]; 

  // initialize stars with random positions 
  for (let i = 0; i < numStars; i++) {
    let x = random(width);
    let y = random(height);
    stars.push(new Star(x, y));
  }
}

function draw() {
  background(0);

  // updates stars based on the current state
  for (let star of stars) {
    if (state === 'attract') {
      star.attractToCenter(attractor, attractionStrength);
    } else if (state === 'explode') {
      star.attractToCenter(attractor, -1 * attractionStrength); 
    }
    star.display();
  }


  if (state === 'attract' && stars.every(star => star.isAtCenter())) {
    state = 'explode';
    explosionStartFrame = frameCount;
  } else if (state === 'explode' && frameCount > explosionStartFrame + 180) {
    state = 'attract';
    changeAttractorPosition(); // change attractor position after each explosion
  }
}

// change the attractor's position
function changeAttractorPosition() {
  attractorIndex = (attractorIndex + 1) % attractorPositions.length; // cycle through positions
  attractor = attractorPositions[attractorIndex];
}




// star class
class Star {
  constructor(x, y) {
    this.position = createVector(x, y);
    this.initialPosition = createVector(x, y);
    this.velocity = createVector(0, 0);
    this.exploding = false; 
    this.distanceToCenter = p5.Vector.dist(this.position, attractor); // initial distance to center
  }

  // attracts/repulse to/from the center 
  attractToCenter(center, attractionStrength) {
    let force = p5.Vector.sub(center, this.position);
    force.normalize();
    force.mult(attractionStrength);
    this.velocity.add(force); // add the positive attraction force to the velocity
    this.velocity.mult(damping); // apply damping to gradually slow down stars as they approach the center
    this.position.add(this.velocity);
    this.distanceToCenter = p5.Vector.dist(this.position, center); 
  }

  // check if the star is close to the center
  isAtCenter() {
    return this.distanceToCenter < 5; 
  }
  
  display() {
    noStroke();
    fill(255, 204, 0); // yellow color for the stars
    ellipse(this.position.x, this.position.y, 5);
  }
}

The full code for the project is attached above. The hardest part was getting the attractor right. I’m using the attractor as a repulsor with a negative attraction strength which makes the force vetro opposite and that was challenging to implement at first. I had also to include a dumping factor to make the simulation go smoothly otherwise the force will just pull all the stars to the attractor and repulse them because the vector becomes negative. For instance, changing the dumping to one makes the simulation an infinite loop of attraction and repulsion without any obvious explosion.

Week 2 – Assignment

The Concept

The concept for this project was to create a rain simulation with lightning. Rain is not something we experience here usually but we can simulate it. I don’t have a specific video or picture in mind; I just went to p5 to simulate raindrops, ripples, and lightning.

Code Highlight

function generateLightningBolt() {
lightningBolt = [];
let x = random(width * 0.25, width * 0.75);
let y = 0;
lightningBolt.push(createVector(x, y));
for (let i = 0; i < 8; i++) {
x += random(-30, 30);
y += random(30, 60);
lightningBolt.push(createVector(x, y));
}
}
function drawLightningBolt() {
stroke(200, 200, 255);
strokeWeight(4);
noFill();
beginShape();
for (let i = 0; i < lightningBolt.length; i++) {
vertex(lightningBolt[i].x, lightningBolt[i].y);
}
endShape();
}
function drawSparkles() {
noStroke();
for (let i = 0; i < 20; i++) {
let sparkleX = lightningBolt[lightningBolt.length - 1].x + random(-40, 40);
let sparkleY = lightningBolt[lightningBolt.length - 1].y + random(-40, 40);
fill(255, 255, 255, random(150, 255));
ellipse(sparkleX, sparkleY, random(3, 8), random(3, 8));
}
}

The part of the code that I’m proud of is the lighting. The lighting has two elements, the bolt and the sparkles. For the bolt, I created a random vector starting from a random position at the top of the screen and started adding random values from fixed intervals to that random vector’s coordinates to get the next subsequent random vectors for the zigzag-like pattern of the lighting bolt. Finally, I plotted those vectors by converting each point to a vertex and using the begin and end shape functions to connect those vertices. For the sparkles, I used 20 ellipses the last vertex of the lightning bolt. Each ellipse has a random major and minor axis length fixed between two values.

Sketch

Future Improvements and Reflection

The simulation is a basic representation of rain with a thunderstorm. However, adding sound might make the experience full and realistic. The sounds are mainly for the rain, ripples, and thunder.

Week 1 – Dynamic Random Walker

This project explores a unique blend of randomness and user interaction through a dynamic color random walk. The random walker is designed with two key features that make its movement and visual output both unpredictable and engaging.

Key Features:

  1. Biased Random Movement:
    • The random walk incorporates a 40% probability of moving towards the direction of the user’s mouse, introducing an element of interactivity. The remaining 60% of the time, the walker moves in a random direction. However, this randomness is also biased; the walker does not have an equal probability of moving left, right, up, or down, which introduces subtle variations in the walker’s path and adds complexity to its movement.
  2. Dynamic Color Evolution:
    • The visual aspect of the walker’s path is enhanced by a dynamic color mapping that evolves as the walker moves across the canvas. The color of each point is determined by its x coordinate, creating a spectrum of hues that shift horizontally across the canvas. Simultaneously, the brightness of each point is mapped to the y coordinate, resulting in a gradient that changes vertically. This color evolution adds a rich, visual narrative to the walker’s journey.
  3. Spatial Harmony:
    • To ensure the visual clarity of the path, the walker only draws a point if it has moved a minimum distance from its previous position. This spacing prevents the points from overlapping, creating a more aesthetically pleasing and well-defined pattern.

Code

let walker;
let stepSize = 8;  
let minDistance = 30; 

function setup() {
  createCanvas(600, 600);
  walker = new ColorWalker();
  background(0); 
}

function draw() {
  walker.step();
  walker.render();
}

class ColorWalker {
  constructor() {
    this.x = width / 2;
    this.y = height / 2;
    this.prevX = this.x;
    this.prevY = this.y;
  }

  step() {
    let direction = random(1) < 0.3 ? 0 : 1;
    let moveX = 0;
    let moveY = 0;

    if (direction === 0) {
      if (mouseX > this.x) {
        moveX = stepSize;
      } else if (mouseX < this.x) {
        moveX = -stepSize;
      }

      if (mouseY > this.y) {
        moveY = stepSize;
      } else if (mouseY < this.y) {
        moveY = -stepSize;
      }
    } 
    else {
      let choice = random(1);
      if (choice <= 0.4) {
        moveX = stepSize;
      } else if (choice <= 0.6) {
        moveX = -stepSize;
      } else if (choice <= 0.8) {
        moveY = stepSize;
      } else {
        moveY = -stepSize;
      }
    }

    this.x += moveX;
    this.y += moveY;

    
    this.x = constrain(this.x, 0, width - 1);
    this.y = constrain(this.y, 0, height - 1);

    
  }

  render() {
    let distance = dist(this.x, this.y, this.prevX, this.prevY);
    
    if (distance > minDistance) {
      let hue = map(this.x, 0, width, 0, 360);
      let brightness = map(this.y, 0, height, 100, 50);
      
      colorMode(HSB, 360, 100, 100);
      strokeWeight(15); 
      stroke(hue, 100, brightness); 
      strokeWeight(15); 
      point(this.x, this.y);

      this.prevX = this.x;
      this.prevY = this.y;
    }
  }
}

Potential Improvements

I want to make this walker a self-avoiding one I think that would be interesting visualization.

Week 1 – Reading Response

In this reading, Gary William Flake invites readers on a journey through the world’s complex systems, where simplicity gives rise to complexity. Flake begins by examining the concept of reductionism which is a traditional scientific approach to understanding systems by dissecting them into their smallest components. While reductionism has been a powerful tool in science, enabling breakthroughs in fields from biology to physics, Flake argues that it falls short when grasping the behavior of complex systems. According to Flake, considering the smallest unit of complex system is not enough to understand the behavior of the entire system; for instance, one can not deduce the behavior of the ant colony by considering individual ants or the nervous system by just considering individual neurons. This is the result of what Flake calls holism where the whole system is greater than the sum of the individual parts mainly due to the complex interaction of the individual parts in the system.  The complex interaction among the individual systems can be explained by three attributes namely: Parallelism, Iteration, and Adaptation. These complex interactions of individual units in complex systems among themselves and with their environment make it difficult to use a direct reductionist approach. 

Later in this chapter, Flake turns to discussing the convergence of sciences due to computation. According to Flake, computers have blurred the line between experimentation and theory. This is mainly a result of the simulation of experiments on computers. Through modeling various phenomena, such as weather patterns and brain networks, researchers have discovered general laws that dictate the actions of intricate systems. Thus, Flake establishes the framework for the remainder of the book, which aims to investigate the computational foundations of some of nature’s most complex phenomena, such as adaptive systems and fractals.