Midterm Draft #1 – Stefania Petre

Concept:
The Dynamic Spirals project creates an engaging visual experience through the generation of spiraling lines that react to user input. The spirals dynamically change in size, color, and motion based on the mouse position, creating a captivating display of colors and forms.

First Draft:

 

Design:

  • Spirals: The sketch generates multiple spirals whose radii are modulated by a sine function, resulting in smooth wave-like motions. Each spiral is drawn around the center of the canvas, enhancing the visual symmetry.
  • Color Palette: Colors are assigned based on the index of each spiral, producing a gradient effect across the spirals. This choice aims to evoke a sense of depth and movement.
  • Interactivity: The maximum radius of the spirals varies according to the mouse’s X position. This feature adds a layer of interactivity, allowing users to influence the visual output directly.

States and Variations:
The sketch can exhibit different states based on user interaction:

  • The number of spirals can be adjusted to create a more dense or sparse visual.
  • Modifying the angleOffset value can lead to variations in the spirals’ animations, resulting in different visual patterns.
  • Introducing additional mouse interactions (e.g., controlling color or speed) can further diversify the visual experience.

Identified Risks:
One of the more complex aspects of this project is managing the performance and responsiveness of the visual output, especially with a larger number of spirals. Ensuring the sketch runs smoothly without lag is essential for a good user experience.

Risk Reduction Strategies:
To minimize potential risks:

  • The number of spirals is set to a manageable level (10) to maintain performance.
  • The use of noFill() and strokeWeight() is applied to enhance rendering efficiency while preserving visual quality.
  • Performance will be tested across different devices to ensure responsiveness, and adjustments will be made based on testing results.

Next Steps

  • Explore the introduction of additional interactive elements, such as changing spiral behavior with keyboard inputs or varying the colors dynamically based on user actions.
  • Experiment with adding background patterns or effects to enhance visual depth.
  • Document findings and user feedback to improve the generative art experience continuously.

Midterm Draft 1 – Branches of Motion

Concept

For my midterm project, I chose to create a generative art piece based on a fractal tree. I’ve always found fractal patterns in nature really interesting, like how trees branch out or how veins spread in a leaf. Each part of a fractal reflects the whole, which is something I wanted to explore in this project. I also wanted the tree to feel interactive so that users can control how the branches grow and move, making it feel more personal and engaging. The mouse controls the angle of the branches, which adds an element of real-time interaction.

Design

The design focuses on creating a tree that changes based on user input. By moving the mouse horizontally, the user can adjust the angle of the branches, which directly influences how the tree grows and branches out. This makes the experience interactive and unique to the user’s movement.

The main function in the code is branch(), which uses recursion to draw the tree. It takes the length of the current branch, draws it, and then calls itself to draw smaller branches at an angle. The recursion stops once the branch length is small enough, and the angle of the branches is tied to the position of the mouse on the screen. This creates a dynamic, interactive system where the tree’s structure shifts as the mouse moves.

Code
For now, I have a basic draft that lays the foundation for the project. Here’s the current code:

let angle = 0; // Initialize the angle variable, which will be controlled by mouse movement

function setup() {
  createCanvas(640, 360); // Set up a canvas of 640 by 360 pixels
}

function draw() {
  background(0); // Set the background to black each frame to clear the previous drawing
  angle = map(mouseX, 0, width, 0, TWO_PI); // Map the mouse's X position to an angle between 0 and TWO_PI (full circle)
  
  stroke(255); // Set the stroke color to white
  strokeWeight(2); // Set the stroke thickness to 2 pixels

  translate(width * 0.5, height); // Move the origin to the bottom center of the canvas
  
  branch(100); // Call the branch function to start drawing the tree with an initial length of 100
}

// Recursive function to draw the branches
function branch(len) {
  line(0, 0, 0, -len); // Draw a vertical line representing the current branch

  translate(0, -len); // Move the origin to the end of the current branch
  
  // If the current branch length is greater than 4, keep drawing smaller branches
  if (len > 4) {
    push(); // Save the current state of the drawing
    rotate(angle); // Rotate by the current angle (controlled by the mouse)
    branch(len * 0.67); // Call the branch function recursively, reducing the length by 67%
    pop(); // Restore the original state

    push(); // Save the current state again for the other branch
    rotate(-angle); // Rotate in the opposite direction by the current angle
    branch(len * 0.67); // Call the branch function for the opposite branch
    pop(); // Restore the original state
  }
}

