Drop a stone in water. It sinks. Drop a wooden log. It floats. Why?
Every object has mass distributed through its volume. Water also has mass distributed through its volume—about 1000 kg per cubic meter. When you push an object into water, you're shoving water out of the way. That displaced water wants to be somewhere, and gravity is pulling it down. The water pushes back.
This is Archimedes' Principle:
A submerged object experiences an upward force equal to the weight of the fluid it displaces.
Written precisely:
F_buoyancy = ρ_water × g × V_submerged
Where:
- ρ_water is water's density (1000 kg/m³)
- g is gravitational acceleration (9.81 m/s²)
- V_submerged is how much of the object's volume is underwater
The stone sinks because even fully submerged, the water it displaces weighs less than the stone does. The log floats because before it's even halfway submerged, it's already displaced its own weight in water.
Whether something floats comes down to one comparison: is the object less dense than water?
Density = Mass / Volume
- Steel: ~8000 kg/m³ → sinks
- Oak wood: ~750 kg/m³ → floats
- Human body: ~985 kg/m³ → barely floats (which is why you can drown)
But wait—ships are made of steel, and they float. That's because a ship isn't solid steel. It's a steel shell surrounding air. The average density of the whole vessel (steel + air + cargo + everything) is less than water.
A ship doesn't float because of magic. It floats because it's a very large, very hollow object.
An object sinks until the buoyancy force equals its weight. At that point, forces balance, and it stops.
At equilibrium: F_buoyancy = F_gravity
ρ_water × g × V_submerged = m × g
V_submerged = m / ρ_water
A 1000 kg boat will sink until it has displaced exactly 1 cubic meter of water. Whether that's 10% of the hull or 90% depends on the hull's total volume.
Here's where boats get interesting.
Forces don't just move things—they can spin them. A force applied away from an object's center of mass creates torque:
τ = r × F
That's a cross product. The torque vector is perpendicular to both the offset (r) and the force (F). Its magnitude is:
|τ| = |r| × |F| × sin(θ)
For a boat, buoyancy pushes up at the center of buoyancy—the centroid of the submerged volume. Gravity pulls down at the center of mass.
When the boat tips, the submerged shape changes, shifting the center of buoyancy. If this shift creates torque that tips the boat back upright, the boat is stable. If it tips it further over, the boat capsizes.
This is why cargo placement matters on ships. Move the center of mass too high, and you've made an unstable boat.
The real physics is elegant but expensive:
Calculating submerged volume requires knowing your exact hull geometry, slicing it at the water plane, and integrating. Every frame. For every boat. For a wavy surface.
Finding the center of buoyancy requires computing the centroid of that sliced volume. Also every frame.
Realistic wave interaction means the water surface isn't flat—it's a heightfield that varies in space and time. Your hull intersects with a complex, moving surface.
For a naval engineering simulator, you might do all of this. For a game where you want a boat that feels good? You approximate.
Instead of computing true submerged volume, we sample the water at a few strategic points around the hull. Each sample point asks: "How deep am I?" and contributes force based on the answer.
These sample points (I'll call them probes) are your floaters. But here's the critical insight:
Probes don't generate buoyancy. The boat has buoyancy. Probes distribute it.
If your boat can generate 1000 N of buoyancy force (based on its mass and how floaty you want it to be), then 4 probes each control up to 250 N. 8 probes each control up to 125 N. The total is the same.
More probes = finer sampling = smoother response to waves. Not more lift.
Your boat has a maximum buoyancy force it can ever produce:
F_max = m × g × floatiness
Where floatiness is your game design knob:
- 1.0 = neutrally buoyant (barely floats, waterline at surface)
- 1.2 = rides 20% "higher" than neutral
- 0.8 = will eventually sink
Think of floatiness as inverse density without computing actual volume.
Each probe has a weight (not mass—just a relative importance value). The weights determine what fraction of the budget each probe controls:
probe_fraction = probe_weight / sum_of_all_weights
probe_max_force = F_max × probe_fraction
If you have 4 probes with weights [1, 1, 1, 1], each controls 25% of the budget.
If you weight the front probes higher [2, 2, 1, 1], the front controls 67% and the back 33%. This would make the boat nose-up.
A probe fully out of water contributes nothing. A probe deep underwater contributes its full share. Between those extremes, we ramp up.
The simplest model: a spring.
depth = water_height - probe_height
if depth > 0:
spring_force = stiffness × depth
actual_force = min(spring_force, probe_max_force)
else:
actual_force = 0
The stiffness controls how fast you reach maximum force: - High stiffness = force maxes out at shallow depth (wide, flat barge) - Low stiffness = force builds slowly with depth (narrow sailboat hull)
A spring is a surprisingly good model because real buoyancy also increases (roughly) linearly with depth—for typical hull shapes. As a boat sinks, more volume goes underwater, and more water is displaced. A V-shaped hull does this gradually. A rectangular barge does it instantly.
The spring stiffness lets you fake hull shape without geometry.
Gravity pulls on every atom of your boat, but the net effect is equivalent to a single force acting at the center of mass:
F_gravity = m × g × down
Applied at: center of mass
Because the offset from the CoM to itself is zero, gravity creates no torque. Gravity doesn't make things spin—it just pulls them down.
Do not apply gravity per-probe. If your probes aren't perfectly symmetric around the CoM, you'll get phantom rotations.
Each probe that's underwater contributes an upward force:
For each probe:
if underwater:
F_probe = calculate_clamped_spring_force(depth) × up
Apply F_probe at probe's world position
These forces sum to the total buoyancy. Because they're applied at different positions, they create torque around the center of mass.
For each probe, the torque it creates is:
offset = probe_position - center_of_mass_position // vector from CoM to probe
torque = offset × F_probe // cross product
Sum all the torques. This net torque determines angular acceleration.
Each physics frame:
total_force = gravity_force
for each probe:
total_force += probe_buoyancy_force
```
2. **Sum torques:**
```text
total_torque = zero
for each probe:
offset = probe_position - center_of_mass
total_torque += cross(offset, probe_buoyancy_force)
```
3. **Integrate linear motion:**
```text
acceleration = total_force / mass
velocity += acceleration × dt
position += velocity × dt
```
4. **Integrate angular motion:**
```text
angular_acceleration = total_torque / moment_of_inertia
angular_velocity += angular_acceleration × dt
rotation = apply_angular_velocity(rotation, angular_velocity, dt)
```
---
## Part 5: Damping and Stability
### Why Damping Matters
Without damping, your boat will oscillate forever. Push it down, it springs up, overshoots, falls back, springs up again. Real water doesn't do this because of:
1. **Drag**: Water resists motion through it
2. **Wave radiation**: Energy leaves as waves spreading outward
3. **Viscosity**: Internal friction in the water
We approximate all of this with damping.
### Linear Damping
Reduce velocity each frame:
```text
velocity *= (1 - drag × dt)
Or apply a drag force opposing motion:
F_drag = -drag_coefficient × velocity
Use higher damping when in water, lower when airborne.
Same principle for rotation:
angular_velocity *= (1 - angular_drag × dt)
Boats should probably have strong angular damping. They don't spin freely—water resists rotation.
For extra stability, damp each probe's local velocity. A probe moving up through water feels resistance:
probe_velocity = velocity + cross(angular_velocity, offset_from_CoM)
damping_force = -damping_coefficient × probe_velocity
This naturally stabilizes both linear and angular motion, and it means faster-moving probes get more damping—which feels right.
A boat pushed deep underwater will rocket upward because spring force is proportional to depth.
Fixes:
1. Cap the force (you're already doing this with probe_max_force)
2. Heavy damping when submerged—real water resists rapid motion
3. Nonlinear spring: force increases slower at high depths
Rotation requires knowing the moment of inertia—how hard the object is to spin. For a proper simulation, this is a 3×3 tensor computed from mass distribution.
For a game, approximate it:
I ≈ mass × (average_distance_of_probes_from_CoM)²
Or just tune it by hand. Higher = slower to rotate, more "massive" feeling.
Real waves move. A probe isn't just comparing against a flat surface—it's sampling a wave that might be at a peak or trough and traveling sideways.
If your water system gives you wave height at any (x, z) coordinate, you're set. Each probe samples the height at its own position. The boat will naturally rock with passing waves.
Real buoyancy can have horizontal components when surfaces are angled. We ignore this. It's subtle and the spring model doesn't handle it anyway.
Once buoyancy works, add gameplay:
| Parameter | Effect | Start Here |
|---|---|---|
| floatiness | How high it rides | 1.1 – 1.3 |
| spring stiffness | How snappy/mushy | 2 – 10 |
| linear damping (water) | Settling speed | 0.9 – 0.98 per second retained |
| angular damping | Rotation settling | 0.8 – 0.95 |
| moment of inertia | Rotation feel | mass × 1–5 |
| probe count | Wave conformance | 4 minimum, 8+ for long boats |
| probe weights | Trim (nose up/down) | Start equal |
The boat has a buoyancy budget = mass × gravity × floatiness
Probes sample depth and distribute that budget based on their weights
Springs ramp force from zero (out of water) to max (fully submerged)
Gravity acts once at the center of mass (no torque)
Buoyancy acts per-probe at each probe's position (creates torque)
Torque = offset × force summed across all probes
7. **Integrate** force → acceleration → velocity → position (linear and angular)
If you want to start minimal and add complexity:
Version 0: Single probe at center. No rotation. Just springs the whole boat up and down.
Version 1: Four probes in a cross. Now it can tilt.
Version 2: Add gravity, damping, and proper force budget.
Version 3: Per-probe velocity damping. Feels stable.
Version 4: Tune stiffness, damping, and weights until it feels like a boat.
The water doesn't know your boat exists. It just has a surface. Your boat is the one doing all the work—sampling that surface, calculating forces, pretending to float. Do it well, and no one will know the difference.