Starting from a sphere floating in mid-air, add a gravity force that accelerates the ball downward each frame. When it hits the floor, apply an energy-absorbing bounce so it eventually comes to rest. Learn Euler integration, velocity accumulation and coefficient of restitution.
-
Ball falls with
vy += g * dtapplied every frame -
Ball bounces off the floor plane with
vy *= -restitution - Ball slows down and settles (restitution < 1)
- Controls: slider for gravity strength (0 → 30 m/s²)
// Three.js r160 is already loaded
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(60, innerWidth/innerHeight, 0.1, 100);
camera.position.set(0, 2, 8);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(innerWidth, innerHeight);
document.body.appendChild(renderer.domElement);
// Ball
const ball = new THREE.Mesh(
new THREE.SphereGeometry(0.5, 32, 32),
new THREE.MeshStandardMaterial({ color: 0x3b82f6 })
);
ball.position.y = 5;
scene.add(ball);
// Floor
scene.add(new THREE.Mesh(
new THREE.PlaneGeometry(20, 20),
new THREE.MeshStandardMaterial({ color: 0x1e293b })
).rotateX(-Math.PI / 2));
scene.add(new THREE.AmbientLight(0xffffff, 0.5));
scene.add(Object.assign(new THREE.DirectionalLight(0xffffff, 1), { position: { set: (x,y,z) => { } } }));
// TODO: add gravity physics here
let vy = 0;
const GRAVITY = 9.8; // m/s²
const RESTITUTION = 0.7; // bounciness
const FLOOR_Y = 0.5; // ball radius
let last = performance.now();
function animate() {
requestAnimationFrame(animate);
const now = performance.now();
const dt = Math.min((now - last) / 1000, 0.05);
last = now;
// ← Your code goes here
renderer.render(scene, camera);
}
animate();
vy -= GRAVITY * dt. Then move the ball:
ball.position.y += vy * dt. The minus sign
makes gravity pull downward (negative Y).
if (ball.position.y < FLOOR_Y). If true,
clamp it back: ball.position.y = FLOOR_Y, then
flip and attenuate velocity:
vy = Math.abs(vy) * RESTITUTION. If
vy < 0.1, set it to 0 so the ball truly
stops.
<input type="range" min="0" max="30"
value="9.8">
overlay on top of the canvas. Read its value inside the
animation loop as
const g = parseFloat(slider.value). Don't
forget a <label> for accessibility.
vy -= GRAVITY * dt;
ball.position.y += vy * dt;
if (ball.position.y < FLOOR_Y) {
ball.position.y = FLOOR_Y;
vy = Math.abs(vy) * RESTITUTION;
if (vy < 0.05) vy = 0;
}