Assignment 10- Merge-ocalypse!

Concept

Suika Game is a popular Japanese puzzle game where players combine fruits of the same type to create larger and more complex fruit combinations. The goal is to strategically merge and stack the fruits to fill the board, aiming to reach the largest possible fruit before the space runs out.

For this assignment I decided to recreate the games using matter.js to create a simple interactive physics-based game.

Highlight I’m proud of

Matter.js powers the physics simulation, providing gravity, collisions, and interactions between objects.

const { Engine, World, Bodies, Composite } = Matter;

let engine, world;

// Create an engine and world
engine = Engine.create();
world = engine.world;

When balls of the same size collide, they merge into a larger ball, and the score increases.

// Scoring values for each size index
const scoreValues = [0, 2, 5, 7, 12, 15, 20, 25, 35, 50];

// Handle collisions to merge balls of the same size, update the score, and mark balls as "old" after any collision
function handleCollisions(event) {
  if (gameOver) return;  // Ignore collisions if the game is over

  const pairs = event.pairs;

  for (let pair of pairs) {
    let ballA = droppedBalls.find(b => b.body === pair.bodyA);
    let ballB = droppedBalls.find(b => b.body === pair.bodyB);

    if (ballA && ballB) {
      // Mark both balls as "old" since they've collided
      ballA.hasCollided = true;
      ballB.hasCollided = true;

      // Check if they are of the same size and can merge
      if (ballA.size === ballB.size) {
        const nextSizeIndex = ballA.body.sizeIndex + 1;
        if (nextSizeIndex < ballSizes.length) {
          const newSize = ballSizes[nextSizeIndex];
          const newColor = colors[nextSizeIndex];

          // Create a new merged ball at the midpoint of the two colliding balls
          const midX = (ballA.body.position.x + ballB.body.position.x) / 2;
          const midY = (ballA.body.position.y + ballB.body.position.y) / 2;
          const mergedBall = Bodies.circle(midX, midY, newSize, { restitution: 0.8 });
          mergedBall.size = newSize;
          mergedBall.color = newColor;
          mergedBall.sizeIndex = nextSizeIndex;
          mergedBall.hasCollided = true;  // Mark new merged ball as "old"

          Composite.add(world, mergedBall);
          droppedBalls.push({ body: mergedBall, size: newSize, color: newColor, hasCollided: true });

          // Update the score based on the size of the merged ball
          score += scoreValues[nextSizeIndex];

          // Play merge sound
          mergeSound.play();

          // Remove the original balls from the world and array
          Composite.remove(world, ballA.body);
          Composite.remove(world, ballB.body);
          droppedBalls = droppedBalls.filter(b => b !== ballA && b !== ballB);
        }
      }
    }
  }
}

 

Players click on the screen to drop a ball, and a preview ball appears under the mouse.

 

let previewBall = { size: 30, color: '#FF6347' };

// Drop the preview ball when mouse is clicked
function mousePressed() {
  if (!gameOver) {
    dropBall(mouseX, previewBall.size, previewBall.color);
  }
}

function dropBall(x, size, color) {
  const ball = Bodies.circle(x, 0, size, { restitution: 0.4 });
  ball.size = size;
  ball.color = color;
  Composite.add(world, ball);
  droppedBalls.push(ball);
}
Embedded sketch

 

Edit Sketch: https://editor.p5js.org/mariamalkhoori/sketches/CaVEWClQZb

Reflection and ideas for future work or improvements

-I really wanted to make this more visually appealing, and I even got to draw some shapes for the game. However, it was quite difficult to detect the edges and make them fall properly, as well as assign the size alongside the merging.
-Adding a Start page and highest score system.

References
  • https://suikagame.com/
  • https://www.youtube.com/watch?v=zTNuMUsO-1g&ab_channel=Caleb%27sCodingCorner
  • https://pixabay.com/sound-effects/search/silly/
  • https://git.tombo.sh/tom/suika-game/src/branch/main/index.html

Week 9 Assignment

Concept

In this code, I merged a few ideas to create a cohesive set of actions I envisioned before executing it:

  • Flowers appearing at random places.
  • Flowers opening and closing.
  • A bee hive.
  • Bees follw to track the flowers (only opened ones and avoid the closed one)..
  • After passing all the open flowers bees return back to the hive.
  • Clicking a bee hive that follows the other swarm of bees (boid flock).
