Midterm Project “Starry Sketches” – Week 7

Starry Sketches

Starry Sketches is a creative generative art project that generates a range of continuously transforming constellation-like patterns. Different iterations of the project accentuate certain elements, like the feel of the sky and its ambiance, the feel of mesmerization, and the feel of amusement.

Source Code

Concept & Artistic Vision

For my midterm project I began with a route that focused on creating underwater species through creative coding and having each creature evolve into the other in some way or form. This idea was quite interesting and the vision I had in mind was relatively executable. Especially when I came across this project that presented the idea of an evolving circle that led to outcomes similar to what I had envisioned.

Source

To get the exact feel of an underwater environment, I read about sea creatures, their physical characteristics, and their movement manner. I decided finally to focus on four creatures; sea horses, starfish, jellyfish, and octopuses, which each depict unique movement mechanisms. I found that sea horses only hovered in place to move around, whereas starfish crawled and rowed like crabs did, while jellyfish drifted along the currents of the ocean, and octopuses propelled the current and crawled with their tentacles.

Source

Unfortunately, I was not able to go so far with my idea for it felt static despite my attempts to produce a generative piece. This led me to decide to shift my focus away from underwater environment and onto the skies for some sort of inspiration. Given that I began by working on the starfish for my initial idea, I was very keen on incorporating stars in my project, hence I began looking into that field. When reading about stars and looking through. Images for inspiration, I came across star/ zodiac constellations, which are basically stars in close proximity that create “two-dimensional maps for astronomers to find objects and explain their location and for navigators to use stars to determine their position”. However, nowadays, these constellations are often linked with astrology, and are brought up in the context of zodiac signs. The problem with this based on the article is that it has not scientific foundation, for they are just groups of stars that appear close to each other and are named after objects, animals etc.

Source

When reading that the constellations have no exact shapes that confine them and the zodiac constellations have no scientific basis, I felt a sense a freedom for exploration, unlike with the underwater species where I was unable to come up with an abstract representation of already existing creatures. This enabled me to come up with the idea of creating a sketch that constantly generates new constellations by drawing lines based on the proximity of the stars to one another. That way I was able to incorporate the element of the star that I had already began working on while also ensuring that my project was generative and did not appear static.

Coding Translation, Logic, & Challenges

When I began working on the starfish for my underwater project attempt, I was mostly focused on perfecting the shape of the creature and its movement, failing to consider the bigger picture of how the project will translate to a generative piece.

This led me to creating one long ‘for loop’ which, although eventually unused, was a learning process for me that allowed to explore the function of ‘noise’ in so many different ways, whether that be directly on the coordinates, lines, or even colors,  which later came in handy when I worked on the constellations project. Another important take out of this part of my process were ‘nested for loops’ that allowed me to iterate not only through the circumference of a circle but also through its diameter, hence leading to a bunch of nested stars that increase in size with every loop creating one big star.

for(let j=0; j<= 50; j++)
  {
  for (let angle = 0; angle <= TWO_PI; angle += PI/2.5) 
{
  let x = centerX + radius * cos(angle);
  let y = centerY + radius * sin(angle) + (3.5 - noise((angle), i / 15) * 7);
  curveVertex(x + noiseX, y + noiseY);
  let x2 = (centerX) + radius2 * cos(angle + HALF_PI / 2.5);
  let y2 = (centerY) + radius2 * sin(angle + HALF_PI / 2.5);
  curveVertex(x2 + noiseX, y2 + noiseY);
}
  //endShape(CLOSE);
  radius-=2;
  radius2-=2;
  }
}

While I was happy with the outcome of the starfish in terms of how it moved and looked, I was not satisfied with the fact that it seemed relatively predictable, and was moving only based on the ‘noise’ that was added to the coordinates, making me feel like I had not incorporated enough from what we have learnt into the project. Hence, moving forward in my process I created a solid plan of what will take place in my sketch.

I started my process simple by creating a class of stars that were of random sizes, positions, velocities, and acceleration, and called a hundred of them to see what the outcome would look like. I immediately realized two main issues with the stars eventually leaving the canvas, and the acceleration increasing nonstop.

class Star 
{
  constructor() 
  {
    this.position = createVector(random(width), random(height));
    this.size = random(3, 7);
    this.velocity = createVector(random(-0.3, 0.3), random(-0.3, 0.3));
    this.acceleration = createVector(random(-0.01, 0.01), random(-0.01, 0.01));
  }

  update() 
  {
    // Update the velocity and position based on acceleration
    this.position.add(this.velocity);
    this.velocity.add(this.acceleration);

   }

  display() {
    let starPoints = random(1, 5);
    let externalRadius = this.size;
    let internalRadius = this.size * 0.4;
    let angle = TWO_PI / starPoints;
    let halfAngle = angle / 2.0;
    fill(255);
    beginShape();
    for (let a = 0; a < TWO_PI; a += angle) {
      let starX = this.position.x + cos(a) * externalRadius;
      let starY = this.position.y + sin(a) * externalRadius;
      vertex(starX, starY);
      starX = this.position.x + cos(a + halfAngle) * internalRadius;
      starY = this.position.y + sin(a + halfAngle) * internalRadius;
      vertex(starX, starY);
    }
    endShape(CLOSE);
  }
}

