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.

Leave a Reply

Your email address will not be published. Required fields are marked *