Highlight I’m proud of
class BeeBoid {
  constructor(x, y) {
    this.position = createVector(x, y);
    this.velocity = createVector(random(-1, 1), random(-1, 1));
    this.acceleration = createVector(0, 0);
    this.maxForce = 0.05; // Maximum steering force
    this.maxSpeed = 3; // Maximum speed
    this.size = 15; // Size of the bee body
    this.wingSize = 8; // Size of the wings
    this.xmove = random(-0.5, 0.5); // Horizontal movement speed
    this.ymove = random(-0.5, 0.5); // Vertical movement speed
  }

  applyForce(force) {
    this.acceleration.add(force);
  }

  seek(target) {
    let desired = p5.Vector.sub(target, this.position);
    desired.setMag(this.maxSpeed);
    let steer = p5.Vector.sub(desired, this.velocity);
    steer.limit(this.maxForce);
    this.applyForce(steer);
  }

  update() {
    this.velocity.add(this.acceleration);
    this.velocity.limit(this.maxSpeed);
    this.position.add(this.velocity);
    this.acceleration.mult(0); // Reset acceleration
  }

  borders() {
    if (this.position.x > width) this.position.x = 0;
    else if (this.position.x < 0) this.position.x = width;
    if (this.position.y > height) this.position.y = 0;
    else if (this.position.y < 0) this.position.y = height;
  }

  display() {
    // Draw wings
    ellipseMode(RADIUS);
    noStroke();
    fill(171, 240, 255);
    // Left wings
    ellipse(this.position.x - 10, this.position.y - 5, this.wingSize, this.wingSize);
    ellipse(this.position.x - 10, this.position.y + 5, this.wingSize, this.wingSize);
    // Right wings
    ellipse(this.position.x + 10, this.position.y - 5, this.wingSize, this.wingSize);
    ellipse(this.position.x + 10, this.position.y + 5, this.wingSize, this.wingSize);

    // Draw body
    fill(255, 244, 61);
    angleMode(DEGREES);
    ellipse(this.position.x, this.position.y, this.size, this.size * 1.5);
    stroke(0);
    strokeWeight(5);
    line(this.position.x - 10, this.position.y - 5, this.position.x + 10, this.position.y - 5);
    line(this.position.x - 10, this.position.y + 5, this.position.x + 10, this.position.y + 5);
  }

  move() {
    this.position.x += this.xmove;
    this.position.y += this.ymove;

    this.barrier();
  }

  barrier() {
    if (this.position.x >= width || this.position.x <= 0) {
      this.xmove = -this.xmove;
    }
    if (this.position.y >= height || this.position.y <= 0) {
      this.ymove = -this.ymove;
    }
  }
}
class Flock {
  constructor() {
    this.boids = [];
  }

  addBoid(boid) {
    this.boids.push(boid);
  }

  run() {
    for (let boid of this.boids) {
      let closestFlower = null;
      let closestDist = Infinity;

      // Check for the closest flower
      for (let flower of flowerArray) {
        if (flower.isAlive) {
          let d = p5.Vector.dist(boid.position, createVector(flower.centreX, flower.centreY));
          if (d < closestDist) {
            closestDist = d;
            closestFlower = flower;
          }
        }
      }

      if (closestFlower) {
        boid.seek(createVector(closestFlower.centreX, closestFlower.centreY));
      }
      
      boid.update();
      boid.borders();
boid.display();    }
  }
}
  • Arrays are used to store flowers, petals, and stem dots, allowing dynamic addition, deletion, and access to individual elements for updating their states.
  • For loops iterate through arrays and draw multiple flowers and petals, while if conditions handle flower removal, withering, and the periodic addition of new flowers.
  • The code uses trigonometric functions like Math.atan2 to calculate angles for petal positioning, creating uniqu-looking curved  flower shapes.
  • The frameCount variable is used to create periodic actions and smooth animations, such as blooming and movement of petals over time.
Embedded sketch

Edit Sketch: https://editor.p5js.org/mariamalkhoori/sketches/exMNOF-7n

Reflection and ideas for future work or improvements
    • I wanted to add sound to this sketch but did not have the time.
    • Playing around to develop some sort of story.
    • Work on better graphics; I wanted the bee wings to flutter and be animated and to create a better-shaped beehive.
    • Create my own unique-looking flowers using trigonometric functions.

