# Spin drift

How far a spinning bullet drifts sideways in calm air, either from a published formula in time of flight and stability, or as a fixed fraction of the come-up.

## Generated table

**Setting:** Spin drift

| Choice | Used by |
|---|---|
| Litz formula (Standard) | Standard, Physics, Spin |
| Classic (fraction of come-up) | Classic |

## What it does

A spinning bullet drifts sideways even in calm air: toward the right for a right-hand twist, toward the left for a left-hand twist. The windage the solver reports includes a correction for it, so you dial against the drift. This setting chooses how the drift is estimated.

- **Litz formula (Standard):** the published empirical formula of Bryan Litz. The drift grows with the time of flight, and a more stable bullet (higher gyroscopic stability factor) drifts more. The stability factor comes from Miller's rule, so the bullet's weight, diameter, length, the twist rate, the muzzle velocity and the air all matter. On a slope the drift is scaled down by the cosine of the slope angle. The Standard and Physics models use it.
- **Classic (fraction of come-up):** the Classic model's way. The drift is a fixed fraction of your come-up, with the fraction set only by the twist rate (about 3.2 % at 1:8, 2.5 % at 1:12). It does not look at the time of flight, the air, the velocity or any bullet dimension. It is a convention, not a physics model.

Under the Spin model (the `dynamics` option set to `'mpm'`) spin drift comes out of the flight integration itself and this setting is not read. See [dynamics](dynamics.md).

## When to choose it

Use the model's own choice unless you are comparing models. Litz is the choice for any bullet whose length is known and whose stability matters; it is the one that responds to the things that physically change drift (a faster twist, a heavier or longer bullet, thin air). Classic exists to reproduce the classic reference's calculator on its own terms, and it is the one to pick when you want your windage to agree with that calculator. Neither is a measurement of your bullet: if you can see the drift at the range (a calm day, a long shot), trust the target over either formula.

## How to picture it

Look at the bullet from behind as it flies. Its nose rides a little above the flight path (gravity pulls the path downward faster than the nose follows), and the spin, acting like a gyroscope, turns that nose slightly toward the side the bullet's top is rolling toward. The bullet then meets the air a little off its axis and is pushed gently sideways, always the same way for a given twist direction. The push is tiny at any moment and builds with the time in flight, so the drift grows faster than the range: a plot of drift against range curves upward.

**Litz.** Draw the curve as a power of the time of flight, a little steeper than the square of it, scaled by a factor that is larger for a more stable bullet. On a slope, gravity is less perpendicular to the path, the nose rides less above it, and the curve is lowered by the cosine of the slope.

**Classic.** Draw the come-up curve (the elevation you dial against range) and draw the drift as a thin copy of it, a few percent as tall. The drift therefore has the shape of the drop, rising steeply beyond the range where the bullet slows down, and a flatter twist (a larger inches-per-turn number) makes the copy thinner.

**The zero.** Windage is reported relative to the zero. At the zero range both formulas give a small amount of drift, and that amount is taken off every row, so the reported windage is the extra drift beyond what the zeroing already absorbed.

## The math

| Symbol | Meaning | Unit |
|---|---|---|
| $S_g$ | gyroscopic stability factor at the muzzle, corrected for the air | none |
| $S_{g0}$ | Miller's stability factor at 59 °F and 29.92 inHg, before the air correction | none |
| $m$ | bullet weight | gr |
| $d$ | bullet diameter | in |
| $L$ | bullet length | in |
| $n$ | twist rate, the barrel's inches per turn | in |
| $t$, $l$ | twist and length in calibers, $n/d$ and $L/d$ | none |
| $v_0$ | muzzle velocity | fps |
| $T_F$ | air temperature at the firing line | °F |
| $P$ | station pressure at the firing line | inHg |
| $\tau$ | time of flight to the row's range | s |
| $R$ | range of the row | in |
| $\theta$ | inclination of the line of sight | rad |
| $E$ | the row's come-up before the aerodynamic jump, the drop-scale factor and the elevation factor | mil |
| $k$ | the Classic fraction | none |
| $D$ | the drift magnitude, before the twist sign | mil |
| $s$ | $+1$ for a right-hand twist, $-1$ for a left-hand twist | none |
| $w$ | the windage the solver reports | mil |
| $w_0$ | the same quantity at the zero range, taken off every row | mil |
| $c_w$ | the profile's windage scale factor | none |

**The stability factor (Litz).** Miller's rule with a velocity term, then the air correction:

$$
S_{g0} = \frac{30\,m}{t^{2}\,d^{3}\,l\,(1 + l^{2})}\left(\frac{v_0}{2800}\right)^{1/3}
$$

$$
S_g = S_{g0}\,\frac{T_F + 460}{519}\,\frac{29.92}{P}
$$

It is computed once, from the muzzle velocity and the air at the firing line, and held fixed over the whole flight. (The Spin model instead lets it evolve along the path.) Humidity is not used.

**Litz.** The drift in inches, then in mil, with the slope factor:

$$
x = 1.25\,(S_g + 1.2)\,\tau^{1.83} \quad\text{inches}
$$

