3D SPHERE
COLLISION EVENTS
Detect when two 3D spheres collide in the browser with Three.js — bounce them off each other and the walls, and flash a new random color on every impact.
LIVE DEMO
The two spheres bouncing and colliding behind this page are the live demo. Click and drag with the mouse to orbit the camera around them. Watch them change color each time they collide. Scroll to keep reading — on mobile just swipe to scroll.
Introduction
Collision detection is the foundation of almost every game and physics effect — knowing when two objects touch so you can react. For spheres it is beautifully simple: because a sphere is just a center point and a radius, you can detect a collision with a single distance check.
This free tutorial builds two spheres that drift around a lit ground plane. Each frame you move them along a direction vector, check the distance between them, and when they touch you reverse their directions and flash a random color. You will also bounce them off the edges of the plane like invisible walls. No physics engine required.
Frequently Asked Questions
How do you detect collisions between spheres in Three.js?
For spheres, the simplest and fastest method is a distance check. Each frame, measure the distance between the two sphere centers with ball1.position.distanceTo(ball2.position). If that distance is less than or equal to the sum of their radii, the spheres are touching — a collision. This works because a sphere is defined entirely by its center and radius.
How do you make objects bounce after a collision in Three.js?
Store each object's movement as a direction vector and add it to the position every frame. When a collision is detected, reverse the relevant components of the direction vectors (for example negate x and z) so the objects move apart. Adding a small random nudge to the reversed direction keeps the motion lively and prevents the objects from getting stuck together.
How do you keep objects inside a boundary in Three.js?
Check each object's position against the edges of your ground plane every frame. If it passes an edge threshold, flip the direction component pointing outward (using -Math.sign of the position) and re-normalize the direction vector. This bounces the object back into the play area like an invisible wall.
Should I use a physics engine for collisions in Three.js?
Not always. For simple shapes like spheres and boxes, manual distance and bounding-box checks are fast and easy. For realistic mass, friction, stacking, and complex shapes, use a physics engine such as Cannon-es, Rapier, or Ammo.js and let it handle collisions while Three.js renders the result. Start simple and add a physics engine only when you need real physics.
Is this Three.js collision tutorial free?
Yes. This is a completely free tutorial. The full source code is on the page, you can copy it, and the two colliding spheres rotating behind this page are the live demo. No sign-in or membership is required.
How The Collision Detection Works
- 1Give each sphere a direction vector. Store a small
THREE.Vector3velocity for each ball and add it to the ball's position every frame so it drifts. - 2Measure the distance between centers. Each frame call
ball1.position.distanceTo(ball2.position). If it is ≤ the sum of the two radii, they are touching. - 3Bounce on collision. Reverse the x and z of each direction vector (plus a small random nudge) so the balls separate instead of overlapping.
- 4Flash a color. On impact, pick a random color from an array and set each ball's
material.colorso the collision is visible. - 5Bounce off the walls. Compare each ball's position to the edges of the ground plane; if it passes the edge, flip the outward direction component and re-normalize.
- 6Render in a loop. Run all of the above inside
requestAnimationFrameand callrenderer.render(scene, camera)each frame.
The Full Code
Paste this into a file named index.html and open it in any modern browser.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Three.js Sphere Collision</title>
<style> body { margin: 0; background: #000; } </style>
<script type="importmap">
{
"imports": {
"three": "https://cdn.jsdelivr.net/npm/three@0.160/build/three.module.js",
"three/addons/": "https://cdn.jsdelivr.net/npm/three@0.160/examples/jsm/"
}
}
</script>
</head>
<body>
<script type="module">
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(
75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 25, 50);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
document.body.appendChild(renderer.domElement);
const controls = new OrbitControls(camera, renderer.domElement);
controls.autoRotate = true;
// Lights
scene.add(new THREE.AmbientLight(0xffffff, 0.6));
const spot = new THREE.SpotLight(0xffffff, 10);
spot.position.set(0, 60, 0);
spot.castShadow = true;
scene.add(spot);
// Ground plane
const plane = new THREE.Mesh(
new THREE.PlaneGeometry(100, 100),
new THREE.MeshPhongMaterial({ color: 0x1e40af, side: THREE.DoubleSide })
);
plane.rotation.x = Math.PI / 2;
plane.position.y = -3;
plane.receiveShadow = true;
scene.add(plane);
scene.add(new THREE.GridHelper(100, 30));
// Two spheres
const RADIUS = 5;
const geo = new THREE.SphereGeometry(RADIUS, 32, 32);
const ball1 = new THREE.Mesh(geo, new THREE.MeshPhongMaterial({ color: 'white' }));
const ball2 = new THREE.Mesh(geo, new THREE.MeshPhongMaterial({ color: 'white' }));
ball1.position.set(-15, 1, 0);
ball2.position.set(15, 1, 0);
ball1.castShadow = ball2.castShadow = true;
scene.add(ball1, ball2);
// Direction vectors (velocity)
const speed = 0.4;
const dir1 = new THREE.Vector3(speed, 0, speed * 0.6);
const dir2 = new THREE.Vector3(-speed, 0, -speed * 0.4);
const colors = [0xff0000, 0x00ff00, 0x0000ff, 0xffffff, 0x800080, 0xffa500];
const rand = () => Math.random() * 2 - 1;
const half = 50, edge = RADIUS + 2;
function animate() {
requestAnimationFrame(animate);
controls.update();
// Move
ball1.position.add(dir1);
ball2.position.add(dir2);
// Sphere-to-sphere collision: distance check
if (ball1.position.distanceTo(ball2.position) <= RADIUS * 2) {
dir1.set(-dir1.x + rand(), 0, -dir1.z + rand());
dir2.set(-dir2.x + rand(), 0, -dir2.z + rand());
ball1.material.color.set(colors[Math.floor(Math.random() * colors.length)]);
ball2.material.color.set(colors[Math.floor(Math.random() * colors.length)]);
}
// Bounce off the plane edges (invisible walls)
for (const [ball, dir] of [[ball1, dir1], [ball2, dir2]]) {
if (Math.abs(ball.position.x) > half - edge) { dir.x = -Math.sign(ball.position.x) * speed; }
if (Math.abs(ball.position.z) > half - edge) { dir.z = -Math.sign(ball.position.z) * speed; }
}
renderer.render(scene, camera);
}
animate();
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
</script>
</body>
</html>
What To Try Next
- Add more spheres and loop over every pair to check collisions between all of them.
- Vary the radii and use
r1 + r2as the collision distance so different-sized balls collide correctly. - Give heavier balls more momentum by scaling how much each direction changes on impact (fake mass).
- Swap the distance check for a real physics engine like Cannon-es or Rapier when you need gravity, friction, and stacking.
SEE 3D AND 2D BROWSER GAMES IN ACTION
Built with Three.js and JavaScript — free, no download, plays right in your browser.
Super Soldier Battle
Free 3D multiplayer FPS built with Three.js and WebGL. Destroy the enemy bots and turrets, climb the leaderboard.
Play now →
AI Tic-Tac-Toe
Play the classic game against an AI opponent right in your browser. Free, no download, with an online leaderboard.
Play now →
Zombie Attack
Survive waves of zombies in this free 3D browser shooter built with Three.js. No download, plays instantly.
Play now →
Space Wow
Blast through space in this free 3D Three.js arcade shooter. Free, no download, plays right in your browser.
Play now →