Elastic Collisions in 167 Lines
Motion looks convincing until two objects hit each other wrong. Then everything falls apart fast. That was the problem I kept running into. Balls sped up after collisions, overlapped, or stuck together. A toy simulation became an exercise in fixing bad assumptions.
This started as a learning project, not a physics engine. I wanted to understand how much believable behavior I could get out of a small Python program. It ended up being 167 lines, one file, and a much better understanding of why simple simulations fail.
Getting the Collisions Right
The part I cared about most was not drawing circles on a screen. It was collision response. I used normal vectors and relative velocity to resolve elastic collisions, then added position correction so overlapping balls separate instead of clumping.
One small detail mattered more than I expected. I capped velocity after collisions. Without it, repeated impacts could inject energy into the system and balls would accelerate in ways that made no physical sense. That bug taught me more than the working version did.
The circular boundary was another interesting problem. I did not want crude axis flips. Reflection used the boundary normal, which made rebounds feel consistent no matter where a ball hit the wall.
Small Decisions That Changed the Simulation
A lot of the project came down to tiny decisions that prevented instability.
- Initial ball placement used polar coordinates to reduce overlap at spawn
- Pairwise collision checks stayed O(n squared) because the scale was small and the code stayed simple
- I treated mass as proportional to radius squared as a lightweight approximation
None of these ideas are huge individually. Together they made the simulation hold together.
What Broke
The cleanest looking code was not always the most stable. Multiple simultaneous collisions exposed that quickly. Handling one collision correctly is easy. Handling chains of impacts without weird behavior is harder.
The biggest limitation is scalability. Collision checks are brute force. Once ball counts climb, performance drops. For a larger simulation I would use spatial partitioning instead of checking every pair.
I would also revisit the physics model. There is no spin, friction, or angular response. It is intentionally simplified, and you can feel those simplifications.
What This Project Represented
This was one of the first projects where math stopped feeling abstract. Dot products were not homework anymore. They decided whether a bounce looked wrong.
It also changed how I think about debugging. Most bugs were not syntax errors. They were systems errors. Energy drift. Overlap correction. Numerical weirdness. You observe behavior, form a hypothesis, and tune the model.
It was a small project. It taught me big habits.
Good simulations do not happen by accident. They survive edge cases.