Week 10- Super Breakout Game

Concept:

When I saw the assignment to create a project using Matter.js, the first thing that came to mind was recreating the classic Super Breakout game. I wanted to take the concept of bouncing a ball to break blocks and add some extra dynamics and fun with a physics engine. Super Breakout is simple yet addictive: you control a paddle, bouncing a ball to break rows of blocks. The challenge grows as the ball speeds up and changes angles, keeping the player focused and engaged.

Embedded Sketch:

Logic of Code:

I started by setting up a canvas with Matter.js as the physics engine to handle everything from gravity to collisions.

  • Matter.js Basics: First, I created an engine and world using Matter.Engine and Matter.World, which handle the entire physics environment. To make the ball and paddle, I used Matter.Bodies to create specific shapes, including a rectangle for the paddle and a circle for the ball.
  • Applying and Changing Forces: The ball starts with an initial velocity that gives it an upward and slightly angled movement. Each time the ball hits a block, I apply a slight random force. This “slight random force” is a tiny push in a random direction, which makes the ball move in an unpredictable way after each collision. This force helps the ball take new paths, creating a more dynamic and challenging gameplay experience, as it doesn’t just keep following the same straight line.
  • Collision Events: Using Matter.Events.on, I could detect whenever the ball hit a block. Each time it does, the code removes that block from the world, applies a small random force to the ball, and slightly increases the ball’s speed, making the game progressively harder. Additionally, I added logic to make the ball bounce when it touches the canvas edges, keeping it in play.

Challenges:

One issue I encountered was the ball getting stuck bouncing horizontally, going left to right without moving much vertically. To solve this, I added a check to ensure the ball always has a minimum vertical speed (y velocity). If the y velocity is too low, the code adjusts it to a minimum value, ensuring the ball keeps moving in both directions. This way, the game remains challenging, and the ball doesn’t get trapped in a back-and-forth loop.

function increaseBallSpeed() {
    let speedFactor = 1.02; // Increase speed slightly after each collision
    let minYVelocity = 3; // Minimum vertical speed to prevent horizontal bouncing
    let currentVelocity = ball.velocity;
    
    // Set new velocity with minimum vertical speed
    let newVelocity = {
        x: currentVelocity.x * speedFactor,
        y: Math.abs(currentVelocity.y) < minYVelocity 
           ? (currentVelocity.y < 0 ? -minYVelocity : minYVelocity) 
           : currentVelocity.y * speedFactor
    };
    
    Body.setVelocity(ball, newVelocity); // Apply the new velocity to the ball
}

In this function:

  • speedFactor controls how much the ball’s speed increases with each hit, keeping it manageable.
  • minYVelocity sets a minimum speed for the y direction, so the ball keeps moving vertically.
  • The code checks if Math.abs(currentVelocity.y) is less than minYVelocity. If it is, the y velocity is adjusted to either minYVelocity or -minYVelocity based on its current direction.

This addition keeps the gameplay smooth and prevents the ball from getting stuck in horizontal motion, which makes it one of my favorite parts of the code!

Future Improvements: 

While I’m happy with the current version, there’s a lot more I could add to enhance the gameplay. Some ideas for future improvements include:

  • Power-Ups: Add special blocks that, when hit, might speed up the ball, slow it down, or make it larger, adding more excitement and strategy to the game.
  • Score Tracking and Levels: Keeping score and advancing to new levels as blocks get progressively harder to break or new rows appear.
  • Sound Effects: Adding sounds for when the ball hits a block, the paddle, or the canvas edges to make it more engaging.
  • Mobile Version: Modifying the controls for touch interaction so the game can be played on mobile devices.

 

Matter.js Game

For my latest coding project, I combined the p5.js and Matter.js libraries to create an interactive game where the player controls a windmill to catch falling particles. The objective is simple yet addictive: catch as many particles in the moving goal as possible, aiming to reach 30 points before the timer runs out. The game features an interactive timer, a scoring system, and a dynamic win screen that displays the final time. Let’s take a closer look at the concept, code, and potential future improvements.

Concept

This project was inspired by classic arcade games, where the focus is on timing and precision. I wanted to create something that required player control and had a clear objective, so I added a rotating windmill controlled by the arrow keys and a particle-spawning system. The goal moves across the bottom of the screen, and every particle that lands inside scores a point. If the player scores 20 points, they win the game! After winning, the player can restart the game by clicking anywhere on the screen.

Embedded Sketch

https://editor.p5js.org/is2431/sketches/oE836dc9v

Reflection and Future Work

Reflecting on the process, I enjoyed exploring how Matter.js could be used to create physics-based interactions in a p5.js sketch. The windmill mechanism, in particular, felt satisfying to code as it introduced realistic physics to the player’s actions. However, one challenge was balancing interactivity and performance, especially when handling a high number of particles on-screen.

Ideas for Future Improvements

For future iterations, I’d like to explore the following improvements:

  1. Enhanced Visuals: Adding particle effects or animations when a particle hits the goal could add visual excitement to the game.
  2. Power-Ups and Obstacles: Introducing occasional power-ups that temporarily increase the windmill’s rotation speed or make the particles larger, as well as obstacles that reduce the score, could add depth and strategy.
  3. Difficulty Levels: Implementing levels with progressively faster-moving goals or smaller particle sizes would increase the challenge and extend playability.
  4. Mobile Compatibility: Adapting the game for mobile by adding touch-based controls for the windmill could open it up to more users.