This code creates a simple tree that responds to the mouse’s horizontal position. The tree grows and splits into smaller branches as the user moves the mouse. This serves as the base for the more complex features I will add later.

Embedded Sketch

Identified Risk and How I Reduced It
The part of the project that worried me the most was making sure the interaction with the mouse felt smooth and natural. I was concerned that the tree would shake too much when the mouse moved quickly, which would make the experience less enjoyable. To deal with this, I focused on carefully mapping the angles using the mouseX position to ensure the tree’s motion stays smooth and doesn’t jitter too much. I also tested the recursive function separately to make sure the branches don’t overlap or become cluttered.

Possible Future Additions
While this is the current draft, there are a few features I am considering for future versions, though these are not finalized yet:

  • Colors: I might add color gradients or change the color of the branches based on their depth or size to make the tree more visually interesting.
  • Animations: Animations can be added to make the tree grow over time or sway in the wind to make the piece feel more dynamic.
  • More Trees: Adding multiple trees on the canvas could create a forest-like scene, where each tree is slightly different in shape or size.

draft 2 for now: https://editor.p5js.org/maryamalmatrooshi/sketches/K0YQElaqm

Midterm Progress 1

Project Concept:

This past summer, I had the incredible opportunity to spend time in Accra during a June term, and I quickly fell in love with the city. Everything about it—the culture, the people, the food, and even the clothing—felt so familiar, like a vibrant reflection of home. It was as if I had stepped into a living blueprint of my own country. The connections between Accra and home were unmistakable, especially in the patterns woven into the fabric of daily life.

I found myself especially drawn to the Adinkra symbols, which originated from the Ashanti tribe. These symbols, while simple to the untrained eye, carry deep significance. They appear on fabrics, pottery, logos, and are even on walls as decor—integrated into everyday life with meaning and tradition. Although I cannot fully capture the weight of their history and symbolism, I have been deeply inspired by them.

This project is my way of paying homage to these symbols by integrating their essence into a generative art form that reflects the patterns I admired so much.

 

Project Approach:

My goal is to create a dynamic, generative pattern that evolves with each click of the mouse. Not only will the patterns themselves change, but I also want the process to unfold visibly allowing the viewer to see the art as it’s being created. By adjusting the frame rate and tracking oscillations, I hope to give each sketch a sense of fluidity and movement.

The key elements of this project will be variations in color and symbol placement, ensuring that each generated pattern feels unique. While some patterns may repeat, my challenge is to make them complement one another seamlessly.

Embedded sketches:

code:

This is a piece of the code that I wrote that generates some of the patterns that you see in the sketches. Overall, I’m really proud of them so far but there are a lot of things that I want to add and  possibly some things that I may remove as well. Currently, I’m working on some more patterns and     continuing to test them within the code before I produce another sketch.

// Geometric Triangles
function drawGeometricTriangles(x, y, size) {
  let numTriangles = 5;
  let colorPick = random(colors);
  stroke(colorPick);
  noFill();
  
  let triangleSize = size;
  for (let i = 0; i < numTriangles; i++) {
    triangle(-triangleSize / 2, triangleSize / 2, triangleSize / 2, triangleSize / 2, 0, -triangleSize / 2);
    triangleSize *= 0.7;
  }
}

// Harmonic Motion Style Sine Waves
function drawSineWaves(x, y, size) {
  let waveHeight = size / 4;
  let frequency = 0.2;
  strokeWeight(2);
  noFill();
  
  beginShape();
  for (let i = 0; i < size; i++) {
    let xOffset = i;
    let yOffset = sin(i * frequency) * waveHeight;
    vertex(xOffset - size / 2, yOffset);
  }
  endShape();
}

