# Projectile Data Lab

> Repeat a launch with a little speed error and the landings form a distribution. Mean, spread and why R ∝ v² shifts the average: the maths of weapon spread.

- Category: Physics › Mechanics
- Level: common
- Tags: statistics, ballistics, uncertainty, spread, sampling
- Updated: 2026-11-01
- URL: https://gdcs.coody.app/physics/mechanics/projectile-data-lab

## What it is

Real launches are never identical. If the launch speed varies by ±`p`% around a mean, every shot follows the [projectile](https://gdcs.coody.app/physics/mechanics/projectile-motion) range formula `R = v²·sin 2θ / g` with a slightly different `v`, and the landing points spread out along the ground. Collecting many trials and summarising them with a **mean** and **standard deviation** turns that scatter into two numbers you can design against.

Because range grows with the **square** of speed, the error propagates nonlinearly: a +10% speed error adds about 21% range while −10% removes only 19%. The landings are skewed long, and the mean range is slightly greater than the range of the mean speed. For small errors the relative range spread is about twice the relative speed spread (`σ_R/R ≈ 2·σ_v/v`).

This is exactly how weapon spread, AI aim error and throw variance behave in games. Choose the input noise (uniform, Gaussian, stratified) on purpose, check the resulting landing distribution with a quick histogram, and use a seeded or stratified sampler so tests and replays are deterministic. See also [seeded noise](https://gdcs.coody.app/mathematics/procedural/seeded-noise).

## Key formulas

- **Range per trial:** $R_i = \dfrac{v_i^2 \sin 2\theta}{g}$
- **Sample mean:** $\bar R = \dfrac{1}{N}\sum_{i=1}^{N} R_i$
- **Standard deviation:** $\sigma = \sqrt{\dfrac{1}{N}\sum_{i=1}^{N}(R_i - \bar R)^2}$
- **Error propagation (small errors):** $\dfrac{\sigma_R}{R} \approx 2\,\dfrac{\sigma_v}{v}$
- **Mean shift from convexity:** $\mathbb{E}[R] = \dfrac{\sin 2\theta}{g}\left(\bar v^2 + \sigma_v^2\right)$

## Rules of thumb

- A uniform ±p% speed error has σ_v = p/√3 % of the mean, so ±10% gives σ_R ≈ 11.5% of range.
- Need ~30 trials before a sample mean is trustworthy to a few percent; the standard error shrinks as `σ/√N`.
- Stratified samples (one per quantile) give the same distribution every run and converge faster than pure random sampling.
- At 45° range is insensitive to angle error (first-order zero), but fully sensitive to speed error.

## Implementation

Deterministic landing spread and summary statistics:

```ts
export function landingSpread(angleDeg: number, speed: number, spreadPct: number, runs: number, g = 9.81) {
  const s2 = Math.sin((2 * angleDeg * Math.PI) / 180)
  const ranges = Array.from({ length: runs }, (_, i) => {
    const q = (i + 0.5) / runs // stratified quantile in (0, 1)
    const v = speed * (1 + ((2 * q - 1) * spreadPct) / 100)
    return (v * v * s2) / g
  })
  const mean = ranges.reduce((a, r) => a + r, 0) / runs
  const std = Math.sqrt(ranges.reduce((a, r) => a + (r - mean) ** 2, 0) / runs)
  return { ranges, mean, std }
}

// AI grenade: aim at target, then accept if (std / mean) keeps 90% of throws inside the blast radius.
```

## Common mistakes

- Assuming symmetric input noise gives a symmetric output: R ∝ v² skews the landings long.
- Calling `Math.random()` inside gameplay code that must replay identically; use a seeded PRNG or stratified samples.
- Reporting only the mean. Players feel the spread (σ, or the worst 5%), not the average.
- Binning a histogram over [0, max] so all the data lands in two bars; bin over the samples' own min–max range.

## Interactive visual

The cyan arc is the mean-speed launch and the grey arcs are the slowest and fastest; orange dots mark every landing and the purple bars are their histogram. The yellow line is the mean landing distance and the green dashed line the ideal range at mean speed: the yellow sits slightly beyond the green. Raise the speed error to widen the cluster.

## Prompt

```text
Write a TypeScript helper for a Three.js artillery game that estimates landing spread. Inputs: launch angle (degrees), mean speed, speed error ±p%, trial count N, gravity g, and an optional seeded PRNG (otherwise use stratified quantiles (i+0.5)/N). Return ranges[], mean, standard deviation, min, max and a histogram(bins) over [min, max] with the last bin closed. Draw the trajectories with THREE.Line and the histogram as instanced boxes. Unit tests: zero error gives σ = 0; results are identical across runs; the mean range is greater than v̄²·sin2θ/g for any p > 0.
```

## Related

- [Projectile Motion](https://gdcs.coody.app/physics/mechanics/projectile-motion.md) — Launch angle, speed and gravity fully determine a ballistic arc: x moves at constant speed while y accelerates downward.
- [Seeded Noise](https://gdcs.coody.app/mathematics/procedural/seeded-noise.md) — A seeded PRNG plus gradient noise and fBm turns one integer into an endless, smooth, repeatable world: same seed, same terrain on every machine.
- [Particle System](https://gdcs.coody.app/systems/patterns/particle-system.md) — An emitter spawns particles at a rate, each lives for a set time under simple forces, and all of them render in a single instanced draw call.
- [Fixed Timestep](https://gdcs.coody.app/systems/simulation/fixed-timestep.md) — Run physics in constant dt slices from an accumulator, clamp long frames, and interpolate between the last two states for smooth, repeatable motion.

## Further reading

- [OpenStax Introductory Statistics — Measures of the Spread of the Data](https://openstax.org/books/introductory-statistics/pages/2-7-measures-of-the-spread-of-the-data)
- [OpenStax University Physics — Projectile Motion](https://openstax.org/books/university-physics-volume-1/pages/4-3-projectile-motion)

---
Source: Game Development Cheat Sheet (https://gdcs.coody.app)