$$
D = \frac{0.0254\,x}{R}\,1000\,\cos\theta
$$

**Classic.** A fraction of the come-up. The fraction is linear in the twist rate:

$$
k = 0.0480 - 0.00194\,n
$$

$$
D = k\,E
$$

For $n = 8$ in this gives $k = 0.03248$ and for $n = 12$ in $k = 0.02472$. There is no floor or clamp: $k$ falls to zero at a twist of $0.0480/0.00194$ inches and would change sign beyond it, far outside any rifle's twist rate.

The come-up $E$ is the row's elevation in mil as the `angle` option computes it ([angle](angle.md): drop ÷ range or the arctangent), or the dialed angle under `flight` `'dialed'` ([flight](flight.md)), before the aerodynamic jump, the drop-scale factor and the elevation factor are applied.

**The reported windage.** The sign convention is that a positive windage is a correction to the right. A right-hand twist drifts the bullet to the right, so the correction is to the left, and:

$$
w = \frac{(\text{wind and Earth-rotation terms}) - s\,D + s\,D_0 - (\text{zero offset})}{c_w}
$$

where $D_0$ is $D$ evaluated at the zero range, by the same formula, for the same shot (the same slope, the same air). The Earth-rotation term of the `coriolis` option and the wind drift are added to the same sum; they are not part of this option. $D_0$ is taken off so that the windage is zero at the zero range. For Litz it is the Litz drift at the zero range's time of flight, with the slope factor of the shot's slope (not the zero slope). For Classic it is $k$ times the come-up of the same flight at the zero range: on a level shot the zeroed bullet is on the line of sight there and the term is zero; on a slope it is not.

**Where the option is not read.** When `dynamics` is `'mpm'`, $D$ is the lateral deflection of the integration. When `dynamics` is `'mpm-empirical'`, $D$ comes from the coefficient set's own spin-drift formula, with no slope factor. In both cases neither value above is evaluated. If the Spin model has no coefficients to run, the solve falls back to point mass and this option is read as usual. That happens when the coefficient estimate returns nothing (a bullet shorter than two calibers) or when `'mpm-empirical'` is asked for and the set has no empirical block. A bullet length of zero or less is refused before any of this.

## The algorithm

The drift is computed row by row, after the flight is flown, in the same step that turns the sideways position into windage.

```
function drift(sample, R, dial):               // mil, + = correction to the right
    w = -(sample.z / R) * 1000                  // the flight's own sideways position (wind)
    if empirical set (dynamics 'mpm-empirical'):
        w = w - empiricalSpinDrift(set, sample.tof, twist) * s     // the option is not read
    else if dynamics is point mass:
        if spinDrift == 'classic':
            E = comeUp(sample, R, dial)         // by `angle`, or the dial under flight 'dialed'
            w = w - (0.0480 - 0.00194 * twist) * E * s
        else:                                   // 'litz'
            x = 1.25 * (Sg + 1.2) * sample.tof ^ 1.83            // inches
            w = w - (x * 0.0254 / R) * 1000 * s * cos(inclination)
    // dynamics 'mpm': nothing added here; the drift is already in sample.z
    if coriolis == 'baseline': w = w - horizontalCoriolis(sample.tof, latitude)
    return w
```

The stability factor used by the Litz branch:

```
function Sg(profile, env):
    t = twist / diameter ; l = length / diameter
    sg = 30 * weight / (t^2 * diameter^3 * l * (1 + l^2)) * cbrt(mv / 2800)
    return sg * (temp_F + 460) / 519 * 29.92 / station_pressure
```

The Litz formula and the Classic fraction:

```
function spinDriftIn(sg, tof):  return 1.25 * (sg + 1.2) * tof ^ 1.83

function classicSpinDriftMil(comeUp, twist):  return (0.0480 - 0.00194 * twist) * comeUp
```

The zero. After the drift is defined, the windage at each row has the drift at the zero range subtracted, using the same function on the flight at the zero range:

```
zeroDrift = drift(fly(base, bore, [zero_range])[0], zero_range, no dial)
windage(row) = (drift(row) - zeroDrift - zeroOffset) / ssf_wind
```

The zeroing itself (finding the bore angle) runs with no spin drift and no Earth rotation. A profile without a bullet length is refused before any of this runs, for both values, even though Classic does not use the length.

## On a real shot

**Changing only this setting on the Standard model**