// Nested Triangles
function drawNestedTriangles(x, y, size) {
  let triangleSize = size;
  noFill();
  stroke(random(colors));
  for (let i = 0; i < 4; i++) {
    triangle(-triangleSize / 2, triangleSize / 2, triangleSize / 2, triangleSize / 2, 0, -triangleSize / 2);
    triangleSize *= 0.7;
  }
}

// West African Symbols/Geometric Shapes
function drawSymbols(x, y, size) {
  noFill();
  let symbolSize = size * 0.6;
  stroke(random(colors));

  // Circle with horizontal/vertical line cross
  ellipse(0, 0, symbolSize, symbolSize);
  line(-symbolSize / 2, 0, symbolSize / 2, 0);
  line(0, -symbolSize / 2, 0, symbolSize / 2);
  
  // Small triangles within
  for (let i = 0; i < 3; i++) {
    let triSize = symbolSize * (0.3 - i * 0.1);
    triangle(0, -triSize / 2, triSize / 2, triSize / 2, -triSize / 2, triSize / 2);
  }
}

// Zebra Print
function drawZebraPrint(x, y, size) {
  let stripes = 10;
  for (let i = 0; i < stripes; i++) {
    let step = i * (size / stripes);
    line(-size / 2 + step, -size / 2, size / 2 - step, size / 2);
    line(size / 2 - step, -size / 2, -size / 2 + step, size / 2);
  }
}

// Diamonds within Diamonds
function drawDiamondsInDiamond(x, y, size) {
  let dSize = size;
  noFill();
  for (let i = 0; i < 5; i++) {
    beginShape();
    vertex(0, -dSize / 2);
    vertex(dSize / 2, 0);
    vertex(0, dSize / 2);
    vertex(-dSize / 2, 0);
    endShape(CLOSE);
    dSize *= 0.7;
  }
}

// Bezier Curves
function drawCurves(x, y, size) {
  noFill();
  strokeWeight(2);
  for (let i = 0; i < 6; i++) {
    bezier(-size / 2, -size / 2, random(-size, size), random(-size, size),
           random(-size, size), random(-size, size), size / 2, size / 2);
  }
}

Challenges:

The biggest challenge lies in generating patterns that I can feel genuinely proud of, especially given the personal connection I have to the source of inspiration. It’s a bit daunting to try and replicate the visual and symbolic depth of Adinkra, but I believe in trial and error as my guide. I’m starting by setting up a grid-based design in my sketches, generating initial patterns based on what we’ve learned in class. From there, I plan to randomly assign patterns to grid positions. The tricky part is not the overall pattern, but the design of each individual grid symbol—finding the balance between randomness and intentionality.

Risk Management:

To reduce the uncertainty, I simply dove in. I knew I wouldn’t find the right direction without first experimenting with the code. While the process is still evolving, I’m happy with some of the early results. I’m now focusing on refining the color combinations and continuing to develop patterns that are both visually compelling and meaningful. At the moment, I have two sketches—one that’s a modified version of the other—and I’m letting the process guide me, tweaking the code until I find something that truly resonates.

Ripple Cubes

https://editor.p5js.org/is2431/sketches/FM0-hjygj

 

Description of the Code

This p5.js sketch creates a 3D cube grid with a dynamic ripple effect. Each cube’s vertical position oscillates based on its distance from the center, while its color changes according to its depth (z coordinate). The concept is inspired by natural wave patterns, where the ripple effect imitates waves traveling through the surface, and color gradients add depth perception to the 3D space. The visual result is a dynamic, pulsating cube, creating an engaging, hypnotic effect.

Functions and Organization

The program organizes the core logic in the draw() function, which is responsible for rendering each frame of the sketch. The ripple effect is achieved through a combination of sin() and dist() functions to compute the oscillation offsets, and the color fill is managed by mapping the z values to RGB values.

Variable and Function Naming

  • w stands for the width of each cube, ensuring the ripple and grid are proportional.
  • rippleHeight controls the amplitude of the waves.
  • cols and rows are used for calculating how many cubes fill the canvas based on the grid spacing.

Reflection and Future Improvements