Refrences:
https://editor.p5js.org/HelenaCui/sketches/qwgXBW6Da

Reflection Blogpost- MUJO

Ever since my introductory class in Interactive Media, I’ve encountered a number of projects that build on themes I’m already somewhat familiar with. But what these artists accomplish is something transformative—they take these foundational ideas and push them to fit their own new realms. The MOJU Project installation, in particular, exemplifies this, as it’s more than just a system in motion; it’s a structured concept reimagined as a live, immersive performance. What fascinates me is how the project begins with familiar ideas, then radically reinterprets them to create something that resonates deeply with the artists, inviting the audience to explore the unique perspectives they can bring.

I would say that rhis project is a true multimedia experience. It combines the diverse passions and talents of its co-creators, blending coding, music, and live performance into a cohesive artistic expression. There’s a synergy between the technical and the performative, which elevates the experience, drawing the viewer into a space where these elements don’t just coexist but enhance each other. It showcases how interactive media can break down the boundaries between disciplines, creating a unified piece that’s both technically impressive and emotionally compelling.

While the story itself is abstract and requires some interpretation, this complexity adds to its allure. I’m constantly drawn in by the choreography of the movements and the intricate visuals displayed, which seem to pulse with life. The ambiguity of the narrative lets each viewer bring their own understanding, fostering a connection that feels personal yet communal.

Assignment 8

Concept

This project is all about creating an interactive experience from the seek command where a magnifying glass chases after footprints on the canvas.  When the magnifying glass “catches” a footprint, a new one pops up randomly, representing the excitement of exploration and discovery.

Highlight I’m proud of

I’m proud of how smoothly the magnifying glass interacts with the footprints. Watching it follow the moving footprints is super satisfying, especially with the slight speed difference

 

class MagnifyingGlass {
  constructor(x, y) {
    this.pos = createVector(x, y);
    this.vel = createVector(0, 0);
    this.acc = createVector(0, 0);
    this.maxSpeed = 1.5; // Slower speed for magnifying glass
    this.maxForce = 0.05; // Slower acceleration
  }

  seek(target) {
    let force = p5.Vector.sub(target, this.pos);
    force.setMag(this.maxSpeed);
    force.sub(this.vel);
    force.limit(this.maxForce);
    return force;
  }

  applyForce(force) {
    this.acc.add(force);
  }

  update() {
    this.vel.add(this.acc);
    this.vel.limit(this.maxSpeed);
    this.pos.add(this.vel);
    this.acc.set(0, 0);
  }

