Midterm Project – Half of what I say is meaningless…….Julia

Concept:

The Julia set is a captivating mathematical concept that beautifully intertwines the realms of complex numbers and visual art. As I explore the intricate patterns generated by varying real and imaginary numbers, I find a profound resonance with the fluidity of creativity. Each adjustment in the parameters breathes life into the design, revealing a unique, ever-evolving masterpiece. The dance between chaos and order in the Julia set mirrors my artistic journey, where boundaries blur and possibilities expand. It serves as a reminder that the most enchanting creations often arise from the interplay of structured mathematics and the boundless freedom of artistic expression. (Not only this but there are many songs for Julia- hence the title).

In my code, I aimed to explore the intricate designs possible in p5.js using the Julia set. Both the dynamic range and the still design produced satisfying results. Getting the main code, which features interactive and dynamic effects, to achieve a smooth and colorful outcome took some time. On the other hand, the still version I created specifically for pen plotting was much easier to develop.

Results:

Main Sketch:

Pen-plot Sketch:

Coding Concepts

Referenced Image

Understanding Julia and Mandelbrot Sets

  • Particle System: The code initializes an empty array particles to hold the particle instances. Each Particle is represented by a position vector, velocity vector, and color, allowing them to move and change color dynamically.

 

class Particle {
  constructor(x, y, col) {
    this.position = createVector(x, y);
    this.velocity = createVector(0, 0);
    this.acceleration = createVector(0, 0);
    this.col = col; // Store particle's color and opacity
  }

  display() {
    stroke(this.col); 
    strokeWeight(2);
    point(this.position.x, this.position.y);

    // Add glowing effect by drawing semi-transparent ellipses
    noFill();
    stroke(this.col.levels[0], this.col.levels[1], this.col.levels[2], this.col.levels[3] / 2); // Fainter stroke for glow
    ellipse(this.position.x, this.position.y, 4, 4); // Small glowing ellipse
  }

  update() {
    let n = noise(
      this.position.x * noiseScale,
      this.position.y * noiseScale,
      frameCount * noiseScale
    );

    let a = TAU * n; // Noise angle for motion

    this.acceleration = createVector(cos(a) * 0.05, sin(a) * 0.05); // Smooth acceleration
    this.velocity.add(this.acceleration); // Update velocity based on acceleration
    this.velocity.limit(2); // Limit speed for smoothness
    this.position.add(this.velocity); // Update position based on velocity

    // Wrap particles around the screen when they go out of bounds
    if (!this.onScreen()) {
      this.position.x = random(width);
      this.position.y = random(height);
    }
  }

  onScreen() {
    return (
      this.position.x >= 0 &&
      this.position.x <= width &&
      this.position.y >= 0 &&
      this.position.y <= height
    );
  }
}

 

 

  • Julia Sets: The code defines multiple Julia sets, stored in the juliaSets array. Each Julia set is created with random complex constants (real and imaginary parts). The class JuliaSet manages the constants for generating the fractals and generates particles based on the Julia set equations.

 

class JuliaSet {
  constructor(cRe, cIm) {
    this.cRe = cRe;
    this.cIm = cIm;
  }

  // Update constants based on either rotation or mouse position
  updateConstants(cRe, cIm) {
    this.cRe = cRe;
    this.cIm = cIm;
  }

  createParticles(xMin, yMin, xMax, yMax) {
    push();
    // Rotate around the center of the quadrant
    translate((xMin + xMax) / 2, (yMin + yMax) / 2);
    rotate(frameCount * 0.001); 
    translate(-(xMin + xMax) / 2, -(yMin + yMax) / 2);

    for (let i = 0; i < numParticles; i++) {
      let x = random(xMin, xMax);
      let y = random(yMin, yMax);
      let zx = map(x, xMin, xMax, -1, 1);  
      let zy = map(y, yMin, yMax, -1, 1);  
      let iter = 0;

      while (zx * zx + zy * zy < 4 && iter < maxIterations) {
        let tmp = zx * zx - zy * zy + this.cRe;
        zy = 2 * zx * zy + this.cIm;
        zx = tmp;
        iter++;
      }

      // Assign colors based on the number of iterations
      let colorHue = map(iter, 0, maxIterations, 0, 360); // Map iteration to hue
      let opacity = map(iter, 0, maxIterations, 0, 255); // Map iteration to opacity
      let col = color(colorHue, 100, 255, opacity); // HSB color with variable opacity
      
      particles.push(new Particle(x, y, col));
    }
    pop();
  }
}

 

 

  • Oscillation: Oscillation is controlled by angleRe and angleIm, which are updated in the draw function when the mouse is not over the canvas. This creates a smooth oscillatory effect for the real and imaginary parts of the Julia sets. The amplitude of the oscillation is controlled by oscillationAmplitude, and oscillationSpeed determines how fast the angles change, causing the Julia set to dynamically oscillate.

 