I’m proud of how well the ripple effect integrates with the 3D rendering and color mapping. However, future improvements could involve adding user interactivity, allowing control over ripple speed, amplitude, or even camera rotation. Another potential idea could be integrating sound, where the ripple responds to external audio input or ambient sounds, enhancing the immersive experience.

The sketch could also evolve into more complex 3D shapes, expanding from simple cubes to spheres or even irregular polygons, to add variety and depth to the visual.

 

Assignemnt 4

Concept

In this project, I aimed to create a mesmerizing visual display that responds dynamically to sound input using the p5.js library. The core concept revolves around simulating particles that mimic harmonic motion while being influenced by real-time audio levels detected through the microphone. This interplay of sound and visuals creates an engaging experience, reminiscent of a light show or a digital aurora, where each particle dances in response to the surrounding environment.

Highlights I’m Proud Of
class Particle {
  constructor(x, y) {
    this.x = x;
    this.y = y;
    this.angle = random(TWO_PI);
    this.length = random(10, 20); // Length of the trails
    this.timeOffset = random(TWO_PI); 
  }

  update(vol) {
   
    let harmonicX = cos(this.angle) * 200 * sin(frameCount * 0.02 + this.timeOffset); // Horizontal oscillation
    let harmonicY = sin(this.angle * 2) * 200 * cos(frameCount * 0.03 + this.timeOffset); // Vertical oscillation

    // Update positions based on combined harmonic motion
    this.x += harmonicX * vol * 0.5; 
    this.y += harmonicY * vol * 0.5; 

  
    this.x += cos(this.angle) * vol * 100; 
    this.y += sin(this.angle) * vol * 100; 

  
    this.x = constrain(this.x, 0, width);
    this.y = constrain(this.y, 0, height);
    
    this.angle += 0.05; // Smooth rotation
  }

 

Embedded sketch

https://editor.p5js.org/mariamalkhoori/full/_9bo6lBl_

Edit Sketch:
https://editor.p5js.org/mariamalkhoori/sketches/_9bo6lBl_
Reflection and Ideas for Future Work
  • Expanded Color Palette: Experiment with a broader range of colors or gradients, perhaps responding to different frequencies or sound levels for a richer visual output.
  • More Complex Patterns: Investigate additional mathematical functions to define the particle movements, creating even more intricate and visually stunning animations.

 

Refrences

Simple Harmonic Motion #11 for Light at Blenheim Palace (2014)

Sound from my favourite band :3 — https://music.youtube.com/watch?v=CaMcDzvwL30&si=ZyQE7yoDhOCHxzFR

Midterm Progress #1 | Equations!

The Concept I am Aiming For

For the midterm, I have two ideas that I wanted to try and make. My first idea is that I would create:

a flowfield representation of mathematical parametric equations, with the mouse or any spawned external objects as an attractor to each field particle. 

Essentially (hopefully), is that the flowfields would be attracted forming, and shaping, a parametric equation on the left. And since different equations can form different fields, it would create variation in the art. I could also create a modifier for the equation so that it’s slightly different. This field can be interrupted and played around with an attractor vertex that is attached to the mouse. I am interested to see how this would work out.

Scouring the vast internet, I found an artist, Ilhan Zulji, who played around a lot with p5.js an interactive elements. My second idea would be to create:

Create waves, squares, ellipses, or any predefined shapes that react to external sources: audio, microphone, or camera, to form a new shape. 

Both of the examples I have shown above were from Zulji and Yankov, where they, through some equations and visuals, generated the art via parameters that respond to sound, acoustics, or noises. I want to see whether I could re-create this with my own personal touch.


Update #01 September 2024

After many thoughts and considerations, plus looking at the capabilities of the pen plotter, I decided to go with the first idea. The sketch below is what I have played around so far:

Essentially, I have particles that follow a certain mathematical equation. This movement is then disrupted by noise and wind (for now). For my final product, I wanted to add a fading feature, as well as more equations, and see how two equations can be combined so that it returns a new shape.

Potential Useful Resources

# Drawing Parametric Equations – Umit Sen 

# Drawing Flow Fields with Vectors – Colorful Coding

# Quadio – Ikko. graphics 

# Audio Reactive Visuals – Leksha Yankov

# Audio Reactive Visuals CS Project – Austin Zhang

Sketch – Week 4

Concept

I wanted to create a lines and circles that moved together, similar to one of Memo Akten’s art pieces. Once I created the basic shapes and movement, I added to the aesthetic by making the sketch resemble flowers in a field.

Code Snippet

let elapsedTime = millis() - startTime;

// adjust speed using lerp over time
if (elapsedTime < 10000) {
  speedFactor = lerp(0.05, 0.5, elapsedTime / 10000);
} else if (elapsedTime < 20000) {
  speedFactor = 0.5;
} else {
  speedFactor = lerp(0.5, 0.05, (elapsedTime - 20000) / 10000);
}

Embedded Sketch 

Reflections

I struggled with making the petals for the flower, and I couldn’t get the petals to touch the circumference of flower. With more time, I would tweak the code and experiment more. I would also try to achieve more interesting/complex visual effects, as Memo Akten’s works are very flashy and colourful.

Letter Jellyfish

The project I made this week was a pure accident. While trying to experiment with one concept, I made a mistake in my code that created something so interesting I decided to keep it. I named this piece “Letter Jellyfish” because the movement of the sine waves reminded me of a jellyfish swimming. The code is on GitHub here.

My original concept, while not fully fleshed out, was intended to be a program where I experimented with creating moving 3D text over a sine wave. From there I planned to experiment with the core to make the program look more interesting. To do this, I would layer multiple sine waves made up of letters to create a “3D” effect of the text. Originally, I wanted the sine wave to iterate over the English alphabet, which would be mapped to its x-coordinate. I decided to make the wave choose random letters instead because it made the piece feel more animated and chaotic. Because the coordinate system is different in WEBGL than in 2D mode, I had trouble seeing the sine wave. I was using the camera() method to try to fix this. After this, I wanted to try to rotate the letters so they spin around while oscillating. While doing this, I accidentally rotated the entire sine waves, creating something similar to what my final piece looks like. I liked how this looked so much, that I pivoted my piece to incorporate these multiple sine waves in a 3D space instead. I tried keeping the aspect of the “3D” text as well, but the program ended up being too laggy to keep it this way. After this, to add some more visual flair to the piece, I made the waves shift their colors over time, based on the current frameCount. The code for these sine waves can be seen here:

for (let i = 0; i < total; i++) {
    let y = map(sin(angles[i]), -1, 1, -height, height / 2);
    let x = map(i, 0, total, -width * 2, width / 2, true);
    let letter;
    letter = map(x, -width / 2, width / 2, 0, alphabet.length - 1, true);
    push();
    for (let i = 0; i < 25; i++) {
      rotateY(45);
      translate(0, 0, 0.5);
      fill((i * 2 + frameCount) % 360, 100, 100);
      text(alphabet[floor(random(alphabet.length))], x, y);
    }
    pop();
    let increment = TWO_PI / 60;
    angles[i] += increment;
  }

 

I was doing a lot of experimenting using the camera() function, something I’d never used before. I was not able to make the camera move as I wanted it to, but I made a nice rudimentary one that would rotate around the piece. Ideally, I wanted the camera to occasionally go inside the center of the piece so that the camera would be surrounded by the letters close-up. This would probably be the main thing I want to improve on for this piece. I would also like to add more waves and make the pseudo-3D text, since it looked even more visually pleasing, but the program becomes too laggy to do this.

Atomic Harmony: Pendulums and Electrons in Motion – Week 4

Concept
Inspired by Memo Akten’s Simple Harmonic Motion, I wanted to create something that combines pendulums and electron orbits, both moving in harmony. Akten’s work is amazing because it shows natural forces and mathematical patterns as art, and I wanted to bring that same feeling into my own sketch. In my sketch, pendulums swing while electrons move around a nucleus, representing atomic motion. I wanted to show how harmonic motion, something we usually see in science, can also be artistic and full of life. The result is an abstract but dynamic experience that connects physics with art.

Code Highlight
A section of the code I’m particularly happy with is the part that updates and draws the electrons. I didn’t want them to just follow a fixed orbit; I wanted them to move more like waves, giving the sketch a more organic and alive feel.

// Electron class to represent electrons orbiting the nucleus
class Electron {
    constructor(orbitalRadius, startAngle) {
        this.orbitalRadius = orbitalRadius; // Radius of the orbital
        this.angle = startAngle; // Starting angle for electron
        this.speed = 0.02; // Angular speed of electron
        this.amplitude = 40; // Amplitude for oscillating motion (to simulate wave-like behavior)
        this.frequency = 0.2; // Frequency of oscillation
        this.phase = random(TWO_PI); // Random phase offset for each electron
    }