  followFootprints() {
    if (footprints.length > 0) {
      let target = footprints[footprintIndex].pos;
      let distance = p5.Vector.dist(this.pos, target);

      // If close to current footprint, remove it and generate a new one
      if (distance < 5) {
        footprints.splice(footprintIndex, 1); 
        this.generateNewFootprint(); 
        if (footprints.length > 0) {
          footprintIndex = footprintIndex % footprints.length;
        }
      } else {
        let force = this.seek(target);
        this.applyForce(force);
      }
    }
  }

 

Embedded sketch

 

Edit Sketch: https://editor.p5js.org/mariamalkhoori/sketches/LcZtAjZgp

Reflection and ideas for future work or improvements
  • Footprint Movement: Right now, the footprints just wander randomly. I could make them move in more interesting patterns or even have them react to the magnifying glass’s position to make things more interactive.
  • Sound Effects: Adding sound effects for when the magnifying glass catches a footprint could make the experience even more engaging.

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

 

Midterm Progress 2 – Julia Set

Concept:

Continuing my work from the previous sketch, I noticed two things that needed to be done: refining my original code and creating a pen-plotting-friendly version. This time, I mainly focused on the latter. I kept the Julia set design but made it less dynamic and interactive to suit pen plotting. The design in the coding sketch produced a different concept than the plotted version, but I still liked the final outcome.

Code Highlight:
let c = { re: 0, im: 0 };
let zoom = 1;

function setup() {
  createCanvas(600, 600);
  noFill();
}

function draw() {
  background(0); 
  let w = 4 / zoom;
  let h = (4 * height) / width / zoom;

  stroke(255); 
  for (let x = 0; x < width; x++) {
    for (let y = 0; y < height; y++) {
      let zx = map(x, 0, width, -2, 2) * zoom;
      let zy = map(y, 0, height, -2, 2) * zoom;
      let i = 0;
      while (i < 100) {
        let tmp = zx * zx - zy * zy + c.re;
        zy = 2.0 * zx * zy + c.im;
        zx = tmp;
        if (zx * zx + zy * zy > 4) break;
        i++;
      }
      if (i === 100) {
        point(x, y);
      }
    }
  }

  // Draw additional Julia sets
  drawAdditionalSets();
}

function mouseMoved() {
  c.re = map(mouseX, 0, width, -1.5, 1.5);
  c.im = map(mouseY, 0, height, -1.5, 1.5);
}

function mouseWheel(event) {
  zoom += event.delta * 0.001;
  zoom = constrain(zoom, 0.1, 10);
}

// Draw additional Julia sets
function drawAdditionalSets() {
  let sets = [
    { re: -0.7, im: 0.27015 },
    { re: 0.355, im: 0.355 },
    { re: -0.4, im: 0.6 },
    { re: 0.355, im: -0.355 },
    { re: -0.7, im: -0.27015 }
  ];

  for (let set of sets) {
    let zx, zy, i;
    for (let x = 0; x < width; x++) {
      for (let y = 0; y < height; y++) {
        zx = map(x, 0, width, -2, 2) * zoom;
        zy = map(y, 0, height, -2, 2) * zoom;
        i = 0;
        while (i < 100) {
          let tmp = zx * zx - zy * zy + set.re;
          zy = 2.0 * zx * zy + set.im;
          zx = tmp;
          if (zx * zx + zy * zy > 4) break;
          i++;
        }
        if (i === 100) {
          stroke(225); 
          point(x, y);
        }
      }
    }
  }
}

 

 

Key Components
  • Julia Set Calculation: The core of the code lies in the logic that iterates over each pixel on the canvas, mapping pixel coordinates to a complex plane and then applying the iterative formula for the Julia set.
  • Rendering Multiple Julia Sets: The drawAdditionalSets() function renders additional Julia sets with predefined complex constants. By iterating over multiple constants and reapplying the Julia set formula, this function draws additional sets on the same canvas, expanding the visual complexity of the sketch.
Sketch:

Pen Point Sketch:
Final Steps:
  • Focus on refining the main code.
  • Provide A3 prints.

Midterm Progress – Julia Set

Concept:

While researching randomly generated methods to implement in my code, I came across the Julia Set. A Julia Set is a set of complex numbers that do not converge to any limit when a given mapping is repeatedly applied to them. The primary purpose of this code is to provide an engaging way to explore the fascinating world of fractals and complex dynamics through visual art. It allows for a deeper appreciation of mathematical concepts by transforming them into a dynamic visual experience. This kind of interactive art can be used for educational purposes, artistic expression, or simply as a fun visual experiment (although it is extremely difficult).

Understanding Julia and Mandelbrot Sets

Code Highlight:
class JuliaSet {
  constructor(cRe, cIm) {
    this.cRe = cRe; // Real part of the Julia constant
    this.cIm = cIm; // Imaginary part of the Julia constant
  }

  // Create particles based on the Julia set
  createParticles() {
    for (let i = 0; i < numParticles; i++) {
      // Random position for each particle
      let x = random(width);
      let y = random(height);
      let zx = map(x, 0, width, -2, 2);
      let zy = map(y, 0, height, -2, 2);
      let iter = 0;

      // Check if the point is in the Julia set
      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++;
      }

      // Create particles based on whether they are in the Julia set
      let brightness = iter < maxIterations ? 50 : map(iter, 0, maxIterations, 0, 255);
      particles.push(new Particle(x, y, brightness));
    }
  }
}

 

