Building a Rolling Ball in Three.js: What Actually Works
I've spent too many weekends debugging sphere collisions. This guide cuts straight to the code that works. A 3D ball roll project is a browser-based game where you control a sphere moving across a surface using keyboard or touch input. The core loop involves applying forces to a rigid body, detecting collisions with the ground plane and obstacles, and rendering everything at 60fps. It sounds simple until gravity behaves weirdly on sloped surfaces. The typical stack is Three.js for rendering plus Cannon-es or Ammo.js for physics. Three.js handles the visuals. Cannon-es handles the simulation. They communicate through a shared transform loop.
The Physics Setup
Most tutorials tell you to slap a SphereCollider on a mesh and call it a day. That works until you need consistent behavior across devices. Here's what actually matters: Set your physics world gravity to new CANNON.Vec3(0, -9.81, 0) and make sure your timestep is locked at 1/60. If you use requestAnimationFrame without a fixed timestep, your ball will roll at different speeds depending on frame rate. That happened to me on a 144Hz monitor during testing. The ball moved roughly 2.4x faster than on a 60Hz screen. Locking the timestep fixed it immediately. Your sphere body needs a radius that matches your visual mesh exactly. If the sphere has radius 0.5 and your mesh has radius 0.48, the collision detection and the visual will be subtly misaligned. Ball will appear to sink into the ground or bounce incorrectly. I wasted an afternoon debugging a sphere that kept jittering on flat ground before realizing the mesh and collider radii didn't match.
The Rendering Loop
Sync your Three.js meshes to your Cannon bodies every frame. Use the same object references, not copies. Here's the pattern that works: Create your physics sphere with shape and mass. Create your visual sphere with material and geometry. Store both references in the same object. In your update loop, after the physics step, copy the position and quaternion from the physics body directly onto the visual mesh. Do not create new meshes in the loop. Do not clone geometries each frame. I've seen people instantiate new BoxGeometries inside the render function and wonder why their framerate drops to single digits after thirty seconds.
Get the Full Details

Controlling the Ball in 3D Ball Roll
Input maps to force, not direct position changes. Apply a vector scaled by your desired acceleration and clamped to your maximum force value. Using raw velocity assignment creates jittery, unrealistic movement. Force application smooths everything out. Here's what my input handler looks like. Read the current WASD or arrow key state. Build a direction vector from those inputs. Multiply by your acceleration constant, say 50 Newtons. Apply that as an impulse to the sphere body along the XZ plane only. Keep the Y component zero so gravity remains the sole vertical force. I had a problem where the ball would spin uncontrollably on certain surfaces. The issue was friction coupling between the sphere and a custom-shaped ground mesh. Setting the sphere's linear factor to {x:1, y:0, z:1} and angular factor to {x:1, y:1, z:1} prevented the physics engine from trying to rotate the ball around axes that shouldn't matter for gameplay. That workaround saved me from rewriting the entire control scheme.
Common Pitfalls
The biggest mistake beginners make is building the entire level geometry as a single compound collision shape. It looks efficient but every time the ball touches any part of that mesh, the broad-phase collider sweep explodes. A level made of fifty separate box colliders runs fine. One giant merged collider with fifty faces chokes the physics solver. Another issue is sleeping. Cannon-es puts bodies to sleep when they stop moving to save CPU. If your ball comes to rest and then suddenly jerks awake, it's because something else in the scene woke the body. Set sleepSpeedLimit and sleepTimeLimit manually on your ball body if you notice erratic wake behavior. Material friction values also matter more than people admit. Default friction is usually 0.3. On a flat plane that feels fine. Tilt the plane to 30 degrees and the ball might not roll at all depending on how your friction coefficients are configured. Test with your actual surface angles, not just flat ground.
Getting It Running
The fastest way to start is scaffolding a project with Vite and importing Cannon-es. You'll need the Three.js renderer, a camera positioned above and behind the ball, lighting that shows surface detail, and a ground plane with a physics body. Then wire your input to apply forces. Then add walls or obstacles as box colliders with static bodies. Performance tips that actually matter: use instanced meshes for repeated geometry like boxes or ramps. Limit shadow casting to one light source unless you specifically need multiple. Disable pixel ratio scaling on mobile devices because rendering at 3x density on a phone GPU is the fastest path to a broken experience. The code I ended up using in production starts with a single Cannon.World, a shared renderer loop, and an input state object that tracks keydown and keyup separately from the physics update. Keep those concerns separated from the start and you won't spend two weeks untangling them later.
For the actual asset, search GitHub for cannon-es examples. There are working repos with full ball roll games that you can fork and modify. The physics setup is the non-trivial part, not the visual assets.