Skip to content

Physics · Mechanics · core

Projectile Motion

constant sideways speed, steadily changing vertical speed

Launch angle, speed and gravity fully determine a ballistic arc: x moves at constant speed while y accelerates downward.

View .md
Interactive visual

The orange ball follows the cyan arc and the yellow line is its instantaneous velocity. Change the angle, launch speed and gravity to reshape the arc; the readout shows flight time and range.

What it is

A projectile is any body moving under gravity alone. Split its velocity into components: the horizontal part v0·cos θ never changes (no drag), while the vertical part v0·sin θ loses g metres per second every second.

Because the two axes are independent, the path is a parabola. Flight time, peak height and range all follow from the launch speed, angle and gravity, so you can predict a landing point before the shot happens.

In games this is how grenades, arrows, jump arcs and aim-assist trajectory previews are computed. It is also the reference you check a fixed timestep integrator against.

Key formulas

Horizontal position
Vertical position
Flight time (flat ground)
Range

Maximum at 45° on flat ground, with no drag.

Peak height

Rules of thumb

  • Complementary angles (30° and 60°) land at the same spot; the steeper one stays in the air longer.
  • Earth g = 9.81 m/s². Many platformers use 2–3× that for snappier jumps.
  • To hit a target, solve the range equation for θ. Two solutions mean a low and a high lob.

Implementation

Sample an arc for a trajectory preview (Three.js):
import * as THREE from "three"

export function trajectory(v0: number, angle: number, g = 9.81, samples = 64) {
  const vx = v0 * Math.cos(angle)
  const vy = v0 * Math.sin(angle)
  const T = (2 * vy) / g
  const points: THREE.Vector3[] = []
  for (let i = 0; i <= samples; i++) {
    const t = (i / samples) * T
    points.push(new THREE.Vector3(vx * t, vy * t - 0.5 * g * t * t, 0))
  }
  return new THREE.BufferGeometry().setFromPoints(points) // draw with THREE.Line
}

Common mistakes

  • Mixing degrees and radians: Math.sin(45) is not sin 45°.
  • Integrating with explicit Euler at a variable frame rate, which makes the landing point depend on FPS.
  • Forgetting launch height: the flat-ground formulas assume the projectile lands at the height it started from.
  • Adding drag later and still trusting the analytic range. Once there is drag, simulate numerically.

Prompt

Implement a projectile trajectory preview in TypeScript with Three.js. Given launch speed v0 (m/s), angle θ (radians) and gravity g, compute x(t)=v0·cosθ·t and y(t)=v0·sinθ·t−½gt², sample 64 points until it returns to launch height (T=2v0·sinθ/g), and draw them with THREE.Line. Also return range R=v0²·sin2θ/g and peak height H=(v0·sinθ)²/(2g). Keep angles in radians, make the sampler a pure function, and add a unit test checking that 30° and 60° give the same range.

Further reading

Updated 2026-11-01