# Balls bouncing off each other, without using Box2D or other libraries

**URL:** <https://discourse.processing.org/t/balls-bouncing-off-each-other-without-using-box2d-or-other-libraries/24286>\
**Category:** Coding Questions\
**Created:** [October 2, 2020, 2:56pm UTC](https://discourse.processing.org/t/balls-bouncing-off-each-other-without-using-box2d-or-other-libraries/24286 "2020-10-02T14:56:59Z")\
**Posts on this page:** 12\
**Page:** 1

<div class="post-metadata">

**Author:** ![HumanoidX](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/humanoidx/32/8506_2.png) [@HumanoidX](https://discourse.processing.org/u/HumanoidX)\
**Post date:** [October 2, 2020, 2:56pm UTC](https://discourse.processing.org/t/balls-bouncing-off-each-other-without-using-box2d-or-other-libraries/24286/1 "2020-10-02T14:56:59Z")

</div>

This is my code:

```auto
Circle[] circles = {
  new Circle(40,color(0),color(255,0,0),0,2,0,0.5,new PVector(250,300),new PVector(0,0)),
  new Circle(200,color(0),color(255,0,0),0,10,0,0.5,new PVector(500,300),new PVector(0,0)),
  new Circle(20,color(0),color(255,0,0),0,0.1,0,0.5,new PVector(750,300),new PVector(0,0)),
};

PVector wind = (PVector.random2D()).div(20);
PVector gravity = new PVector(0,0.2);

void setup(){
  size(1000,600);
}

void draw(){
  background(0);
  circles[0].applyForce(wind);
  circles[0].applyForce(gravity);
  circles[0].update();
  circles[1].applyForce(wind);
  circles[1].applyForce(gravity);
  circles[1].update();
  circles[2].applyForce(wind);
  circles[2].applyForce(gravity);
  circles[2].update();
}

class Circle{
  float radius;
  color colorStroke;
  color colorFill;
  float thickness;
  
  float mass;
  float friction;
  float restitution;
  
  PVector position,velocity;
  
  Circle(float r,color cs,color cf,float t,float m,float f,float re,PVector p,PVector v){
    radius = r;
    colorStroke = cs;
    colorFill = cf;
    thickness = t;
    
    mass = m;
    friction = f;
    restitution = re;
    
    position = p;
    velocity = v;
  }
  
  void update(){
    if(thickness == 0){
      noStroke();
    }else{
      strokeWeight(thickness);
    }
    stroke(colorStroke);
    fill(colorFill);
    ellipse(position.x,position.y,radius,radius);
    
    position.add(velocity);
    
    if(position.y > (height - (radius / 2)) && velocity.y > 0){
      velocity.y = velocity.y * -restitution;
      position.y = height - (radius / 2);
    }
    if(position.y < (radius / 2) && velocity.y < 0){
      velocity.y = velocity.y * -restitution;
      position.y = radius / 2;
    }
    if(position.x > (width - (radius / 2)) && velocity.x > 0){
      velocity.x = velocity.x * -restitution;
      position.x = width - (radius / 2);
    }
    if(position.x < (radius / 2) && velocity.x < 0){
      velocity.x = velocity.x * -restitution;
      position.x = radius / 2;
    }
  }
  
  void applyForce(PVector force){
      velocity.x = velocity.x + (force.x / mass);
      velocity.y = velocity.y + (force.y);
  }
}

```

The edge bouncing is finished, but I want the balls to bounce off each other. So how do I make them detect collision and bounce?

---

<div class="post-metadata">

**Author:** ![HumanoidX](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/humanoidx/32/8506_2.png) [@HumanoidX](https://discourse.processing.org/u/HumanoidX)\
**Post date:** [October 2, 2020, 3:06pm UTC](https://discourse.processing.org/t/balls-bouncing-off-each-other-without-using-box2d-or-other-libraries/24286/2 "2020-10-02T15:06:00Z")

</div>

Please help 😕 😕 😕 😶

---

<div class="post-metadata">

**Author:** ![raron](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/raron/32/13651_2.png) [@raron](https://discourse.processing.org/u/raron)\
**Post date:** [October 2, 2020, 4:17pm UTC](https://discourse.processing.org/t/balls-bouncing-off-each-other-without-using-box2d-or-other-libraries/24286/3 "2020-10-02T16:17:07Z")

</div>

Writing “collision detection” (or just “collision det”) in the search box above produce some hits.

Here’s one which might be of interest: [Collision Detection (Game Design)](https://discourse.processing.org/t/collision-detection-game-design/24142)

---

<div class="post-metadata">

**Author:** ![paulgoux](https://avatars.discourse-cdn.com/v4/letter/p/b9bd4f/32.png) [@paulgoux](https://discourse.processing.org/u/paulgoux)\
**Post date:** [October 2, 2020, 9:44pm UTC](https://discourse.processing.org/t/balls-bouncing-off-each-other-without-using-box2d-or-other-libraries/24286/4 "2020-10-02T21:44:07Z")

</div>

from the examples section in processing

```auto
/**
 * Bouncy Bubbles  
 * based on code from Keith Peters. 
 * 
 * Multiple-object collision.
 */
 
 
int numBalls = 12;
float spring = 0.05;
float gravity = 0.03;
float friction = -0.9;
Ball[] balls = new Ball[numBalls];

void setup() {
  size(640, 360);
  for (int i = 0; i < numBalls; i++) {
    balls[i] = new Ball(random(width), random(height), random(30, 70), i, balls);
  }
  noStroke();
  fill(255, 204);
}

void draw() {
  background(0);
  for (Ball ball : balls) {
    ball.collide();
    ball.move();
    ball.display();  
  }
}

class Ball {
  
  float x, y;
  float diameter;
  float vx = 0;
  float vy = 0;
  int id;
  Ball[] others;
 
  Ball(float xin, float yin, float din, int idin, Ball[] oin) {
    x = xin;
    y = yin;
    diameter = din;
    id = idin;
    others = oin;
  } 
  
  void collide() {
    for (int i = id + 1; i < numBalls; i++) {
      float dx = others[i].x - x;
      float dy = others[i].y - y;
      float distance = sqrt(dx*dx + dy*dy);
      float minDist = others[i].diameter/2 + diameter/2;
      if (distance < minDist) { 
        float angle = atan2(dy, dx);
        float targetX = x + cos(angle) * minDist;
        float targetY = y + sin(angle) * minDist;
        float ax = (targetX - others[i].x) * spring;
        float ay = (targetY - others[i].y) * spring;
        vx -= ax;
        vy -= ay;
        others[i].vx += ax;
        others[i].vy += ay;
      }
    }   
  }
  
  void move() {
    vy += gravity;
    x += vx;
    y += vy;
    if (x + diameter/2 > width) {
      x = width - diameter/2;
      vx *= friction; 
    }
    else if (x - diameter/2 < 0) {
      x = diameter/2;
      vx *= friction;
    }
    if (y + diameter/2 > height) {
      y = height - diameter/2;
      vy *= friction; 
    } 
    else if (y - diameter/2 < 0) {
      y = diameter/2;
      vy *= friction;
    }
  }
  
  void display() {
    ellipse(x, y, diameter, diameter);
  }
}

```

---

<div class="post-metadata">

**Author:** ![HumanoidX](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/humanoidx/32/8506_2.png) [@HumanoidX](https://discourse.processing.org/u/HumanoidX)\
**Post date:** [October 3, 2020, 3:23am UTC](https://discourse.processing.org/t/balls-bouncing-off-each-other-without-using-box2d-or-other-libraries/24286/5 "2020-10-03T03:23:12Z")

</div>

What is the array “other” for?

---

<div class="post-metadata">

**Author:** ![HumanoidX](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/humanoidx/32/8506_2.png) [@HumanoidX](https://discourse.processing.org/u/HumanoidX)\
**Post date:** [October 3, 2020, 3:24am UTC](https://discourse.processing.org/t/balls-bouncing-off-each-other-without-using-box2d-or-other-libraries/24286/6 "2020-10-03T03:24:07Z")

</div>

And also the float “spring” ?

---

<div class="post-metadata">

**Author:** ![paulgoux](https://avatars.discourse-cdn.com/v4/letter/p/b9bd4f/32.png) [@paulgoux](https://discourse.processing.org/u/paulgoux)\
**Post date:** [October 3, 2020, 8:20pm UTC](https://discourse.processing.org/t/balls-bouncing-off-each-other-without-using-box2d-or-other-libraries/24286/7 "2020-10-03T20:20:33Z")

</div>

not sure as I’ve not had time to study it you might have to do a bit of research.

---

<div class="post-metadata">

**Author:** ![HumanoidX](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/humanoidx/32/8506_2.png) [@HumanoidX](https://discourse.processing.org/u/HumanoidX)\
**Post date:** [October 4, 2020, 12:28am UTC](https://discourse.processing.org/t/balls-bouncing-off-each-other-without-using-box2d-or-other-libraries/24286/8 "2020-10-04T00:28:58Z")

</div>

Alright. Can you tell me where you got this script (Or you wrote it yourself)?

---

<div class="post-metadata">

**Author:** ![paulgoux](https://avatars.discourse-cdn.com/v4/letter/p/b9bd4f/32.png) [@paulgoux](https://discourse.processing.org/u/paulgoux)\
**Post date:** [October 4, 2020, 12:37am UTC](https://discourse.processing.org/t/balls-bouncing-off-each-other-without-using-box2d-or-other-libraries/24286/9 "2020-10-04T00:37:55Z")

</div>

open the examples in your processing ide, the application includes a ton of examples.

---

<div class="post-metadata">

**Author:** ![raron](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/raron/32/13651_2.png) [@raron](https://discourse.processing.org/u/raron)\
**Post date:** [October 4, 2020, 12:38am UTC](https://discourse.processing.org/t/balls-bouncing-off-each-other-without-using-box2d-or-other-libraries/24286/10 "2020-10-04T00:38:40Z")

</div>

> [@HumanoidX](#):
>
> What is the array “other” for?

It’s the same as the “balls” array. If you look in the `collide()` method, you’ll see that it’s used for getting the position of the other “balls” objects.

Btw notice how the for loop starts at `id + 1`, to avoid collision detection twice for the same two objects.

But actually it isn’t needed. You could just use `balls[i]` instead of `others[i]` in the collision() method. I guess it was done to make the code more readable.

> **Click to (un) expand**
>
> ```auto
> /**
> * Bouncy Bubbles mod without "others" array 
> * based on code from Keith Peters. 
> * 
> * Multiple-object collision.
> */
>  
>  
> int numBalls = 12;
> float spring = 0.05;
> float gravity = 0.03;
> float friction = -0.9;
> Ball[] balls = new Ball[numBalls];
> 
> void setup() {
> size(640, 360);
> for (int i = 0; i < numBalls; i++) {
> balls[i] = new Ball(random(width), random(height), random(30, 70), i);
> }
> noStroke();
> fill(255, 204);
> }
> 
> void draw() {
> background(0);
> for (Ball ball : balls) {
> ball.collide();
> ball.move();
> ball.display();  
> }
> }
> 
> class Ball {
>   
> float x, y;
> float diameter;
> float vx = 0;
> float vy = 0;
> int id;
>  
> Ball(float xin, float yin, float din, int idin) {
> x = xin;
> y = yin;
> diameter = din;
> id = idin;
> } 
>   
> void collide() {
> for (int i = id + 1; i < numBalls; i++) {
> float dx = balls[i].x - x;
> float dy = balls[i].y - y;
> float distance = sqrt(dx*dx + dy*dy);
> float minDist = balls[i].diameter/2 + diameter/2;
> if (distance < minDist) { 
> float angle = atan2(dy, dx);
> float targetX = x + cos(angle) * minDist;
> float targetY = y + sin(angle) * minDist;
> float ax = (targetX - balls[i].x) * spring;
> float ay = (targetY - balls[i].y) * spring;
> vx -= ax;
> vy -= ay;
> balls[i].vx += ax;
> balls[i].vy += ay;
> }
> }   
> }
>   
> void move() {
> vy += gravity;
> x += vx;
> y += vy;
> if (x + diameter/2 > width) {
> x = width - diameter/2;
> vx *= friction; 
> }
> else if (x - diameter/2 < 0) {
> x = diameter/2;
> vx *= friction;
> }
> if (y + diameter/2 > height) {
> y = height - diameter/2;
> vy *= friction; 
> } 
> else if (y - diameter/2 < 0) {
> y = diameter/2;
> vy *= friction;
> }
> }
>   
> void display() {
> ellipse(x, y, diameter, diameter);
> }
> }
> 
> ```

> [@HumanoidX](#):
>
> And also the float “spring” ?

If you play with the value, you’ll see how it affects the collisions. Try a value 10x the original, then 1/10th the original, for example.

Or look in the collision() method.

```auto
        float ax = (targetX - others[i].x) * spring;
        float ay = (targetY - others[i].y) * spring;

```

“spring” is used to modify how much “force” (or rather velocity) is applied at each moment (frame).

It’s essentially just Newton’s 3rd law of motion, the one about applying a force to an object, results in the object applying the same but opposite force back (action - reaction).  
Basically swapping the result of the collision “force” between the objects in the next lines.

```auto
        vx -= ax;
        vy -= ay;
        others[i].vx += ax;
        others[i].vy += ay;

```

That’s what I got out of it anyway…

> [@HumanoidX](#):
>
> Alright. Can you tell me where you got this script (Or you wrote it yourself)?

Since I’m about to post, I’ll answer (Btw he did say where he got it, from the Processing IDE’s examples).

You should find it here: File menu \> “Examples…” → Topics \> Motion \> Bouncy bubbles.

---

<div class="post-metadata">

**Author:** ![HumanoidX](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/humanoidx/32/8506_2.png) [@HumanoidX](https://discourse.processing.org/u/HumanoidX)\
**Post date:** [October 4, 2020, 2:10am UTC](https://discourse.processing.org/t/balls-bouncing-off-each-other-without-using-box2d-or-other-libraries/24286/11 "2020-10-04T02:10:03Z")

</div>

Thanks 😃 That explain anything

---

<div class="post-metadata">

**Author:** ![HumanoidX](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/humanoidx/32/8506_2.png) [@HumanoidX](https://discourse.processing.org/u/HumanoidX)\
**Post date:** [October 4, 2020, 2:18am UTC](https://discourse.processing.org/t/balls-bouncing-off-each-other-without-using-box2d-or-other-libraries/24286/12 "2020-10-04T02:18:03Z")

</div>

It worked fine!

Code:

```auto
float fadeSpeed = 20;

float mouseStrength = 100;
float windStrength = 0.1;
float gravityStrength = 0.2;

Circle[] circles = new Circle[12];

PVector wind = (PVector.random2D()).mult(0.1);
PVector gravity = new PVector(0,gravityStrength);

void setup(){
  background(0);
  size(1000,600);
  for(int n = 0;n < circles.length;n++){
    circles[n] = new Circle(
                            random(20,100),
                            color(random(0,255),random(0,255),random(0,255),random(128,255)),
                            color(random(0,255),random(0,255),random(0,255),random(128,255)),
                            random(0,20),
                            random(-5.0,20.0),
                            random(0.0,1.0),
                            new PVector(random(0,width),random(0,height)),
                            new PVector(random(-0.2,-0.2),random(-0.2,0.2))
                           );
  }
}

void draw(){
  fill(0,fadeSpeed);
  rect(0,0,width,height);
  
  for(int n = 0;n < circles.length;n++){
    circles[n].applyForce(wind);
    circles[n].applyForce(gravity);
    circles[n].update();
    circles[n].display();
  }
  
  drawArrow(new PVector(500,200),80,angle(new PVector(0,0),wind),8,color(0));
  drawArrow(new PVector(500,200),80,angle(new PVector(0,0),wind),5,color(255));
  
  if(mousePressed && mouseButton == CENTER){
    wind = new PVector(mouseX - 500,mouseY - 200);
    wind.normalize();
    wind.div(10);
  }else{
    wind.rotate((noise(frameCount) - 0.5) / 10);
  }
}

void drawArrow(PVector position,float distance,float angle,float thickness,color colorStroke){
  strokeWeight(thickness);
  stroke(colorStroke);
  
  pushMatrix();
  translate(position.x,position.y);
  rotate(angle);
  rotate(-QUARTER_PI);
  translate(distance,distance);
  line(-distance,-distance,0,0);
  rotate(HALF_PI + QUARTER_PI);
  line(0,0,20,20);
  rotate(-(PI + HALF_PI));
  line(0,0,20,20);
  popMatrix();
}

float angle(PVector v1, PVector v2) {
  float a = atan2(v2.y, v2.x) - atan2(v1.y, v1.x);
  if (a < 0) a += TWO_PI;
  return a;
}

class Circle{
  float radius;
  color colorStroke;
  color colorFill;
  float thickness;
  
  float mass;
  float restitution;
  
  PVector position,velocity;
  
  Circle(float r,color cs,color cf,float t,float m,float re,PVector p,PVector v){
    radius = r;
    colorStroke = cs;
    colorFill = cf;
    thickness = t;
    
    mass = m;
    restitution = re;
    
    position = p;
    velocity = v;
  }
  
  void update(){
    position.add(velocity);
    
    if(position.y > (height - (radius / 2)) && velocity.y > 0){
      velocity.y = velocity.y * -restitution;
      position.y = height - (radius / 2);
    }
    if(position.y < (radius / 2) && velocity.y < 0){
      velocity.y = velocity.y * -restitution;
      position.y = radius / 2;
    }
    if(position.x > (width - (radius / 2)) && velocity.x > 0){
      velocity.x = velocity.x * -restitution;
      position.x = width - (radius / 2);
    }
    if(position.x < (radius / 2) && velocity.x < 0){
      velocity.x = velocity.x * -restitution;
      position.x = radius / 2;
    }
    
    for (int i = 1;i < circles.length;i++) {
      float dx = circles[i].position.x - position.x;
      float dy = circles[i].position.y - position.y;
      float distance = sqrt(dx*dx + dy*dy);
      float minDist = ((circles[i].radius) / 2) + (radius / 2);
      if (distance < minDist) { 
        float angle = atan2(dy, dx);
        float targetX = position.x + cos(angle) * minDist;
        float targetY = position.y + sin(angle) * minDist;
        float ax = (targetX - circles[i].position.x);
        float ay = (targetY - circles[i].position.y);
        velocity.x -= ax;
        velocity.y -= ay;
        circles[i].velocity.x += ax;
        circles[i].velocity.y += ay;
      }
    }
  }
  
  void applyForce(PVector force){
    velocity.x = velocity.x + (force.x / abs(mass));
    if(mass < 0){
      velocity.y = velocity.y - force.y;
    }
    if(mass == 0){
      velocity.y = velocity.y;
    }
    if(mass > 0){
      velocity.y = velocity.y + force.y;
    }
  }
  
  void display(){
    stroke(colorStroke);
    if(thickness == 0){
      noStroke();
    }else{
      strokeWeight(thickness);
    }
    
    fill(colorFill);
    ellipse(position.x,position.y,radius,radius);
  }
}

```

The arrow I drawn is the wind direction.