    // Update electron's position with simple harmonic motion
    update() {
        this.angle += this.speed; // Increase angle for circular motion
        let displacement = this.amplitude * sin(this.frequency * time + this.phase); // Calculate oscillation in radius
        this.currentRadius = this.orbitalRadius + displacement; // Current radius based on oscillation
    }

    // Display electron and its trail
    display() {
        let x = cos(this.angle) * this.currentRadius; // Calculate x position based on current angle and radius
        let y = sin(this.angle) * this.currentRadius; // Calculate y position based on current angle and radius

        // Draw a trail behind the electron
        stroke(180, 100, 100, 0.2); // Light trail color
        noFill(); 
        beginShape(); // Begin trail shape
        for (let i = 0; i < 20; i++) {
            let trailAngle = this.angle - i * this.speed; // Calculate trail angle
            let trailRadius = this.orbitalRadius + this.amplitude * sin(this.frequency * (time - i * 0.1) + this.phase); // Calculate trailing radius with oscillation
            let tx = cos(trailAngle) * trailRadius; // X position of trail point
            let ty = sin(trailAngle) * trailRadius; // Y position of trail point
            vertex(tx, ty); // Draw vertex at trail point
        }
        endShape(); // Finish trail shape

        // Draw the electron as a filled circle
        fill(180, 100, 100); // Bright blue electron color
        noStroke(); 
        circle(x, y, 12); // Draw electron at current position
    }
}

The key part here is the use of harmonic oscillation to give the electrons a wave-like displacement, which makes them feel less rigid and more alive. I think this detail makes the overall animation more interesting and adds a layer of depth to the movement.

Challenges
One of the biggest challenges I faced was getting the electron orbits to feel natural. At first, they moved too mechanically, just following a circle. It felt too predictable and didn’t fit with the kind of fluid, organic motion I was aiming for. To fix this, I added the harmonic oscillation to their radius, which made them move in and out as they orbit, giving the motion more life.

Embedded Sketch

Reflection and Future Work
I’m happy with how the pendulums and electrons came together in this sketch. The combination of pendulums swinging and electrons orbiting creates an interesting mix of science and art. I liked exploring how these motions can be turned into something visually engaging. In the future, I’d like to add some interactivity to the sketch. For example, letting the viewer control the speed or direction of the pendulums and electrons using their mouse. I think that would make the experience more fun and personal. Finally, using randomness (like Perlin noise) for the electron movement could make it look more natural and less predictable. There are a lot of ways I could continue improving this, but for now, I’m happy with what I’ve created!

Week 4 – Harmony in the Cosmos

Inspiration

Memo Akten’s work on Simple Harmonic Motion made me drawn to the rhythmic, cyclical patterns that form the foundation of so much of our world. Akten’s focus on symmetry and motion led me to think about the natural world—and nothing reflects cosmic symmetry and harmonic motion quite like a galaxy. I drew inspiration from Akten to code a visual representation of a spiral galaxy, exploring the underlying harmony that often governs the universe itself.

The Concept

A galaxy, particularly a spiral galaxy, is a perfect example of natural harmony. Galaxies are bound by gravity, with billions of stars moving in circular or elliptical orbits around a central massive core, often a black hole. While galaxies appear chaotic at first glance, they actually follow harmonious patterns of motion. The stars within them move predictably, the spiral arms wind in beautiful formations, and the entire system evolves slowly over time.

What fascinated me about Akten’s work was his ability to capture movement that is predictable yet fluid, a concept that parallels the dynamics of galaxies. Inspired by his approach to harmonic motion, I wanted to create my own representation of a galaxy, shifting and evolving while remaining within the boundaries of harmonic symmetry.

Sketch

The Core Code Idea