Key Components
  1. Particle System: An array called particles stores instances of Particle, representing individual points that are displayed on the canvas..
  2. Mapping Mouse Movement: The cRe and cIm variables are calculated using the map() function, which transforms the mouse’s x and y positions to a range suitable for generating the Julia set (between -1.5 and 1.5).
  3. Julia Set Creation: A JuliaSet object is created with the mapped constants, and its createParticles() method generates particles based on whether points fall within the Julia set.
  4. Each point on the canvas is tested using an iterative algorithm to see if it remains bounded (within a circle of radius 2) when applying the Julia set’s equation. If it does not escape, it belongs to the set.
  5. Particle Behavior: Each Particle is updated every frame using Perlin noise (noise()) to create smooth, organic movements. The angle of movement is determined by the noise value, leading to a natural, swirling motion.
  6. Particles are checked to see if they are within the canvas boundaries; if they exit, they are randomly repositioned.
  7. Brightness Calculation: The brightness of each particle is determined based on the number of iterations it took to escape the set. If the point escapes before reaching the maximum number of iterations, it is assigned a low brightness. Otherwise, its brightness is scaled based on how quickly it escaped.
Sketch:

Edit:   https://editor.p5js.org/mariamalkhoori/sketches/M0NHDl7zU
Next Steps:

I want to expand the Julia set to take on a fuller shape across my canvas. So far, this is not what I had in mind at all, but working with such a new concept has been challenging, so I will pause here for now.

  • Work on the generative aspect further.
  • Refine the particle system, as I still feel it doesn’t meet the design I envision.

Advanced Mathematical Exploration:

  • Different Constants: Allow users to explore different Julia constants (e.g., c values) by letting them input values directly or select from a preset list.
  • Mandelbrot Set Comparison: Implement a feature to compare the Julia set with its corresponding Mandelbrot set, illustrating their relationship.

 

References

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

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

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

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

Assignment 3- Mixing Water and Salt

Concept

Pure water is one of the simplest chemical compounds to understand, and the concept of mixing solubles with water is something most people learn early on in third grade. However, translating this fundamental idea into a digital simulation presented a significant challenge. Representing molecules and their interactions through code required a deeper understanding of both chemistry and programming. Initially, my goal was to model how water and salt molecules interact with each other using forces of attraction. This involved not only visualizing the individual molecules but also accurately simulating their dynamic interactions. The process of creating this simulation highlighted the complexity behind seemingly simple concepts and provided valuable insights into the behavior of molecules in a digital environment.

 

I tried to implement the particles movements in this seperate code using attractors to mimic an atom: https://editor.p5js.org/mariamalkhoori/sketches/VmTpfxlVA

Water Atoms Stock Illustrations – 1,606 Water Atoms Stock Illustrations, Vectors & Clipart - Dreamstime

Highlight I’m proud of

 

