<svg id="svg" width="" height="" viewBox="">
  <rect id="bg" width="" height="" fill="#eaeaea" stroke="black" />
  <g id="particleHolder"></g>
</svg>
<button id="button" class="text-black bg-gray-200 py-2 px-12 my-5 hover:bg-gray-300 rounded">replay</button>
<div id="rendererHolder"></div>

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

tailwind - 
https://cdn.tailwindcss.com
*/

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

// particle vars
let particleGraphic, particleBody;
const particleRadius = 15;
const particleHolder = document.querySelector("#particleHolder");

// boundaries
let leftwall, rightwall, floor;
const wallThickness = 100;

// svg variables
const viewportWidth = 400;
const viewportHeight = 400;
const w = 400;
const h = 400;
const namespace = "http://www.w3.org/2000/svg";

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

function initSVG() {
  const svg = document.querySelector("svg");
  svg.setAttribute("width", `${viewportWidth}`);
  svg.setAttribute("height", `${viewportHeight}`);
  svg.setAttribute("viewBox", `0 0 ${w} ${h}`);
  const bg = document.querySelector("#bg");
  bg.setAttribute("width", w);
  bg.setAttribute("height", h);
  bg.setAttribute("fill", "#eaeaea");
}

function initRenderer() {
  // create renderer
  const rendererHolder = document.querySelector("#rendererHolder");
  const render = Render.create({
    element: rendererHolder,
    engine: engine,
    options: {
      width: w,
      height: h,
      showAngleIndicator: true
    }
  });

  Render.run(render);
}

function initParticle() {
  if (particleGraphic) {
    particleHolder.removeChild(particleGraphic);
    particleGraphic = null;
    Composite.remove(engine.world, particleBody);
  }
  // dimensions, etc
  const xpos = w / 2;
  const ypos = -particleRadius;
  // randomly decide how many sides the particle will have
  const sides = 3 + Math.round(Math.random() * 7);
  const radius = particleRadius + Math.random() * particleRadius;
  const restitution = 0.9;
  const color = getRandomColor();

  // body
  particleBody = getPolygonBody(0, 0, sides, radius, restitution, 0);
  // get all the verticies so we can use them to create the svg polygon
  const vertices = particleBody.vertices;

  // graphic
  particleGraphic = getPolygonGraphic(xpos, ypos, vertices, color);
  particleHolder.appendChild(particleGraphic);

  // give the body an initial random rotation and position
  Body.setAngle(particleBody, Math.random() * 2 * Math.PI);
  Body.setPosition(particleBody, { x: xpos, y: ypos });
}

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 initUI() {
  replayButton.addEventListener("click", () => {
    initParticle();
    Composite.add(engine.world, [particleBody]);
    Body.setPosition(particleBody, {
      x: 20 + Math.random() * (w - 40),
      y: -particleRadius
    });
    Body.setSpeed(particleBody, 0);
    Body.setVelocity(particleBody, { x: 0, y: 0 });
  });
}

function getPolygonBody(
  xpos,
  ypos,
  sides,
  radius,
  restitution,
  index,
  isStatic = false
) {
  const b = Bodies.polygon(xpos, ypos, sides, radius, {
    id: `polygon_${index}`,
    friction: 1,
    restitution: restitution,
    isStatic: isStatic
  });

  return b;
}

function getPolygonGraphic(xpos, ypos, vertices, color = "black") {
  let points = "";

  vertices.forEach((vertex) => {
    console.log("vertex: ", vertex);
    points = `${points} ${vertex.x},${vertex.y}`;
  });

  const pg = document.createElementNS(namespace, "polygon");
  pg.setAttribute("points", points);
  pg.setAttribute("fill", color);
  pg.setAttribute("stroke", "#212121");

  const g = document.createElementNS(namespace, "g");
  g.appendChild(pg);
  g.setAttribute("transform", `translate(${xpos} ${ypos})`);

  return g;
}

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

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

function update() {
  Engine.update(engine);

  const pos = particleBody.position;
  const radians = particleBody.angle;
  const degrees = (radians * 180) / Math.PI;

  particleGraphic.setAttribute(
    "transform",
    `translate(${pos.x} ${pos.y}) rotate(${degrees})`
  );

  window.requestAnimationFrame(update);
}

initSVG();
initRenderer();
initParticle();
initWallAndFloor();
initWorld();
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
  2. https://cdn.tailwindcss.com