I hope you enjoyed this walkthrough of my proje

Jump Game with Matter.js Library – Week 10

Concept
This week’s sketch is all about creating a simple platform game using the Matter.js physics library. The game involves controlling a ball that jumps from platform to platform, trying to reach new heights. The ball collects points as it climbs higher, and the goal is to score as much as possible before falling off the screen. This sketch explores gravity, jumping mechanics, and collision detection, creating a fun game where timing and precision matter.

Code Highlight
One of the main parts of this code is the handleCollision function, which checks when the ball lands on a platform. When the ball hits a platform, it enables jumping again, letting the player continue their ascent. Here’s a snippet of how this works:

function handleCollision(event) {
  for (let pair of event.pairs) {
    const { bodyA, bodyB } = pair; // Get the bodies in collision
    // Check if ball collides with any platform
    if (
      (bodyA === ball && platforms.includes(bodyB)) ||
      (bodyB === ball && platforms.includes(bodyA))
    ) {
      if (ball.velocity.y > 0) { // Allow jump if ball is moving downward
        canJump = true;
      }
    }
  }
}

In this function, each time the ball collides with a platform, the canJump variable is set to true if the ball is moving downwards. This allows the player to jump again, making the gameplay smooth and responsive. This function is essential because it controls when the player can jump, ensuring the ball only jumps after touching a platform.

Embedded Sketch


Reflection and Future Ideas
I’m happy with how this game turned out! The ball’s movement and simple controls make it easy to pick up and play, and the scoring system adds a fun challenge. Watching the ball jump from platform to platform is satisfying, especially with the smooth collision detection and responsive jumping mechanics. Setting up the collision events in Matter.js was a new experience, and it was exciting to see how different forces and gravity values could create realistic movement in the game.

In the future, I’d like to explore more ideas to make the game more dynamic. Adding different platform types, like moving or disappearing ones, would introduce an extra layer of difficulty and variety to the gameplay. Visually, adding effects like particle trails for jumps or enhancing the ball’s design would make the game feel more polished. Another improvement would be adding sound effects for actions like jumping and landing, which would make the game more immersive and engaging.

 

References

https://editor.p5js.org/hinatahave91/sketches/G91aWNP_9

https://github.com/jakewarrenblack/p5.js-ball-game

 

Week 10 – Khalifa Alshamsi

Inspiration

The Coding Train’s tutorials on Matter.js, particularly on “Adding More Forces” and “Collision Events,” provided the foundation for this project. The tutorials demonstrated how applying varied forces to objects and managing their interactions through collision events can produce compelling visual dynamics. This inspired me to experiment with these concepts by creating a controlled environment where these physics phenomena could be observed and manipulated.

Sketch

Full Code

// Module aliases
const { Engine, Render, Runner, World, Bodies, Body, Events, Mouse, MouseConstraint } = Matter;

// Create an engine
const engine = Engine.create();

// Create a renderer
const render = Render.create({
    element: document.getElementById('canvas-container'),
    engine: engine,
    options: {
        width: 800,
        height: 600,
        wireframes: false
    }
});

// Create ground, roof, and walls
const ground = Bodies.rectangle(400, 590, 810, 40, { isStatic: true, render: { fillStyle: 'brown' } });
const roof = Bodies.rectangle(400, 10, 810, 40, { isStatic: true, render: { fillStyle: 'gray' } });
const leftWall = Bodies.rectangle(0, 300, 40, 600, { isStatic: true, render: { fillStyle: 'gray' } });
const rightWall = Bodies.rectangle(800, 300, 40, 600, { isStatic: true, render: { fillStyle: 'gray' } });

// Create a box
const box = Bodies.rectangle(400, 200, 80, 80, { restitution: 0.5 });

// Add all of the bodies to the world
World.add(engine.world, [box, ground, leftWall, rightWall, roof]);

// Add mouse control
const mouse = Mouse.create(render.canvas);
const mouseConstraint = MouseConstraint.create(engine, {
    mouse: mouse,
    constraint: {
        render: { visible: false }
    }
});
World.add(engine.world, mouseConstraint);

// Create runner
const runner = Runner.create();

// Run the engine
Runner.run(runner, engine);

// Run the renderer
Render.run(render);

// Apply a continuous upward force every update
Events.on(engine, 'beforeUpdate', function(event) {
    // Apply force only if the simulation is active
    if (!mouseConstraint.mouse.button) {
        Body.applyForce(box, { x: box.position.x, y: box.position.y }, { x: 0, y: -0.05 });
    }
});

// Collision events for aesthetic purposes
Events.on(engine, 'collisionStart', function(event) {
    const pairs = event.pairs;
    pairs.forEach(pair => {
        const colors = ['#FF4136', '#0074D9', '#2ECC40', '#FF851B', '#7FDBFF'];
        pair.bodyA.render.fillStyle = colors[Math.floor(Math.random() * colors.length)];
        pair.bodyB.render.fillStyle = colors[Math.floor(Math.random() * colors.length)];
    });
});

