← All posts

Elastic Collisions in 167 Lines

2023Physics · Pygame

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.

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.

View all projects →View on GitHub ↗