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);
}
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.
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 variableis used to create periodic actions and smooth animations, such as blooming and movement of petals over time.
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.
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);
}
}
}
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.
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
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
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:
SVG Implementation
Image Editing
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.
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.
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).
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
Particle System: An array called particles stores instances of Particle, representing individual points that are displayed on the canvas..
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).
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.
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.
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.
Particles are checked to see if they are within the canvas boundaries; if they exit, they are randomly repositioned.
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.
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.
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.
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.
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.
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
}
}
}
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.
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.
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
}
}