What is Matter.js?

Matter.js is an open-source library that facilitates the simulation of physical systems, providing tools to create objects that behave according to the laws of physics within a digital environment. It is perfect for developing games, educational simulations, and interactive graphics.

Key Functionalities & Code Breakdown

 1. Setting Up the Environment

The initial step involves setting up Matter.js with a rendering engine and a physics engine, which are crucial for running any physics-based simulation.

const engine = Engine.create();
const render = Render.create({
    element: document.getElementById('canvas-container'),
    engine: engine,
    options: { width: 800, height: 600, wireframes: false }
});

 2. Creating and Controlling Bodies

A singular box is created, bounded by static walls that prevent it from exiting the viewport. The simplicity of the setup allows for focused observation of physical interactions.

const box = Bodies.rectangle(400, 200, 80, 80, { restitution: 0.5 });
const ground = Bodies.rectangle(400, 590, 810, 40, { isStatic: true });

 3. Applying Forces

Reflecting on The Coding Train’s approach to adding dynamism through forces, a continuous upward force counters gravity, enhancing the box’s interaction with its environment.

Events.on(engine, 'beforeUpdate', function() {
    Body.applyForce(box, { x: box.position.x, y: box.position.y }, { x: 0, y: -0.05 });
});

 4. Managing Collision Events

Collision handling illuminates the points of impact, with each collision causing a change in the color of the bodies involved, thereby providing instant visual feedback.

Events.on(engine, 'collisionStart', function(event) {
    event.pairs.forEach(pair => {
        pair.bodyA.render.fillStyle = '#FF4136';
        pair.bodyB.render.fillStyle = '#0074D9';
    });
});

Visuals and Interaction

The simulation is enhanced with visuals from The Coding Train, which underlines, the beauty of physics in motion. By integrating interactive elements, users can directly influence the forces acting on objects, thereby deepening their understanding of physics through real-time feedback.

Future Enhancements

Future versions could include more complex simulations using toxiclibs.js for more intricate particle systems and simulations, further exploring the boundaries of web-based physics simulations. But overall, I am proud that I got it actually to work.

Week 9 _ Glass

Concept: 

For this project, I decided to make a visualization inspired by tension and release with an interactive element as the mouse hovers around the canvas. Initially, the flock is strictly constructed around an attractor in a circular shape, but over time, it changes and moves around the canvas. The mouse interaction enables the Boids to transform shape and size, adding layers to the sketch. The shape of the Boide is inspired by kite-like crystal shapes in the shade of light transparent blue symbolizing shattered glass. Resembling the sketch this way makes sense to me because it shows fragility and the Boids collective movement while remaining individual. This mirrors the sensitivity of how one Boids movement influences others.

Highlight of Code:

For this project, I reviewed the presentation and watched the videos to understand the concept of flocking. I wanted to bring into the sketch things we learned prior to the class, and I decided that having an attractor would be interesting. I initially started playing around with the code we did in class. I played a lot with different classes and ways to make it work. I added three main classes: an attractor, a Boid, and a flock. Even though I had so much debugging to do, I think I am close enough to the visualization I had in my head.

The attractor class pushed the Boids towards it through a force similar to orbiting. It was hard to figure out how to make it work, but as I played with the numbers, it started making sense. The orbiting force is simply rotating the force by 2. The Boid class is affected by flocking and attraction. This helps the body move in groups and respond to the attraction force. The methods used for the Boid class are froce, flock, attraction, update, borders, and the show functions. 

for (let i = 0; i < 400; i++) {
   //changing this part effects how the overall visulaization is 
   let angle = map(i, 0, 400, 0, TWO_PI);
   let boid = new Boid(
     width/1.7  + radious * cos(angle),
     height/1.7 + radious * sin(angle)
   );
   flock.addBoid(boid);
 }

The above code is the main effector of the visualization. It took me some time to create a visualization I though was interesting, and it was inspired by the midterm for this semester.

class Boid {
  constructor(x, y) {
    this.acceleration = createVector(0, 0);
    this.velocity = createVector(random(-0.01, 0.001), random(-0.01, 0.01));
    this.position = createVector(x, y);
    this.r = 3;
    //mass
    this.mass = 2;
    // Maximum speed
    this.maxSpeed = 0.7;
    // Maximum steering force
    this.maxForce = 5;
  }
  applyForce(force) {
    let f = p5.Vector.div(force, this.mass);
    //changed how it moves looks nice
    this.acceleration.sub(f);
    this.acceleration.mult(10);
    this.acceleration.mult(f);

  }

  run(boids, attractor) {
    this.flock(boids);
    this.applyAttraction(attractor);
    this.update();
    this.borders();
    this.show();
  }

  applyAttraction(attractor) {
    //pull boids
    attractor.attract(this);
  }

  // accumulate  acceleration each time based on three rules
  flock(boids) {
    let sep = this.separate(boids); // Separation
    let ali = this.align(boids); // Alignment
    let coh = this.cohere(boids); // Cohesion
    // Arbitrarily weight these forces
    sep.mult(1);
    ali.mult(1);
    coh.mult(1);
    // Add the force vectors to acceleration
    this.applyForce(sep);
    this.applyForce(ali);
    this.applyForce(coh);
  }