I added the checkEdges() function like we had previously done with the movers which resolved the issue of the stars flying out of the canvas, however despite adding the ‘limit’ function to the acceleration, the stars continued to accelerate rapidly. I resolved this issue by including an ‘if-statement’ as shown below.

update() 
  {
    // Update the velocity and position based on acceleration
    this.position.add(this.velocity);
    // Limit acceleration 
    if (this.velocity < 5)
      {
        this.velocity.add(this.acceleration);
      }
   }

Once I had the stars set I began working on the lines which were somewhat a challenge to achieve as envisioned, however I am quite proud of how they came out. I had five different main trials which yielded different results, some of which were actually interesting, but not exactly what I had in mind.

 Attempt 1:

In the first trial, my thought process was simple, I need to draw a line between two stars. So, let the first star be ‘i’ and the second one be ‘j’. Through a ‘for loop’ I thought I was incrementing both the i and j values, however, I came to realize that despite thinking I was incrementing the ‘j’ value, the loop constantly equated its value to one position.

let j;
for (let i = 0; i < stars.length; i++) 
{
  j = 0;
  stroke (255);
  line(stars[i].position.x, stars[i].position.y,        stars[j].position.x, stars[j].position.y); 
  j++;
}
}

Attempt 2:

For my second trial, I tried to have the line drawn between the star ‘i’, and the star that followed it so, star ‘i + 1’. This also yielded an interesting outcome, but again, the issue was that each star was only connecting  to two other stars, leading to a static outcome.

for (let i = 0; i < stars.length - 1; i++) {
  stroke(255);
  line(stars[i].position.x, stars[i].position.y, stars[i + 1].position.x, stars[i + 1].position.y);
}
}

Attempt 3:

For my third trial, I knew I was on the right track with the previous two attempts, I had to find a ‘j’ value that was an incrementation of the ‘i’ value but still have that looping and incrementing to avoid static connections between the same stars. Inspired by the nested ‘for loops’ that I had initially created for my starfish, I created a nested loop that produced incrementing values for both ‘i’ and ‘j’. The issue with this attempt however was that there were endless connections between all the stars regardless of their positions.

 for (let i = 0; i < stars.length; i++) {
  for (let j = i + 1; j < stars.length; j++) {
      stroke(255);
      line(stars[i].position.x, stars[i].position.y, stars[j].position.x, stars[j].position.y);
  }
}
}

Attempt 4:

In my fourth trial, I built onto the previous trial. I measured the distance between the stars ‘i’ and ‘j’ using the ‘dist’ function and included an ‘if-statement’ that only allowed for a line to be drawn if the stars were in close proximity.

 for (let i = 0; i < stars.length; i++) {

  for (let j = i + 1; j < stars.length; j++) {
    let d = dist(stars[i].position.x, stars[i].position.y, stars[j].position.x, stars[j].position.y);
    
    if (d < 100) {
      stroke(255);
      line(stars[i].position.x, stars[i].position.y, stars[j].position.x, stars[j].position.y);
    }
  }
}
}

Attempt 5:

By my fourth attempt I was very happy with the outcome, my only issue was that it seemed like it was not representing constellations accurately because each star was connecting to an endless number of stars. I resolved this issue by a count for the lines to the ‘ if statement’ that measured the distance, that way, a line would only be drawn if the stars are in close proximity and if they are not connected to more than two stars.

 for (let i = 0; i < stars.length; i++) {
  let linesConnected = 0; // Initialize the count of lines connected to the current star

  for (let j = i + 1; j < stars.length; j++) {
    let d = dist(stars[i].position.x, stars[i].position.y, stars[j].position.x, stars[j].position.y);
    
    // Add a condition to check if the number of lines connected to the star is less than 4
    if (d < 100 && linesConnected < 2) {
      stroke(255);
      line(stars[i].position.x, stars[i].position.y, stars[j].position.x, stars[j].position.y);
      linesConnected++; // Increment the count of lines connected to the star
    }
  }
}
}

Moving on from there, I tried to find ways in which I could improve my code and I looked into any further issues that could be resolved. I realized then that my stars overlapped when moving which did not seem realistic to me. I read into that and found that the likelihood of stars overlapping is very small, if not impossible. So, I created a function within the stars that prevents overlapping.

noOverlap() 
  {
    // Loops through every star in the stars array
    for (let singleStar of stars) 
    {
      // Checks if the singleStar is the same current star called in the sketch
      if (singleStar !== this) 
        {
      // If not then measure the distance between this star and the singleStar
        let distance = dist(this.position.x, this.position.y, singleStar.position.x, singleStar.position.y);
      // Making the minimum possible distance between the stars be equal to their sizes combined
        let minDistance = this.size + singleStar.size;
        if (distance < minDistance) 
        {
      // Calculate a vector to move this star away from the singleStar
          let repel = createVector(this.position.x - singleStar.position.x, this.position.y - singleStar.position.y);
          repel.normalize();
          repel.mult(0.5); // Adjust the strength of the repulsion
          this.position.add(repel);
        }
      }
    }
  }

