From Vectors to Gravity: Building a Mini Physics Engine in Rust
While studying physics, i came across a central part of the subject, vector math. Vectors are fundamental in representing quantities that have both magnitude and direction, such as velocity, acceleration, and force. This sparked an idea: what if I could build a simple physics engine from scratch using Rust?
Starting with Vectors
The core building block is the 2D vector. A vector has both magnitude and direction, and can be represented as
Basic operations are simple but powerful:
-
Addition
-
Subtraction
-
Magnitude (length)
-
Dot product
I wrote a simple Vec2 struct in Rust with these methods. Once I had that, I could start simulating motion:
Bouncing Particles
My first simulation was just a set of particles moving inside a box and bouncing off the walls. Each particle has a position, velocity, and radius. On each frame:
- Update position with velocity.
- If it goes past a wall, invert the velocity component and clamp it back inside.
This gave me satisfying little bouncing dots — but they went through each other. Time to fix that.
Elastic Collisions with Impulses
Naively swapping velocities when particles overlap causes chaos — velocities explode because they keep “colliding” every frame. Real physics engines solve this using impulses.
When two particles collide, you compute a collision impulse along the normal between them:
where
- is the relative velocity,
- is the normalized collision normal,
- is the restitution (bounciness).
Then apply equal and opposite impulses:
For equal masses and perfectly elastic collisions (), this simplifies beautifully and gives stable, realistic behavior.
I implemented this in a simple nested loop, resolving each pair of particles once per frame. No runaway speeds, no jittering — just clean bounces.
Moving Beyond ASCII: Macroquad
Originally, I printed particles to the terminal using ASCII grids. It worked, but flickered horribly. The next step was using macroquad — a lightweight graphics library for Rust. It only took a few lines to draw smooth moving circles in a window at 60 FPS.
# Cargo.toml
[dependencies]
macroquad = "0.4"
draw_circle(
self.pos.x as f32,
self.pos.y as f32,
self.radius as f32,
self.color,
);
Suddenly, my physics engine looked like a real sandbox.
Adding Gravity
The final touch (so far) was adding gravity and restitution. Since gravity is just a constant acceleration and restitution is a measure of how much energy is conserved in a collision, all I needed to do was:
const GRAVITY: f64 = 500.0;
const RESTITUTION: f64 = 0.8;
self.vel.y += GRAVITY * dt;
// on every wall collision, and change y based on if it was a vertical or horizontal wall
self.vel.y = -self.vel.y * RESTITUTION;
Particles now fall toward the bottom of the screen, collide, bounce, and settle. A simple restitution coefficient makes them lose a bit of energy on each bounce, so it feels natural.
What I love about this approach is that every feature builds directly on vector math and Newton’s laws — no magic, just physics. At some point i will have to try to combine this with my N-body-simulator project to have particles attract each other with gravity too.
Full Source Code
You can find the full code on my GitHub: 👉 vectors