  // update location
  update() {
    //  velocity
    this.velocity.add(this.acceleration);
    // limit speed
    this.velocity.limit(this.maxSpeed);
    this.position.add(this.velocity);
    // reset accelertion to 0 each cycle
    this.acceleration.mult(0);
    
  }

  // calculate and apply a steering force towards a target
  // STEER = DESIRED MINUS VELOCITY
  seek(target) {
    // A vector pointing from the location to the target
    let desired = p5.Vector.sub(target, this.position);
    // Normalize desired and scale to maximum speed
    desired.normalize();
    desired.mult(this.maxSpeed);
    // Steering = Desired minus Velocity
    let steer = p5.Vector.sub(desired, this.velocity);
    steer.limit(this.maxForce); // Limit to maximum steering force
    return steer;
  }

  show() {
    // draw a kite shape
    let angle = this.velocity.heading();
    fill(random(90, 127), random(150, 200), random(180, 255), 150);
    stroke(random(100, 127), random(100, 200), random(200, 255));
    push();
    translate(this.position.x, this.position.y);
    rotate(angle);

    let freqX = map(mouseX, 0, width, 1, 15);
    let freqY = map(mouseY, 0, height, 1, 15);

    beginShape();

    let x = this.r * cos(10 * this.r) * freqX;
    let y = this.r * cos(10 * this.r) * freqY;
    //crystal like shape
    //right
    vertex(x * 2, 0);
    //top right
    vertex(x * 0.7, -y);
    //top left
    vertex(-x * 0.7, -y);
    //left
    vertex(-x * 2, 0);
    //bottom
    vertex(0, y);
    endShape(CLOSE);
    pop();
  }

  // wraparound
  borders() {
    if (this.position.x < -this.r) this.position.x = width + this.r;
    if (this.position.y < -this.r) this.position.y = height + this.r;
    if (this.position.x > width + this.r) this.position.x = -this.r;
    if (this.position.y > height + this.r) this.position.y = -this.r;
  }

  // separation itchecks for nearby boids and steers away
  separate(boids) {
    let desiredSeparation = 15;
    let steer = createVector(0, 0);
    let count = 0;
    // for every boid in the system, check if it's too close
    for (let i = 0; i < boids.length; i++) {
      let d = p5.Vector.dist(this.position, boids[i].position);
      // ff the distance is greater than 0 and less than an arbitrary amount (0 when you are yourself)
      if (d > 0 && d < desiredSeparation) {
        // calculate vector pointing away from neighbor
        let diff = p5.Vector.sub(this.position, boids[i].position);
        diff.normalize();
        // weight by distance
        diff.div(d);
        steer.add(diff);

        // keep track of how many
        count++;
      }
    }
    // average -- divide by how many
    if (count > 0) {
      steer.div(count);
    }

    // As long as the vector is greater than 0
    if (steer.mag() > 0) {
      // Implement Reynolds: Steering = Desired - Velocity
      steer.normalize();
      steer.mult(this.maxSpeed);
      steer.sub(this.velocity);
      steer.limit(this.maxForce);
    }
    return steer;
  }

  // Alignment
  // For every nearby boid in the system, calculate the average velocity

  align(boids) {
    let neighborDistance = 40;
    let sum = createVector(0, 0);
    let count = 0;
    for (let i = 0; i < boids.length; i++) {
      let d = p5.Vector.dist(this.position, boids[i].position);
      if (d > 0 && d < neighborDistance) {
        sum.add(boids[i].velocity);
        count++;
      }
    }
    if (count > 0) {
      sum.div(count);
      sum.normalize();
      sum.mult(this.maxSpeed);
      let steer = p5.Vector.sub(sum, this.velocity);
      steer.limit(this.maxForce);
      return steer;
    } else {
      return createVector(0, 0);
    }
  }

  // Cohesion
  // For the average location (i.e. center) of all nearby boids, calculate steering vector towards that location
  cohere(boids) {
    let neighborDistance = 40;
    let sum = createVector(0, 0); // Start with empty vector to accumulate all locations
    let count = 0;
    for (let i = 0; i < boids.length; i++) {
      let d = p5.Vector.dist(this.position, boids[i].position);
      if (d > 0 && d < neighborDistance) {
        sum.add(boids.position); // Add location
        count++;
      }
    }
    if (count > 0) {
      sum.div(count);
      return this.seek(sum); // Steer towards the location
    } else {
      return createVector(0, 0);
    }
  }
}

The Boid class was the most complicated. I had to do a lot of debugging to add the attractor into action so that the Boids are affected by it.

Embedded Sketch:


Future work:

I am satisfied with how the sketch turned out to be. Things I want to experiment further with is how the flock would move around the canvas by creating a path for them to create different shapes and interactions that are strictly influenced by tension and release. Further, I think having an audio sync into the work is also interesting to see. The audio could perhaps be in relation to the acceleration and speed of the flock’s movement. Further, if this could translate into an interactive project, perhaps I can add a motion sensor so that the audience can influence the behavior of the flock through that data.

Resources:

