Bouncing Balls

Multiple balls bouncing in an enclosed space with ball-to-ball collisions.

What this code does

- Enclosed Room: 3D room with floor, walls, and ceiling for ball containment.
- Ball-to-Ball Collisions: realistic elastic collisions between multiple balls.
- Mass and Momentum: each ball has individual mass affecting collision response.
- Energy Conservation: physics simulation maintains realistic energy transfer.
- Friction Effects: air resistance and ground friction gradually slow movement.
- Interactive Controls: adjust gravity, bounciness, and ball count in real-time.

JavaScript (plain)

const scene = new THREE.Scene()
scene.background = new THREE.Color(0x87ceeb)
const camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000)
camera.position.set(0, 5, 15)
const renderer = new THREE.WebGLRenderer({ antialias: true })
renderer.setSize(width, height)

renderer.shadowMap.enabled = true
document.querySelector('#app').appendChild(renderer.domElement)

let balls = []
let gravity = -0.015
let restitution = 0.85
let roomSize = 8

function createBall() {
  const geometry = new THREE.SphereGeometry(0.5, 16, 16)
  const material = new THREE.MeshStandardMaterial({
    color: Math.random() * 0xffffff
  })
  const ball = new THREE.Mesh(geometry, material)

  ball.position.set(
    (Math.random() - 0.5) * roomSize,
    Math.random() * 5 + 3,
    (Math.random() - 0.5) * roomSize
  )

  ball.userData = {
    velocity: new THREE.Vector3(
      (Math.random() - 0.5) * 0.1,
      0,
      (Math.random() - 0.5) * 0.1
    )
  }

  scene.add(ball)

  balls.push(ball)
}

function animate() {
  requestAnimationFrame(animate)

  balls.forEach(ball => {
    ball.userData.velocity.y += gravity
    ball.position.add(ball.userData.velocity)

    // Floor collision
    if (ball.position.y - 0.5 < -roomSize/2) {
      ball.position.y = -roomSize/2 + 0.5
      ball.userData.velocity.y *= -restitution
    }

    // Wall collisions
    if (Math.abs(ball.position.x) + 0.5 > roomSize/2) {
      ball.position.x = Math.sign(ball.position.x) * (roomSize/2 - 0.5)
      ball.userData.velocity.x *= -restitution
    }
  })

  renderer.render(scene, camera)
}
animate()