A Two-Stage Rocket Launch in About 100 Lines of JavaScript
No physics engine, no orbital-mechanics library — just the Tsiolkovsky rocket equation, a gravity-turn pitch program, and a stage separation event, all in a plain requestAnimationFrame loop. By the end you'll have a real (if simplified) staged launch that pitches over, drops its first stage, and coasts toward orbital velocity.
1. Rocket State and Constants
We model the rocket as a point mass with position (downrange distance, altitude), velocity, and pitch angle. Each stage carries its own dry mass, propellant mass, thrust, and specific impulse (Isp) — the two-stage design mirrors real vehicles like Falcon 9, where a lighter, more efficient second stage takes over once the heavy first stage is spent.
const G0 = 9.81; // m/s^2, standard gravity for Isp conversion
const stage1 = { dryMass: 25000, propMass: 395000, thrust: 7600000, isp: 282 };
const stage2 = { dryMass: 4000, propMass: 92000, thrust: 934000, isp: 348 };
const payload = 8000; // kg
let rocket = {
x: 0, y: 0, // downrange (m), altitude (m)
vx: 0, vy: 0, // velocity components (m/s)
pitch: Math.PI / 2, // radians, pi/2 = straight up
stage: 1,
mass: stage1.dryMass + stage1.propMass + stage2.dryMass + stage2.propMass + payload,
propRemaining: stage1.propMass,
t: 0,
};
2. Thrust and Mass Flow
Thrust and propellant consumption are linked by the exhaust velocity: F = ṁ · v_e, where v_e = Isp · g₀. As propellant burns, total mass drops every timestep — this shrinking mass is exactly what makes the Tsiolkovsky equation nonlinear and rocket staging worthwhile.
function exhaustVelocity(isp) {
return isp * G0;
}
function massFlowRate(thrust, isp) {
return thrust / exhaustVelocity(isp); // kg/s
}
function currentStageConfig(rocket) {
return rocket.stage === 1 ? stage1 : stage2;
}
3. Gravity Turn Pitch Program
A real rocket doesn't fly straight up the whole way — that would waste enormous amounts of propellant fighting gravity instead of building horizontal (orbital) velocity. A gravity turn starts nearly vertical, then pitches gradually toward horizontal as altitude and speed increase, letting gravity itself do most of the turning.
// Simple empirical pitch program: vertical below 1 km, then a smooth pitchover
function pitchProgram(altitude, t) {
if (altitude < 1000) return Math.PI / 2; // straight up off the pad
const turnProgress = Math.min(1, (t - 10) / 120); // pitch over across ~2 minutes
const targetPitch = (Math.PI / 2) * (1 - turnProgress) + 0.12 * turnProgress;
return Math.max(0.05, targetPitch);
}
4. Equations of Motion
Each timestep sums three forces — thrust along the current pitch direction, gravity straight down, and a simplified drag term — then integrates acceleration into velocity and position.
function step(dt) {
const cfg = currentStageConfig(rocket);
const mdot = massFlowRate(cfg.thrust, cfg.isp);
const thrusting = rocket.propRemaining > 0;
const thrust = thrusting ? cfg.thrust : 0;
rocket.pitch = pitchProgram(rocket.y, rocket.t);
const Fx = thrust * Math.cos(rocket.pitch);
const Fy = thrust * Math.sin(rocket.pitch);
const rho = 1.225 * Math.exp(-rocket.y / 8500); // simple exponential atmosphere
const speed = Math.hypot(rocket.vx, rocket.vy);
const dragMag = 0.5 * rho * speed * speed * 0.3 * 10; // Cd*A ≈ 3 m^2
const dragX = speed > 0 ? -dragMag * rocket.vx / speed : 0;
const dragY = speed > 0 ? -dragMag * rocket.vy / speed : 0;
const ax = (Fx + dragX) / rocket.mass;
const ay = (Fy + dragY) / rocket.mass - G0;
rocket.vx += ax * dt;
rocket.vy += ay * dt;
rocket.x += rocket.vx * dt;
rocket.y = Math.max(0, rocket.y + rocket.vy * dt);
if (thrusting) {
const burned = mdot * dt;
rocket.propRemaining -= burned;
rocket.mass -= burned;
}
rocket.t += dt;
checkStaging();
}
5. Stage Separation
When the first stage's propellant runs out, we drop its dry mass entirely — it's dead weight from here on — and switch bookkeeping to the second stage's thrust, Isp, and propellant supply. This discontinuous mass drop is exactly why staging beats a single "average" engine: the vehicle throws away its now-useless empty tanks and structure instead of dragging them to orbit.
function checkStaging() {
if (rocket.stage === 1 && rocket.propRemaining <= 0) {
rocket.mass -= stage1.dryMass; // jettison empty first stage
rocket.stage = 2;
rocket.propRemaining = stage2.propMass;
console.log(`Stage separation at t=${rocket.t.toFixed(1)}s, alt=${(rocket.y/1000).toFixed(1)}km`);
}
}
6. Trajectory Plotting
A simple canvas plot of downrange distance versus altitude reveals the classic rocket-launch arc: nearly vertical climb, a curving gravity turn, then a long, shallow trajectory as horizontal velocity builds toward orbital speed (~7.8 km/s for a circular LEO orbit).
function renderTrajectory(ctx, trace) {
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
ctx.beginPath();
const scaleX = ctx.canvas.width / 500000; // 500 km downrange fits canvas width
const scaleY = ctx.canvas.height / 150000; // 150 km altitude fits canvas height
trace.forEach((p, i) => {
const px = p.x * scaleX;
const py = ctx.canvas.height - p.y * scaleY;
i === 0 ? ctx.moveTo(px, py) : ctx.lineTo(px, py);
});
ctx.strokeStyle = '#3b82f6';
ctx.lineWidth = 2;
ctx.stroke();
}
7. Wiring It Together — the Main Loop
The full simulation runs the physics step several times per rendered frame for numerical stability, records a trajectory trace, and redraws the plot — a complete staged rocket launch in under 100 lines including setup, staging logic, and rendering.
const canvas = document.getElementById('rocket');
const ctx = canvas.getContext('2d');
const trace = [];
function loop() {
for (let i = 0; i < 20; i++) step(0.05); // 20 sub-steps per frame, dt=0.05s
trace.push({ x: rocket.x, y: rocket.y });
renderTrajectory(ctx, trace);
if (rocket.y >= 0 && rocket.t < 600) requestAnimationFrame(loop);
}
loop();
Frequently Asked Questions
What will I learn in this tutorial?
Simulate a staged rocket launch from scratch in about 100 lines of JavaScript: Tsiolkovsky mass ratios, gravity turn, stage separation, and a canvas trajectory plot.
What topics are covered in this tutorial?
This tutorial covers: Rocket State and Constants, Thrust and Mass Flow, Gravity Turn Pitch Program, Equations of Motion, Stage Separation, Trajectory Plotting, Wiring It Together — the Main Loop.
How long does this tutorial take?
This tutorial takes approximately 25 minutes to complete.
What prerequisites do I need before starting?
This is a Intermediate-level tutorial — no special preparation beyond basic JavaScript is assumed.