The Coding Train. “Coding Challenge 124: Flocking Simulation.” YouTube, 11 Dec. 2018, www.youtube.com/watch?v=mhjuuHl6qHM.

Arthur Facredyn. “Path Finding in p5.js # 1.” YouTube, 11 June 2018, www.youtube.com/watch?v=Ohcrstxcci4.

Murmuration – Robert Hodgin. roberthodgin.com/project/murmuration.

5. Autonomous Agents. nature-of-code-2nd-edition.netlify.app/autonomous-agents/#vehicles-and-steering.

 

 

Week 10 – Fabrik by Dachi

Sketch: p5.js Web Editor | Fabrik

Inspiration

The development of this fabric simulation was mainly influenced by the two provided topics outlined in Daniel Shiffman’s “The Nature of Code.” Cloth simulations represent a perfect convergence of multiple physics concepts, making them an ideal platform for exploring forces, constraints, and collision dynamics. What makes cloth particularly fascinating is its ability to demonstrate complex emergent behavior through the interaction of simple forces. While many physics simulations deal with discrete objects, cloth presents the unique challenge of simulating a continuous, flexible surface that must respond naturally to both external forces and its own internal constraints. This complexity makes cloth simulation a particularly challenging and rewarding subject in game development, as it requires careful consideration of both physical accuracy and computational efficiency.

Development Process

The development of this simulation followed an iterative approach, building complexity gradually to ensure stability at each stage. The foundation began with a simple grid of particles connected by spring constraints, establishing the basic structure of the cloth. This was followed by the implementation of mouse interactions, allowing users to grab and manipulate the cloth directly. The addition of a rock object introduced collision dynamics, creating opportunities for more complex interactions. Throughout development, considerable time was spent fine-tuning the physical properties – adjusting stiffness, damping, and grab radius parameters until the cloth behaved naturally. Performance optimization was a constant consideration, leading to the implementation of particle limiting systems during grab interactions. The final stage involved adding velocity-based interactions to create more dynamic and realistic behavior when throwing or quickly manipulating the cloth.

How It Works

At its core, the simulation operates on a particle system where each point in the cloth is connected to its neighbors through spring constraints. The cloth grabbing mechanism works by detecting particles within a specified radius of the mouse position and creating dynamic constraints between these points and the mouse location. These constraints maintain the relative positions of grabbed particles, allowing the cloth to deform naturally when pulled. A separate interaction mode for the rock object is activated by holding the ‘R’ key, creating a single stiff constraint for precise control, with velocity applied upon release to enable throwing mechanics. The physics simulation uses a constraint-based approach for stable cloth behavior, with distance-based stiffness calculations providing natural-feeling grab mechanics and appropriate velocity transfer for realistic momentum.

Code I am proud of

The particle grabbing system stands out as the most sophisticated portion of the codebase. It is sorting particles based on their distance from the mouse and applying distance-based stiffness calculations. Here’s the core implementation:
// Array to store particles within grab radius
let grabbableParticles = [];

// Scan all cloth particles
for (let i = 0; i < cloth.cols; i++) {
    for (let j = 0; j < cloth.rows; j++) {
        let particle = cloth.particles[i][j];
        if (!particle.isStatic) {  // Skip fixed particles
            // Calculate distance from mouse to particle
            let d = dist(mouseX, mouseY, particle.position.x, particle.position.y);
            if (d < DRAG_CONFIG.GRAB_RADIUS) {
                // Store particle info if within grab radius
                grabbableParticles.push({
                    particle: particle,
                    distance: d,
                    offset: {
                        // Store initial offset from mouse to maintain relative positions
                        x: particle.position.x - mouseX,
                        y: particle.position.y - mouseY
                    }
                });
            }
        }
    }
}

// Sort particles by distance to mouse (closest first)
grabbableParticles.sort((a, b) => a.distance - b.distance);
// Limit number of grabbed particles
grabbableParticles = grabbableParticles.slice(0, DRAG_CONFIG.MAX_GRAB_POINTS);

// Only proceed if we have enough particles for natural grab
if (grabbableParticles.length >= DRAG_CONFIG.MIN_POINTS) {
    grabbableParticles.forEach(({particle, distance, offset}) => {
        // Calculate stiffness based on distance (closer = stiffer)
        let constraintStiffness = DRAG_CONFIG.STIFFNESS * (1 - distance / DRAG_CONFIG.GRAB_RADIUS);
        
        // Create constraint between mouse and particle
        let constraint = Constraint.create({
            pointA: { x: mouseX, y: mouseY },  // Anchor at mouse position
            bodyB: particle,                   // Connect to particle
            stiffness: constraintStiffness,    // Distance-based stiffness
            damping: DRAG_CONFIG.DAMPING,      // Reduce oscillation
            length: distance * 0.5             // Allow some slack based on distance
        });
        
        // Store constraint and particle info
        mouseConstraints.push(constraint);
        draggedParticles.add(particle);
        initialGrabOffsets.set(particle.id, offset);
        Composite.add(engine.world, constraint);
        
        // Stop particle's current motion
        Body.setVelocity(particle, { x: 0, y: 0 });
    });
}

This system maintains a minimum number of grab points to ensure stable behavior while limiting the maximum to prevent performance issues. The stiffness of each constraint is calculated based on the particle’s distance from the grab point, creating a more realistic deformation pattern where closer particles are more strongly influenced by the mouse movement.

