Your launch monitor measures only the moment of impact — how fast the ball leaves, at what angle, and how it spins. Everything you see plotted on this site (carry, apex, descent, dispersion, roll-out) is computed from those launch numbers by integrating the physics of flight. This page documents exactly how, with the real equations.
The Garmin Approach R50 reports a set of launch quantities — the ball's state in the first instants after impact:
The R50 also reports its own estimate of carry, apex, and total. We do not use those for anything plotted. They appear only on the Raw Data page, for reference. Every curve on this site is rebuilt from the launch quantities above.
A launch monitor measures launch conditions extremely well but infers carry and apex from a short observation window. By taking only the directly measured launch state and integrating the flight ourselves, every downstream number comes from one transparent, reproducible physics model rather than a black box. The trade-off is honesty: if the model and the monitor disagree, you can see exactly why, in the equations below.
Before any forces act, the model converts launch conditions into vectors and folds the atmosphere into two scalars.
Density is computed from temperature, pressure, elevation, and humidity (the site uses a fixed standard atmosphere: 70°F, 29.92 inHg, sea level, 50% RH):
where \(\rho_0=1.2929\) kg/m³, \(T_K\) is temperature in kelvin, \(P\) barometric pressure (mmHg), \(P_0=760\) mmHg, \(\beta=1.217\times10^{-4}\), \(h\) elevation (m), \(\phi\) relative humidity, and SVP the saturation vapor pressure of water.
Rather than carry area and mass through every step, libgolf folds them — with density — into a single coefficient \(c_0\) that turns a coefficient and speed directly into an acceleration in ft/s²:
\(\rho_{\text{imp}}\) is density in lb/ft³, \(m\) the ball mass (1.62 oz), \(C\) its circumference (5.277 in), and \(m_{\text{ref}}=5.125\) oz, \(C_{\text{ref}}=9.125\) in are libgolf's reference normalizers.
Air viscosity follows Sutherland's law; a reference Reynolds number is taken at 100 mph and then scaled by the instantaneous speed during flight:
\(d\) is the ball diameter in metres. During flight the working Reynolds number scales linearly with speed: \(Re = (v_{\text{mph}}/100)\,Re_{100}\).
With launch angle \(\theta\), direction \(\psi\), and ball speed \(v_0\) (converted to ft/s), the initial velocity is
and the spin vector is built from backspin \(\omega_b\) and sidespin \(\omega_s\) (rpm → rad/s):
Once airborne, three accelerations act on the ball: gravity, aerodynamic drag (opposing motion), and the Magnus force (from spin, perpendicular to motion — this is what curves the ball and holds it up).
Drag points opposite the ball's velocity relative to the air, \(\mathbf{v}_{\text{rel}}=\mathbf{v}-\mathbf{w}\) (with wind \(\mathbf{w}\)):
The Magnus acceleration is proportional to the lift coefficient and to the cross product of spin and velocity — a full 3D treatment, which is exactly why sidespin and a tilted spin axis produce slice/hook curvature, not just height:
Spin bleeds off exponentially in flight with a speed-dependent time constant:
\(C_d\) and \(C_l\) are not constants — they depend on the Reynolds number \(Re\) (binned, in units of \(10^5\)) and the spin ratio \(S = \omega r / v\). These fits trace to dimpled-sphere wind-tunnel data (Bearman & Harvey) and a Washington State University study (Lyu et al.), as used by Nathan's trajectory calculator.
A faster ball (higher \(Re\)) has lower drag — the classic "drag crisis" of a dimpled sphere — and more spin adds a little drag.
\(C_l\) interpolates between four spin-ratio polynomials keyed to Reynolds bins (\(Re\) at 50k, 60k, 65k, 70k), capped by a spin-dependent maximum \(C_{l,\max}\):
Below \(Re=0.3\) there is no lift; above \(Re=0.7\) a saturating spin-gain form is used; in between the model blends the bracketing polynomials linearly. The cap rises with spin ratio:
| Reynolds bin (×10⁵) | Lift behaviour |
|---|---|
| Re ≤ 0.3 | no lift (\(C_l = 0\)) |
| 0.3 – 0.5 | smoothstep ramp into \(C_l^{50k}\) |
| 0.5 – 0.7 | linear blend across the 50k / 60k / 65k / 70k polynomials |
| Re ≥ 0.7 | saturating form \(C_{l,\max}\cdot 16S/(1+16S)\) |
With the forces defined, the ball's path is advanced in small time steps using semi-implicit (symplectic) Euler integration — velocity is updated, then position uses the updated terms. The step is dt = 0.01 s.
Each step: decay the spin, recompute \(Re\) and \(S\), look up \(C_d\) and \(C_l\), form the net acceleration, and advance. The loop ends when the ball crosses the ground (\(z\le0\)); the exact landing point is found by linear interpolation across that final step. From the path the model reads off:
For total distance, the model continues past landing through bounce and roll on a fairway-like surface. The bounce uses the Penner (2003) model: a spin-and-velocity-dependent coefficient of restitution governs the vertical rebound, while the tangential response depends on impact angle.
A steep, energetic impact with high backspin produces the spin-back / check you see on wedges, via a tangential spin-back term:
\(\alpha\) is the impact angle, \(\alpha_c=15^\circ\) the surface's critical angle, \(R_{\text{ret}}\) a spin-scaled retention factor. Shallow or low-energy impacts instead release forward with simple friction. Once the ball is moving slowly enough, Coulomb friction rolls it to rest. This is why on the 3D page a wedge can roll backward while a 3 wood releases forward.
Every coefficient above is copied verbatim from the published library. There is exactly one addition: a small low-spin drag correction. The published \(C_d\) fit slightly over-drags in the low-spin-ratio regime (long fairway woods and low-spin drivers, \(S\) below ~0.20), where measured carries ran longer than the unmodified model predicted. We apply a smooth, drag-only reduction that fades in below \(S=0.20\) and saturates at \(S=0.06\):
It is drag-only on purpose — adding lift instead fixed the distance but ballooned the apex into an unphysical shape. This single change improves bag-wide carry accuracy (RMSE ~5.95 → ~4.2 yd) and leaves every club at \(S\ge0.20\) — the entire 4-iron-through-wedge set — untouched. Setting the gain to zero restores the pure library model.
The flight engine is a faithful JavaScript port of the aerial, bounce, and roll physics in libgolf by Gabriel DiFiore, whose in-air aerodynamics are based on the work of Prof. Alan M. Nathan (University of Illinois Urbana-Champaign). The drag and lift coefficient fits trace to dimpled-sphere wind-tunnel data (Bearman & Harvey) and a Washington State University study (Lyu et al.).
Coefficients are copied verbatim from libgolf's DefaultAerodynamicModel, DefaultBounceModel, DefaultRollModel, physics_constants, ShotPhysicsContext, and DefaultIntegrator — with the single documented low-spin drag tweak above. The bounce model follows Penner (2003).