// Oscillation variables
let angleRe = 0; // Angle for real part rotation
let angleIm = 0; // Angle for imaginary part rotation
let oscillationSpeed = 0.02; // Speed of the oscillation
let oscillationAmplitude = 1.5; // Amplitude of the oscillation

 

 

// Check if mouse is over the canvas
 if (mouseX >= 0 && mouseX <= width && mouseY >= 0 && mouseY <= height) {
   cRe = map(mouseX, 0, width, -1.5, 1.5);
   cIm = map(mouseY, 0, height, -1.5, 1.5);
 } 
 
 else {
   // Use oscillation when mouse is not over the canvas
   cRe = oscillationAmplitude * sin(angleRe);
   cIm = oscillationAmplitude * sin(angleIm);
   
   angleRe += oscillationSpeed;
   angleIm += oscillationSpeed;
 }

 

 

  • Particle Motion: Each Particle instance has:
    Position: Updated in the update method.
    Velocity: Calculated based on acceleration influenced by Perlin noise.
    Acceleration: Derived from noise to create smooth, natural movement.
    The update method utilizes the Perlin noise to define a direction (angle) of motion, which ensures that particles have a fluid, organic movement rather than erratic behavior.

 

let a = TAU * n; // Noise angle for motion

    this.acceleration = createVector(cos(a) * 0.05, sin(a) * 0.05); // Smooth acceleration
    this.velocity.add(this.acceleration); // Update velocity based on acceleration
    this.velocity.limit(2); // Limit speed for smoothness
    this.position.add(this.velocity); // Update position based on velocity

 

Embedded sketch

Sketch links:

Main: https://editor.p5js.org/mariamalkhoori/sketches/rAZ_ErDvE

Pen-Plot: https://editor.p5js.org/mariamalkhoori/sketches/y0ekmRdJv

Parts I’m proud of:

I think I’m overall proud that I was open to try something very new in a field (Maths) that’s quite intimidating.

I’m particularly proud of the integration of oscillation and Perlin noise in the particle system, which creates a captivating and fluid visual effect. The oscillation of the Julia sets introduces a dynamic quality, allowing the fractals to change smoothly over time, while the use of Perlin noise for particle movement ensures that their motion feels organic and natural rather than mechanical. This combination enhances the aesthetic appeal, making the visual experience engaging and immersive. Additionally, the interplay between the colors of the particles, driven by the fractal’s iterative escape dynamics, results in a stunning display that captivates the viewer’s attention. Overall, this synthesis of mathematical beauty and artistic design embodies the essence of generative art, transforming complex mathematical concepts into a mesmerizing visual spectacle.

Challenges:

I mainly faced challenges in figuring out the concept of the Julia set itself. Understanding the results of the different ranges required some effort to implement in the code.

Adding color and visual effects was just me testing and playing around, which resulted in some bugs that took time to fix.

I wanted to create only one Julia set that could spread across the screen, but I was unable to do so, so I settled for adding quadrants instead.

 Pen-Plotting translation and process:

For the pen-plotting translation, I had to create an entirely different code to produce a specific still image. I decided to explore more with this code, and I was really happy with how it ended up looking in the sketch itself. However, the plotted version looked completely different.

Initially, I had trouble implementing the SVG code and making it work, which was a hassle, and I ended up converting my images. In Inkscape, only a specific pattern was shown to me, and it looked very difficult to plot, so I had to edit it to display only the outline of what it provided. I tried to edit the image to make it resemble a Julia set range but failed to do so.

It’s not that I’m dissatisfied with the result; it’s quite the opposite. Upon seeing the final product, it reminded me of my art style, which made me very happy. While it is not a correct version of the Julia set, I would still say it is an extraction from it with a little touch from me.

Areas for improvement:
  1. SVG Implementation
  2. Image Editing
  3. Testing and Debugging
Future Work

Advanced Julia Set Exploration and possibly more math techniques: 

      • Investigate more complex variations of Julia sets and fractals. Experimenting with different mathematical formulas or parameters can yield unique visual results.
      • Consider implementing real-time adjustments to the parameters of the Julia set based on user interaction, enhancing the dynamic aspect of your visualizations.
References

https://thecodingtrain.com/challenges/22-julia-set

https://paulbourke.net/fractals/juliaset/

https://fractalsaco.weebly.com/julia-set.html

Apps: Inkscape

 

Leave a Reply

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