Challenges

While performance optimization was addressed through careful limiting of active constraints, the primary challenge was in achieving authentic cloth behavior. Real fabric exhibits complex properties that proved difficult to replicate – it stretches but maintains its shape, folds naturally along stress lines, and responds to forces with varying degrees of resistance depending on the direction of the force. The initial implementation used uniform spring constants throughout the cloth, resulting in a rubber-like behavior that felt artificial and bouncy. Achieving natural draping behavior required extensive experimentation with different constraint configurations, ultimately leading to a system where horizontal and vertical constraints had different properties than diagonal ones. The way cloth bunches and folds was another significant challenge – early versions would either stretch indefinitely or resist folding altogether. This was solved by implementing a constraint lengths and stiffness values, allowing the cloth to maintain its overall structure while still being able to fold naturally. The grab mechanics also required considerable refinement to feel natural – initial versions would either grab too rigidly, causing the cloth to behave like a solid sheet, or too loosely, resulting in unrealistic stretching like a pointy tear. The solution involved implementing distance-based stiffness calculations and maintaining relative positions between grabbed particles, creating more natural deformation patterns during interaction.

Reflection and Future Considerations

The current implementation successfully demonstrates complex physics interactions in an accessible and intuitive way, but there remain numerous opportunities for enhancement. Future development could incorporate air resistance for more realistic cloth movement, along with self-collision detection to enable proper folding behavior. The addition of tear mechanics would introduce another layer of physical simulation, allowing the cloth to react more realistically to extreme forces. From a performance perspective, implementing spatial partitioning for collision detection and utilizing Web Workers for physics calculations could significantly improve efficiency, especially when dealing with larger cloth sizes. The interactive aspects could be expanded by implementing multiple cloth layers, cutting mechanics, and advanced texture mapping and shading systems. There’s also significant potential for educational applications, such as adding visualizations of forces and constraints, creating interactive tutorials about physics concepts, and implementing different material properties for comparison. Additionally, there is no depth to current implementation because this is inherently a 2D library. For depth-based clashing (which is what happens in real world) we would need to find 3D library.
These enhancements would further strengthen the project’s value as both a technical demonstration and an educational tool, illustrating how complex physical behaviors can be effectively simulated through carefully crafted rules and constraints.

Week- 10 Net Blaster

Concept

For my Week 10 assignment, I decided to create a game inspired by football (soccer) and Daniel Shiffman’s Angry Birds slingshot mechanic. I came across a matter.js cloth sketch that moved like a football net, which sparked the idea to bring that into my sketch. I wanted to add a twist: instead of just launching the ball like in Angry Birds, the ball would interact with the goal net, creating a realistic effect. When the ball hits the net, it doesn’t just pass through—it actually impacts the net, which bends and moves as if hit by a real object. This reaction gives the game extra depth and makes scoring feel even more satisfying. Players use the slingshot to aim and score while racing against a 10-second timer. A goal counter keeps track of the number of goals scored, so you can try to beat your best score.

Embedded Sketch

Key Features

  • Slingshot Mechanic: You use the slingshot to aim and launch the ball, trying to score goals by hitting the target.
  • Interactive Goal Net: The net moves and reacts when the ball hits it, making the game feel more dynamic and realistic.
  • Scoring: Each time you score, your goal count increases, and you get a satisfying visual of your progress.
  • Physics-Based Collision: The ball interacts with both the goal and the net particles, which makes for a more immersive experience.

Code I’m Proud Of:

The piece of code I’m really proud of in this project is the way the ball interacts with the net. I wanted to make it so that when the ball hits the net, the net doesn’t just stay rigid or let the ball pass through like it’s not even there. Instead, it reacts and moves as though it’s actually impacted, which makes the game feel more real and satisfying.

Here’s the simplified idea: whenever the ball hits a certain area, which I’ve defined as the “goal box,” the code checks if the ball is near any of the net’s “particles.” These particles represent different points on the net that can move when hit by the ball. If the ball is close enough, a force is applied to these particles, which makes them move in response, simulating a realistic impact on the net. The ball’s velocity is then reduced to simulate a “catch,” which gives a nice effect of the ball being absorbed by the net.

// Define the area for the goal box (where the net is located)
let goalBoxX = width / 4 - 40;
let goalBoxY = 20;
let goalBoxW = goalBoxWidth;
let goalBoxH = goalBoxHeight;

if (
  ball.body.position.x > goalBoxX &&
  ball.body.position.x < goalBoxX + goalBoxW &&
  ball.body.position.y > goalBoxY &&
  ball.body.position.y < goalBoxY + goalBoxH
) {
  // If the ball hasn't already scored, increase the goal count
  if (!ball.scored) {
    goalCount++;
    ball.scored = true;
  }

  // Check for a "collision" with the net particles
  for (let i = 0; i < cols; i++) {
    for (let j = 0; j < rows; j++) {
      let particle = particles[i][j];
      let distance = dist(
        ball.body.position.x,
        ball.body.position.y,
        particle.x,
        particle.y
      );

      if (distance < ball.r + w / 2) {
        // Apply force to the particle to simulate movement
        let forceX = (particle.x - ball.body.position.x) * 2;
        let forceY = (particle.y - ball.body.position.y) * 2;
        particle.addForce(new Vec2D(forceX, forceY));

        // Slow the ball down to simulate a "catch"
        Matter.Body.setVelocity(ball.body, { x: 0, y: 0 });
      }
    }
  }
}

