title: Why are my objects overlapping in my p5.js project?
body: <p>I'm working on a project where I simulate physics with balls.
  Here is <a href="https://editor.p5js.org/titoineis/sketches/ls4ku7eX-" rel="nofollow noreferrer">the link to the p5 editor</a> of the project.</p>
  
  <p>My problem is the following, when I add a lot of ball (like 200), balls are stacking but some of them will eventually collapse and I don't know why.</p>
  
  <p>Can somebody explain why it does this and how to solve the problem ?</p>
  
  <p>Thanks.</p>
  
  <p>Here is the code of the sketch.</p>
  
  <pre><code>document.oncontextmenu = function () {
      return false;
  }
  
  let isFlushing = false;
  let isBallDiameterRandom = false;
  let displayInfos = true;
  let displayWeight = false;
  let clickOnce = false;
  
  let FRAME_RATE = 60;
  let SPEED_FLUSH = 3;
  let Y_GROUND;
  let lastFR;
  
  let balls = [];
  
  function setup() {
      frameRate(FRAME_RATE);
      createCanvas(window.innerWidth, window.innerHeight);
  
      Y_GROUND = height / 20 * 19;
      lastFR = FRAME_RATE;
  }
  
  function draw() {
      background(255);
  
      if (isFlushing) {
          for (let i = 0; i &lt; SPEED_FLUSH; i++) {
              balls.pop();
          }
  
          if (balls.length === 0) {
              isFlushing = false;
          }
      }
  
      balls.forEach(ball =&gt; {
          ball.collide();
          ball.move();
          ball.display(displayWeight);
          ball.checkCollisions();
      });
  
      if (mouseIsPressed) {
          let ballDiameter;
  
          if (isBallDiameterRandom) {
              ballDiameter = random(15, 101);
          } else {
              ballDiameter = 25;
          }
  
          if (canAddBall(mouseX, mouseY, ballDiameter)) {
              isFlushing = false;
              let newBall = new Ball(mouseX, mouseY, ballDiameter, balls);
  
              if (mouseButton === LEFT &amp;&amp; !clickOnce) {
                  balls.push(newBall);
                  clickOnce = true;
              }
  
              if (mouseButton === RIGHT) {
                  balls.push(newBall);
              }
          }
      }
  
      drawGround();
  
      if (displayInfos) {
          displayShortcuts();
          displayFrameRate();
          displayBallCount();
      }
  }
  
  function mouseReleased() {
      if (mouseButton === LEFT) {
          clickOnce = false;
      }
  }
  
  function keyPressed() {
      if (keyCode === 32) {//SPACE
          displayInfos = !displayInfos;
      }
  
      if (keyCode === 70) {//F
          isFlushing = true;
      }
  
      if (keyCode === 71) {//G
          isBallDiameterRandom = !isBallDiameterRandom;
      }
  
      if (keyCode === 72) {//H
          displayWeight = !displayWeight;
      }
  }
  
  function canAddBall(x, y, d) {
      let isInScreen =
          y + d / 2 &lt; Y_GROUND &amp;&amp;
          y - d / 2 &gt; 0 &amp;&amp;
          x + d / 2 &lt; width &amp;&amp;
          x - d / 2 &gt; 0;
  
      let isInAnotherBall = false;
  
      for (let i = 0; i &lt; balls.length; i++) {
          let d = dist(x, y, balls[i].position.x, balls[i].position.y);
  
          if (d &lt; balls[i].w) {
              isInAnotherBall = true;
              break;
          }
      }
  
      return isInScreen &amp;&amp; !isInAnotherBall;
  }
  
  function drawGround() {
      strokeWeight(0);
      fill('rgba(200,200,200, 0.25)');
      rect(0, height / 10 * 9, width, height / 10);
  }
  
  function displayFrameRate() {
      if (frameCount % 30 === 0) {
          lastFR = round(frameRate());
      }
  
      textSize(50);
      fill(255, 0, 0);
  
      let lastFRWidth = textWidth(lastFR);
      text(lastFR, width - lastFRWidth - 25, 50);
      textSize(10);
      text('fps', width - 20, 50);
  }
  
  function displayBallCount() {
      textSize(50);
      fill(255, 0, 0);
      text(balls.length, 10, 50);
      let twBalls = textWidth(balls.length);
      textSize(10);
      text('balls', 15 + twBalls, 50);
  }
  
  function displayShortcuts() {
      let hStart = 30;
      let steps = 15;
      let maxTW = 0;
      let controlTexts = [
          'LEFT CLICK : add 1 ball',
          'RIGHT CLICK : add 1 ball continuously',
          'SPACE : display infos',
          'F : flush balls',
          'G : set random ball diameter (' + isBallDiameterRandom + ')',
          'H : display weight of balls (' + displayWeight + ')'
      ];
  
      textSize(11);
      fill(0);
  
      for (let i = 0; i &lt; controlTexts.length; i++) {
          let currentTW = textWidth(controlTexts[i]);
  
          if (currentTW &gt; maxTW) {
              maxTW = currentTW;
          }
      }
  
      for (let i = 0; i &lt; controlTexts.length; i++) {
          text(controlTexts[i], width / 2 - maxTW / 2 + 5, hStart);
          hStart += steps;
      }
  
      fill(200, 200, 200, 100);
      rect(width / 2 - maxTW / 2,
          hStart - (controlTexts.length + 1) * steps,
          maxTW + steps,
          (controlTexts.length + 1) * steps - steps / 2
      );
  }
  </code></pre>
  
  <p>Here is the code of the Ball class.</p>
  
  <pre><code>class Ball {
      constructor(x, y, w, e) {
          this.id = e.length;
          this.w = w;
          this.e = e;
  
          this.progressiveWidth = 0;
          this.rgb = [
              floor(random(0, 256)),
              floor(random(0, 256)),
              floor(random(0, 256))
          ];
          this.mass = w;
          this.position = createVector(x + random(-1, 1), y);
          this.velocity = createVector(0, 0);
          this.acceleration = createVector(0, 0);
  
          this.gravity = 0.2;
          this.friction = 0.5;
      }
  
      collide() {
          for (let i = this.id + 1; i &lt; this.e.length; i++) {
              let dx = this.e[i].position.x - this.position.x;
              let dy = this.e[i].position.y - this.position.y;
              let distance = sqrt(dx * dx + dy * dy);
              let minDist = this.e[i].w / 2 + this.w / 2;
  
              if (distance &lt; minDist) {
                  let angle = atan2(dy, dx);
                  let targetX = this.position.x + cos(angle) * minDist;
                  let targetY = this.position.y + sin(angle) * minDist;
  
                  this.acceleration.set(
                      targetX - this.e[i].position.x,
                      targetY - this.e[i].position.y
                  );
                  this.velocity.sub(this.acceleration);
                  this.e[i].velocity.add(this.acceleration);
  
                  //TODO : Effets bizarre quand on empile les boules (chevauchement)
  
                  this.velocity.mult(this.friction);
              }
          }
      }
  
      move() {
          this.velocity.add(createVector(0, this.gravity));
          this.position.add(this.velocity);
      }
  
      display(displayMass) {
          if (this.progressiveWidth &lt; this.w) {
              this.progressiveWidth += this.w / 10;
          }
  
          stroke(0);
          strokeWeight(2);
          fill(this.rgb[0], this.rgb[1], this.rgb[2], 100);
          ellipse(this.position.x, this.position.y, this.progressiveWidth);
  
          if (displayMass) {
              strokeWeight(1);
              textSize(10);
              let tempTW = textWidth(int(this.w));
              text(int(this.w), this.position.x - tempTW / 2, this.position.y + 4);
          }
      }
  
      checkCollisions() {
          if (this.position.x &gt; width - this.w / 2) {
              this.velocity.x *= -this.friction;
              this.position.x = width - this.w / 2;
          } else if (this.position.x &lt; this.w / 2) {
              this.velocity.x *= -this.friction;
              this.position.x = this.w / 2;
          }
  
          if (this.position.y &gt; Y_GROUND - this.w / 2) {
              this.velocity.x -= this.velocity.x / 100;
              this.velocity.y *= -this.friction;
              this.position.y = Y_GROUND - this.w / 2;
          } else if (this.position.y &lt; this.w / 2) {
              this.velocity.y *= -this.friction;
              this.position.y = this.w / 2;
          }
      }
  }
  </code></pre>