<svg width="500" height="500" viewBox="0 0 1000 1000">
  <g id="holder"></g>
</svg>
<button id="replayButton">replay</button>
button {
  margin: 1rem;
  width: 200px;
}
// matter.js cdn:  https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.20.0/matter.min.js

const { Engine, Body, Bodies, Composite} = Matter;
const engine = Engine.create();

// particle vars
const particleGraphics = [];
const particleBodies = [];
const numberOfParticles = 50;
const particleRadius = 20;
const wallThickness = 100;

// boundaries
let leftwall, rightwall, floor;

const namespace = "http://www.w3.org/2000/svg";
const w = 1000;
const h = 1000;

const holder = document.querySelector("#holder");

// ui
const replayButton = document.querySelector("#replayButton");

function initParticleGraphics() {
  for (let i = numberOfParticles; i > 0; i--) {
    const particleGraphic = document.createElementNS(namespace, "circle");
    particleGraphic.setAttribute("cx", 0);
    particleGraphic.setAttribute("cy", 0);
    particleGraphic.setAttribute("r", particleRadius);
    particleGraphic.setAttribute("fill", getRandomColor());
    holder.appendChild(particleGraphic);
    particleGraphics.push(particleGraphic);
  }
};

function getRandomColor() {
  return `hsl(${Math.round(Math.random() * 360)} 50% 50%)`;
};

function initParticleBodies() {
  for (let i = numberOfParticles; i > 0; i--) {
    const particleBody = Bodies.circle(0, 0, particleRadius, {
      id: "particleBody_" + i,
      friction: 0,
      restitution: 0.9
    });
    const xpos = w/3 + Math.random() * w/3;;
    const ypos = Math.random() * (-i * particleRadius*2);
    Body.setPosition(particleBody, { x: xpos, y: ypos });

    particleBodies.push(particleBody);
  }
};

function initWallAndFloor() {
  floor = Bodies.rectangle(w/2, h + wallThickness/2, w, wallThickness, {
    isStatic: true,
    id: "floor"
  });

  leftwall = Bodies.rectangle(-wallThickness/2, h/2, wallThickness, h, {
    isStatic: true,
    id: "leftwall"
  });

  rightwall = Bodies.rectangle(w + wallThickness/2, h/2, wallThickness, h, {
    isStatic: true,
    id: "righttwall"
  });
};

function makeWorld() {
  Composite.add(engine.world, [...particleBodies, leftwall, rightwall, floor]);
};

function initUI() {
  replayButton.addEventListener("click", () => {
    particleBodies.forEach((particleBody, index) => {
      const xpos = w/3 + Math.random() * w/3;
      const ypos = -index * (particleRadius*2);
      Body.setPosition(particleBody, { x: xpos, y: ypos });
      Body.setSpeed(particleBody, 0);
    });
  });
};

function update() {
  // update the engine
  Engine.update(engine);
  // look at the particleBody position and update graphic position accordingly.
  particleBodies.forEach((particleBody, index) => {
    const pos = particleBody.position;
    const particleGraphic = particleGraphics[index];
    particleGraphic.setAttribute("cx", pos.x);
    particleGraphic.setAttribute("cy", pos.y);
  });
  window.requestAnimationFrame(update);
};

initParticleGraphics();
initParticleBodies();
initWallAndFloor();
makeWorld();
initUI();
update();
Run Pen

External CSS

  1. https://codepen.io/aokorodu/pen/poBNOgp.css

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.20.0/matter.min.js