Earlier versions of my physics engine used a penalty-based method for collisions. While simple to implement (pushing objects apart based on overlap depth), it resulted in "spongy" interactions where objects would sink into each other before bouncing back.
To fix this, I refactored the solver to use Impulse Resolution. Instead of correcting position directly over time, we calculate an instantaneous change in velocity.
The Math
The magnitude of the impulse scalar $j$ is calculated using the coefficient of restitution $e$ (bounciness) and the relative velocity along the normal.
Implementation Details
Here is the simplified C# implementation used in the solver loop. Note that we calculate inverse mass upfront to avoid division during the physics step.