This function was one that I explored in one of my previous assignments, it basically loops for every star in the stars array measuring the distance between it and the current ‘this’ star that is being called. It then creates a repulsion force through a vector that subtracts the position of the stars being compared. This force is then added to the position which leads to the stars repelling or moving away from each other.

To further improve my project I looked into more images of constellations to try and find what is missing in my sketch. I realized that in most images the represent constellations the sky is shown with not only the bright stars that create the constellation but also with all the tiny stars that surround. Hence, I decided to create a class that produces tiny stars of varying sizes for the background. This came out really nice especially since the constructor of the class had 10 stars initialized, hence 10 new stars appeared with every frame creating a sparkling effect in the background.

To accentuate the feeling of stargazing and constellations, I decided to change the background to an image of the sky with stars and I added a music track to further draw in the audience.

Source Code

I also attempted to create different versions applying the function of ‘noise’ on multiple elements of my project like I did initially when working on the starfish.

Version 1:

update() 
  {
    this.position.x += map(noise(this.noiseX), 0, 1, -2, 2);
    this.position.y += map(noise(this.noiseY), 0, 1, -2, 2);
    this.colors = color(noise(this.noiseColor) * 255);
    this.noiseX += 0.01;
    this.noiseY += 0.01;
    this.noiseColor += 0.01;
    // Update the velocity and position based on acceleration
    this.position.add(this.velocity);
    // to limit acceleration
    if (this.velocity < 5)
      {
        this.velocity.add(this.acceleration);
      }
   }

In this version I added noise to the position of the stars and allowed for that noise to constantly update, leading to a trail behind the stars that made them look somewhat like shooting stars. Because of the looping update of the noise and the unlimited change of position the stars move in rapid speeds and seem to be leaving the canvas despite the bounds.

Version 2: 

update() 
  {
    this.colors = color(noise(this.noiseColor) * 255);
    this.noiseColor += 0.01;
    // Update the velocity and position based on acceleration
    this.position.add(this.velocity);
    // to limit acceleration
    if (this.velocity < 5)
      {
        this.velocity.add(this.acceleration);
      }
   }

In this version I only added noise to the color of the lines and the stars, and changed the alpha/ opacity which was sufficient to create the starry effect I was looking for with having the stars mov out of the frame as in the previous version.

Version 3 & 4:

In these two versions I added an element of color and changed the background to experiment with the effects that I can achieve.

rainbowColors() {
   // Calculate a color based on noise for the star & lines
   let noiseColorR = noise(this.noiseColor);
   let noiseColorG = noise(this.noiseColor + 1000);
   let noiseColorB = noise(this.noiseColor + 2000);
   let noiseColorA = noise(this.noiseColor + 3000);

     starColor = color(
     Math.round(255 * noiseColorR),
     Math.round(255 * noiseColorG),
     Math.round(255 * noiseColorB),
     Math.round(255 * noiseColorA)
   );
 }

Pen Plotting Process

My process with the pen plotting was luckily simple, although I did struggle initially with exporting the SVG files, however, once I had that figured out the process was relatively smooth.

The issue that arose with the SVG file was from the beginning of my project process when I was working on the starfish. I realized after multiple trials that the problem was not with my method of exporting but rather with the size and elements of the sketch itself. Given that my sketch of the starfish had a great deal of ‘noise’ embedded in its design it made exporting the file into an SVG impossible.

I took that into account when working on my new project with the constellations, and made sure that I worked based on that leaving any ‘noise’ or extensively large elements out of my design for plotting purposes.

In doing so I was able to get an SVG file ready for plotting. The only editing I had to do on Inkscape was remove the background. From there we imported the sketch to the plotter and it began the process which was quite lengthy since I decided to give the star the flexibility to connect to an endless number of nearby stars and I also called 300 stars on the canvas.

Areas of Improvement & Future Work

In terms of improvement and future work, I came across an article that details collisions of stars when reading about whether or not stars overlap. This article mentioned an interesting detail about stars’ collisions explaining that in the rare cases that they collide, if moving at a slow pace they merge to create brighter stars, and if traveling at a fast pace during the collision then they disappear leaving behind only hydrogen gas. I attempted to add this to my sketch however,  I was unsure of how to go about it and since I was somewhat tight on time I decided to leave this as a future addition to the project.

Source

One thing that I realized later could be improved is the way in which the lines appear. Right now the lines immediately connect the stars once the sketch is played. What I think would look nice if the lines connected slowly and gradually to one another. I attempted to work on this issue by adding by only allowing the lines to appear after a certain frame count, however, that only postponed the time of which all the lines appeared simultaneously. I am hoping to edit this soon to create a more smooth-looking environment.

Leave a Reply

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