Future Work / Reflections

Creating this game was both fun and challenging, especially figuring out how to make the net realistically react to the ball. Seeing it finally work as I imagined was super rewarding! Moving forward, I’d like to add more levels and maybe obstacles to make scoring harder. I would like to play with the positioning of the ball when it regenerates and maybe try to randomize that however, overall I am really proud of what I created.

Resources:

Free Flowing Flocking System

The Concept

I wanted to create a system that goes beyond traditional boid flocking behavior. The goal was to craft a visual experience that tells a story through motion, tension, and release. The system transitions between free-flowing states and complex mathematical formations, all while maintaining the organic feel of natural flocking behavior.

The particles move through several distinct phases:

  1. Free-flowing exploration
  2. Lissajous curve formations
  3. Logarithmic spiral patterns
  4. Mandala-like circular arrangements
  5. A parametric heart shape with controlled chaos

Each phase has its own character, with carefully tuned parameters for alignment, cohesion, and separation that give it a unique feel while maintaining visual coherence with the whole piece.

Technical Highlight

One of the most challenging aspects was creating smooth transitions when particles wrap around screen edges. Traditional edge wrapping creates jarring visual artifacts – straight lines cutting across the screen. The solution involves calculating the closest position between two particles, considering wrapped positions around screen edges. This ensures smooth flocking behavior even when some particles are on opposite sides of the screen. When combined with history clearing during wrapping, it eliminates the visual artifacts that often plague edge-wrapping systems.

Color and Visual Design

The visual design draws inspiration from natural phenomena – deep sea bioluminescence, sunset plasma, and aurora borealis. Each color scheme transitions smoothly into the next, creating a constantly evolving palette that complements the movement patterns.

The particle trails add a sense of history to the motion, creating organic flowing lines that help viewers understand the system’s evolution. The trail length and opacity are carefully tuned to create a balance between showing motion history and maintaining visual clarity.

Reflections and Future Work

While I’m pleased with the current state of the project, there are several exciting directions for future development:

  1. 3D Extensions: Expanding the system into three dimensions would open up possibilities for more complex formations and viewpoint changes.
  2. Interactive Elements: Adding user interaction through mouse movement or touch could create interesting disruptions in the flocking patterns.
  3. Performance Optimization: While the current system handles 150 particles smoothly, optimizing the spatial partitioning could allow for thousands of particles, creating even more complex emergent patterns.

The most interesting aspect of this project has been finding the sweet spot between chaos and order – allowing enough randomness to keep the system feeling organic while maintaining the mathematical beauty of the formations. It’s a delicate balance that mirrors many natural systems, where simple rules give rise to complex, beautiful behaviors.

Sketch – Week 9

Concept

For this week’s assignment, I was inspired by Ryoichi Kurokawa’s “syn_mod.n” artwork. I liked how fluid the artwork looked, and the intricacy of the webs. I intended to create similar visual shapes and manipulate them through separation and alignment.

Code Snippet 

class Boid {
  constructor(x, y) {
    this.position = createVector(x, y);
    this.velocity = p5.Vector.random2D(); 
    this.acceleration = createVector(0, 0);

    // Define maximum speed and steering force for the boid
    this.maxSpeed = 3;
    this.maxForce = 0.05;

    // Set initial weights for flocking behaviors
    this.setBehaviorWeights(20, 1, 1);
  }

  run(boids) {
    // Control boid's behavior, movement, and visual display
    this.flock(boids);      
    this.update();         
    this.wrapAround();     
    this.showConnections(boids); // Draw connecting lines between boids
  }

  setBehaviorWeights(sepWeight, aliWeight, cohWeight) {
    // Set weights for separation, alignment, and cohesion behaviors
    this.sepWeight = sepWeight;
    this.aliWeight = aliWeight;
    this.cohWeight = cohWeight;
  }