  1. Condensed Spiral Galaxy: I focused on creating a galaxy that’s small enough to fit within the canvas while still representing the vastness and depth of space. A maximum radius of 150 pixels keeps the stars tight and visually coherent.
  2. Stars on Spiral Arms: The stars are distributed along several spiral arms (in this case, five) and move in circular orbits around the center. Their speed is dependent on how far they are from the center, mimicking how stars in real galaxies behave.
  3. Slow Shape Shifting: Inspired by Akten’s harmonic motion, I added a subtle shifting mechanism where the spiral arms slowly evolve over time, creating a dynamic and living representation of a galaxy.

Full Code

let stars = [];
let numStars = 400; // Number of stars
let spiralArms = 5; // Number of spiral arms
let galaxyRadius = 150; // Maximum radius of the galaxy (more condensed)
let timeFactor = 0.001; // Factor for the shape-shifting over time

function setup() {
  createCanvas(600, 500); 
  noStroke();

  // Create stars
  for (let i = 0; i < numStars; i++) {
    let angle = random(TWO_PI); // Random starting angle
    let radius = sqrt(random()) * galaxyRadius; // Random radius within the galaxy radius
    let star = {
      angle: angle, // Initial angle
      radius: radius, // Distance from the center
      speed: map(radius, 0, galaxyRadius, 0.002, 0.015), // Stars closer to the center move faster
      size: map(radius, 0, galaxyRadius, 2, 4), // Smaller stars are further out
      twinkleOffset: random(TWO_PI) // Random phase for twinkle effect
    };
    stars.push(star);
  }
}

function draw() {
  background(10, 10, 30, 80); 
  translate(width / 2, height / 2); // Moves origin to the center of the canvas

  // Slowly shift the shape of the spiral galaxy over time
  let spiralShift = sin(frameCount * timeFactor) * 0.5;

  for (let i = 0; i < stars.length; i++) {
    let star = stars[i];
    
    // Calculates star position with spiral shifting
    let armOffset = (i % spiralArms) * TWO_PI / spiralArms + spiralShift; // Spiral arm offset with time-based shift
    let x = star.radius * cos(star.angle + armOffset);
    let y = star.radius * sin(star.angle + armOffset);
    
    // Updates star's angle to make it rotate around the center
    star.angle += star.speed;
    
    // Twinkle effect: stars slowly change brightness
    let twinkle = map(sin(star.twinkleOffset + frameCount * 0.05), -1, 1, 180, 255);
    fill(twinkle, twinkle, 255); // Soft white-blue color for stars
    
    // Draws the star
    ellipse(x, y, star.size, star.size);
  }

    // Draws the central black hole 
  fill(0);
  ellipse(0, 0, 20, 20); // Central black hole at the center
}

Explanation: 

  • Cos() and Sin() Functions calculate the stars’ X and Y positions based on their angle around the galaxy’s center.
  • The formula x = radius * cos(angle) calculates the horizontal (X) position of the star.
  • Similarly, y = radius * sin(angle) calculates the vertical (Y) position.
  • Angle Update: Each star’s angle property increases slightly in every frame, causing them to move in circular orbits. This simulates the motion of stars around the galaxy’s core.
  • Spiral Shift: The spiralShift variable gradually changes over time, slowly altering the spiral arms’ positions, mimicking a galaxy’s slow evolution.

This code allows us to simulate stars moving in elliptical paths around the galaxy’s center, which is key to creating the harmonic motion of a spiral galaxy.

What Can Be Improved

  • Interactive Zoom and Pan: Allow users to zoom in and out of the galaxy to simulate a sense of scale, as galaxies are vast and complex. Panning around the galaxy would also help users explore different parts of it.
  • Nebulae and Star Clusters: Adding glowing clouds or star clusters would enhance the visual complexity.