The shot: a 350 gr 0.375 in bullet at 3048 fps muzzle velocity, fired at 3520 yd in air of 60 °F, 40 % humidity, 24.43 inHg at the firing line, latitude 42.1°, wind 10 mph from the right (90° to the line of fire), the same wind in every column; site altitude 5,500 ft. In the app, type it as "Site altitude" (shown when the profile's model reads one); left blank, the classic values count from sea level and can differ from this table. All other settings are the Standard model's. In the first column (North, level) the reference arc reaches 223 ft above the line of sight at 2160 yd.

| Value | Shown in the app as | North, level | North, 10° uphill | North, 10° downhill |
|---|---|---|---|---|
| `'litz'` | Litz formula (Standard) | reference | reference | reference |
| `'classic'` | Classic (fraction of come-up) | 0.000 / -0.373 / 0.0 | 0.000 / -0.377 / 0.0 | 0.000 / -0.358 / 0.0 |

Each cell is the change from the reference row (`'litz'`) in the same shot, as elevation (mil) / windage (mil) / velocity at the target (fps). Elevation: plus means more elevation must be dialed. Windage: plus means the windage correction moves to the right (dial further right); this is a correction, not where the bullet lands. Velocity: plus means the bullet arrives faster. A zero means no difference at the shown precision.

**Each value under the Classic model's other settings (reference row: the Classic value)**

The shot: a 350 gr 0.375 in bullet at 3048 fps muzzle velocity, fired at 3520 yd in air of 60 °F, 40 % humidity, 24.43 inHg at the firing line, latitude 42.1°, wind 10 mph from the right (90° to the line of fire), the same wind in every column; site altitude 5,500 ft. In the app, type it as "Site altitude" (shown when the profile's model reads one); left blank, the classic values count from sea level and can differ from this table. All other settings are the Classic model's. In the first column (North, level) the reference arc reaches 223 ft above the line of sight at 2160 yd.

| Value | Shown in the app as | North, level | North, 10° uphill | North, 10° downhill |
|---|---|---|---|---|
| `'litz'` | Litz formula (Standard) | 0.000 / +0.374 / 0.0 | 0.000 / +0.375 / 0.0 | 0.000 / +0.359 / 0.0 |
| `'classic'` | Classic (fraction of come-up) | reference | reference | reference |

Each cell is the change from the reference row (`'classic'`) in the same shot, as elevation (mil) / windage (mil) / velocity at the target (fps). Elevation: plus means more elevation must be dialed. Windage: plus means the windage correction moves to the right (dial further right); this is a correction, not where the bullet lands. Velocity: plus means the bullet arrives faster. A zero means no difference at the shown precision.

**A shorter shot: 1,000 yd, Standard model**

The shot: a 350 gr 0.375 in bullet at 3048 fps muzzle velocity, fired at 1000 yd in air of 60 °F, 40 % humidity, 24.43 inHg at the firing line, latitude 42.1°, wind 10 mph from the right (90° to the line of fire), the same wind in every column; site altitude 5,500 ft. In the app, type it as "Site altitude" (shown when the profile's model reads one); left blank, the classic values count from sea level and can differ from this table. The shot is changed from the scenario's for this table: 1000 yd. All other settings are the Standard model's. In the first column (North, level) the reference arc reaches 5 ft above the line of sight at 530 yd.

| Value | Shown in the app as | North, level | North, 10° uphill | North, 10° downhill |
|---|---|---|---|---|
| `'litz'` | Litz formula (Standard) | reference | reference | reference |
| `'classic'` | Classic (fraction of come-up) | 0.000 / -0.022 / 0.0 | 0.000 / -0.022 / 0.0 | 0.000 / -0.021 / 0.0 |

Each cell is the change from the reference row (`'litz'`) in the same shot, as elevation (mil) / windage (mil) / velocity at the target (fps). Elevation: plus means more elevation must be dialed. Windage: plus means the windage correction moves to the right (dial further right); this is a correction, not where the bullet lands. Velocity: plus means the bullet arrives faster. A zero means no difference at the shown precision.

Read from the tables (every decimal here is copied from them). Each cell is the change from the reference row for the same shot, so it isolates this option; wind is blowing in every column, but it is the same under both values and cancels. Elevation and velocity read 0.000 and 0.0 everywhere: the option adds a sideways term to the windage and nothing else, as the algorithm shows. On the 3,520 yd shot on the Standard model the Classic value moves the windage correction by -0.373 mil, -0.377 uphill and -0.358 downhill. The shot is a right-hand twist (1:10), so a negative change means Classic subtracts more drift: it predicts more rightward drift than the Litz formula, by about a third of the drift Litz itself predicts there (read from a separate calm-air solve of the same shot, not from the table). The second table reverses the reference, so the same gap reads +0.374, +0.375 and +0.359; it differs from the first by a thousandth because the Classic model's other settings change the flight a little.

At 1,000 yd the gap is -0.022, -0.022 and -0.021. The two formulas nearly agree there, and most of the small gap is the zero term, not the drift: the Litz drift at the 100 yd zero is not zero, so subtracting it lowers the Litz windage, while the Classic term at the zero is zero on a level shot (the zeroed bullet is on the line of sight). A calm-air solve of the same shot puts roughly three quarters of the 1,000 yd gap in that zero term. Beyond that the gap widens quickly: the Litz drift grows as the time of flight raised to a power a little under two, while the Classic drift grows with the come-up, which rises faster than that with range as the bullet slows.

The uphill and downhill columns differ from the level one and from each other, though the cosine factor in the Litz formula is the same for $+10^\circ$ and $-10^\circ$. The difference comes from the flight: the come-up (which Classic follows) and the time of flight (which Litz follows) differ by slope, as the same calm-air solves show.

---

[All reference pages](../index.md)