  flock(boids) {
    // Calculate the three main flocking behaviors
    let sep = this.separate(boids).mult(this.sepWeight); // Separation: avoid crowding
    let ali = this.align(boids).mult(this.aliWeight);    // Alignment: steer towards average heading
    let coh = this.cohere(boids).mult(this.cohWeight);   // Cohesion: move towards group center

    // Apply the combined steering forces to the boid
    this.applyForce(sep.add(ali).add(coh));
  }

Embedded Sketch 

Reflections 

I enjoyed learning the flocking behaviours. To improve my code, I would add interaction to it. I wanted to add a mousePressed interaction but I was struggling to get the visual effect I wanted. I also wanted the web to look more organic, and I would change the code using perlin noise to achieve this effect.

Dynamic Flocking

Concept

Here I tried to simulate flocking behavior by creating a “school” of boids that move together in a coordinated, natural pattern. Each boid follows three core rules—separation, alignment, and cohesion—to avoid crowding, match direction, and stay close to neighbors. Together, these rules give rise to a lifelike group movement, where each boid is influenced by its surroundings to form a cohesive, dynamic flock.

Code Breakdown

Each Boid has properties for position, velocity, and acceleration, and the draw loop continuously applies flocking behaviors, updates the boid’s position, and renders it on the canvas with a trailing effect for smooth movement. The flock method combines the three behaviors by calculating forces based on nearby boids within defined distances. The separation force keeps boids from colliding, alignment adjusts direction to match neighboring boids, and cohesion steers toward the group center. The wrapEdgesmethod ensures that boids reappear on the opposite side if they move off-screen. An interactive feature, mouseDragged, adds new boids at the mouse location when dragged, adding flexibility to the simulation.

let boids = []; // Array to store boid instances
let w = 600, h = 600;

function setup() {
  createCanvas(w, h);
  background(0);

  // Initialize boids with random positions
  for (let i = 0; i < 50; i++) {
    boids.push(new Boid(random(width), random(height)));
  }
}

function draw() {
  background(255, 5); // Slight trail effect
  
  boids.forEach(boid => {
    boid.flock(boids);  // Apply flocking behavior
    boid.update();      // Update position and velocity
    boid.wrapEdges();   // Wrap around edges of the canvas
    boid.show();        // Draw the boid
  });
}

// Constants for boid behavior
let M = 2;        // Max speed
let F = 0.28;     // Max force
let sepDist = 25; // Desired separation distance
let aliDist = 50; // Alignment neighbor distance
let cohDist = 150; // Cohesion neighbor distance

// Class to represent a single boid
class Boid {
  constructor(x, y) {
    this.position = createVector(x, y);
    this.velocity = createVector(random(-1, 1), random(-1, 1));
    this.acceleration = createVector(0, 0);
  }

  // Method to apply flocking behavior
  flock(boids) {
    let separation = this.separate(boids); // Separation
    let alignment = this.align(boids);     // Alignment
    let cohesion = this.cohesion(boids);   // Cohesion

    // Adjust weights for the forces
    separation.mult(1.5);
    alignment.mult(0.99);
    cohesion.mult(0.99);

    // Apply forces to acceleration
    this.acceleration.add(separation);
    this.acceleration.add(alignment);
    this.acceleration.add(cohesion);
  }

  // Update position based on velocity and acceleration
  update() {
    this.velocity.add(this.acceleration);
    this.velocity.limit(M); // Limit speed
    this.position.add(this.velocity);
    this.acceleration.mult(0); // Reset acceleration
  }

  // Wrap boids around the screen edges
  wrapEdges() {
    this.position.x = (this.position.x + w) % w;
    this.position.y = (this.position.y + h) % h;
  }

  // Draw boid as a small circle
  show() {
    fill(255, 0, 0);
    ellipse(this.position.x, this.position.y, 10);
  }

  // Separation behavior: Avoid crowding neighbors
  separate(boids) {
    let steer = createVector(0, 0);
    let count = 0;
    boids.forEach(other => {
      let distance = p5.Vector.dist(this.position, other.position);
      if (distance > 0 && distance < sepDist) {
        let diff = p5.Vector.sub(this.position, other.position);
        diff.normalize();
        diff.div(distance);
        steer.add(diff);
        count++;
      }
    });
    if (count > 0) steer.div(count);
    if (steer.mag() > 0) {
      steer.normalize();
      steer.mult(M);
      steer.sub(this.velocity);
      steer.limit(F);
    }
    return steer;
  }

  // Alignment behavior: Steer towards the average heading of local flockmates
  align(boids) {
    let sum = createVector(0, 0);
    let count = 0;
    boids.forEach(other => {
      let distance = p5.Vector.dist(this.position, other.position);
      if (distance > 0 && distance < aliDist) {
        sum.add(other.velocity);
        count++;
      }
    });
    if (count > 0) {
      sum.div(count);
      sum.normalize();
      sum.mult(M);
      let steer = p5.Vector.sub(sum, this.velocity);
      steer.limit(F);
      return steer;
    }
    return createVector(0, 0);
  }

  // Cohesion behavior: Steer towards the average position of local flockmates
  cohesion(boids) {
    let sum = createVector(0, 0);
    let count = 0;
    boids.forEach(other => {
      let distance = p5.Vector.dist(this.position, other.position);
      if (distance > 0 && distance < cohDist) {
        sum.add(other.position);
        count++;
      }
    });
    if (count > 0) {
      sum.div(count);
      return this.seek(sum);
    }
    return createVector(0, 0);
  }

  // Seek method to steer boid towards a target position
  seek(target) {
    let desired = p5.Vector.sub(target, this.position);
    desired.normalize();
    desired.mult(M);
    let steer = p5.Vector.sub(desired, this.velocity);
    steer.limit(F);
    return steer;
  }
}

// Function to add a new boid on mouse drag
function mouseDragged() {
  boids.push(new Boid(mouseX, mouseY));
}

Future Improvements

While the current simulation focuses on realistic flocking dynamics, future enhancements will center around visual and auditory immersion. Adding a colorful background and subtle sound effects could heighten the simulation’s atmosphere, making the school of boids feel more alive and enhancing the viewer’s experience.