function draw() {
  background(img);
  
  // Draw the glass of water
  fill(177, 177, 199, 225);
  quad(131, 126, 318, 126, 290, 344, 158, 344);
  
  fill(85, 179, 236, 100);
  quad(137, 177, 312, 177, 290, 344, 158, 344);
  
  // Update and display water molecules
  for (let molecule of waterMolecules) {
    molecule.update();
    molecule.display();
  }

  // Update and display salt molecules if showSalt is true
  if (showSalt) {
    for (let molecule of saltMolecules) {
      molecule.update();
      molecule.display();
    }
  }

  // Check if attraction pairs exist and apply attraction during MIX
  if (isMixing) {
    for (let pair of attractPairs) {
      let waterMolecule = pair.water;
      let saltMolecule = pair.salt;

      // Calculate the distance between the water molecule and the salt molecule
      let target = createVector(saltMolecule.x, saltMolecule.y);
      let currentPos = createVector(waterMolecule.x, waterMolecule.y);
      let distance = p5.Vector.dist(currentPos, target);

      // If the water molecule is close enough, snap to the salt molecule and stop moving
      if (distance > 15) { // Adjust the threshold to be equal to their combined radii
        let attractionForce = p5.Vector.sub(target, currentPos);
        attractionForce.setMag(0.5); // Set attraction force strength

        // Apply the attraction force to move the water molecule towards the salt molecule
        waterMolecule.x += attractionForce.x;
        waterMolecule.y += attractionForce.y;
      } else {
        // Snap the water molecule to the salt molecule's position
        waterMolecule.x = saltMolecule.x;
        waterMolecule.y = saltMolecule.y;
        waterMolecule.isAttached = true; // Mark as attached to stop future movement
      }
    }
  }

 

Embedded sketch

https://editor.p5js.org/mariamalkhoori/sketches/Oy3gJilH_

Reflection and ideas for future work or improvements

The project provided a valuable exploration of simulating molecular interactions and highlighted the complexities of translating theoretical chemistry into a visual format. It revealed the challenge of balancing scientific accuracy with effective coding techniques and emphasized the importance of a solid understanding of molecular behavior. Future improvements could include enhancing simulation accuracy, adding interactive user features, incorporating advanced visualization techniques, expanding to more complex scenarios, and developing educational tools to support learning.

Future Work:

  • Accuracy Improvement: Enhance precision of molecular interactions.
  • User Interaction:Add features for user input and parameter adjustments.
References:

//https://www.youtube.com/watch?v=OAcXnzRNiCY&ab_channel=TheCodingTrain

Assignment 2- Spiral Repulsion

Concept

For this assignment, I was primarily inspired by the galaxy—a seemingly simple yet profoundly intricate phenomenon in our vast universe. The swirling motion of stars and cosmic dust, the vibrant colors, and the immense scale all evoke a sense of mystery and wonder. Galaxies are both elegant and complex, embodying the balance between chaos and order. This duality inspired me to explore patterns and shapes that reflect the beauty and unpredictability of the cosmos. Through this project, I aimed to capture the essence of the galaxy’s mesmerizing movement and its delicate balance of simplicity and complexity.

In my code, I implemented two key features. The first was the required assessment of movement and acceleration. By pressing the up and down arrow keys, the speed of the particles could be controlled, allowing for a dynamic interaction with the animation. The second feature involved particle interaction through hovering. As the cursor hovers over the particles, they would disperse, creating a sense of disruption and fluid motion in response to user input.

Dark Matter May Not Exist: These Physicists Favor of a New Theory of Gravity

Highlight I’m proud of

Here I was able to create a special class for the particles.

class Particle {
  constructor(vx, vy) {
    this.vx = vx;
    this.vy = vy;
    this.num = 255;
    this.a = 255;
    this.loc = createVector(width / 2, height / 2);  // Start from the center
    this.vel = createVector(0, 0);  
    this.acc = createVector(1, 1);
  }

  update() {
    // Apply acceleration using the acceleration factor
    this.vel.add(this.acc.mult(accelerationFactor));  
    this.loc.add(this.vel);

    // Reset acceleration and limit the velocity for smoother control
    this.acc.mult(0);
    this.vel.limit(0.5);  
  }

  repulse() {
    // Calculate the distance between the particle and the mouse
    let mouse = createVector(mouseX, mouseY);
    let dir = p5.Vector.sub(this.loc, mouse);  
    let distance = dir.mag();  

    if (distance < repulsionRadius) {  // If the particle is within the repulsion radius
      dir.normalize();  // Normalize the direction to get just the direction vector
      let force = map(distance, 0, repulsionRadius, 5, 0);  // Stronger repulsion the closer the particle is
      dir.mult(force);  // Multiply the direction vector by the repulsion force
      this.vel.add(dir);  // Apply the repulsion force to the particle's velocity
    }
  }

  isOutside() {
    // Check if the particle goes outside the canvas
    return (this.loc.x < 0 || this.loc.x > width || this.loc.y < 0 || this.loc.y > height);
  }

  display() {
    // Update acceleration based on the particle's properties and a trigonometric function for spirals
    this.acc = createVector(
      sin(radians(this.vx + this.num / 2)) / 2,
      cos(radians(this.vy - this.num / 2)) / 2
    );

    // Draw the particle with a fade effect
    fill(255, this.a);
    var r = map(this.a, 255, 0, 1, 10);  // Particle size changes as it fades
    ellipse(this.loc.x, this.loc.y, r);

    this.num += map(this.a, 255, 0, 1, 0);  // Update num for smooth spiral motion
    this.a -= 0.1;  // Gradually reduce alpha for a fade-out effect
  }
}

Embedded sketch

Sketch:
 https://editor.p5js.org/mariamalkhoori/sketches/_t5yMvlGp
Reflection and ideas for future work or improvements
  • User Interaction: Add interaction features such as particles reacting to clicks or drag-and-drop functionality.
  • Different Particle Types: Introduce various particle types with distinct behaviors, such as different shapes or sizes.
  • Customizable Motion Patterns: Allow particles to follow different patterns or trajectories, like zig-zags or chaotic paths.
Refrences:
  • https://editor.p5js.org/RonanChang/sketches/S1NQJgbx4
  • https://decodingnature.nyuadim.com/2024/08/28/week-1-fireflies-by-dachi/