Туторіал · Аерокосмос · Фізика · JavaScript
📅 Липень 2026 ⏱ ≈ 25 хв 🎯 Середній рівень

Запуск двоступеневої ракети приблизно за 100 рядків JavaScript

Ніякого фізичного рушія, ніякої бібліотеки орбітальної механіки — лише рівняння ракети Ціолковського, програма тангажу гравітаційного розвороту та подія відділення ступеня, все у звичайному циклі requestAnimationFrame. Наприкінці у вас буде справжній (хоч і спрощений) ступеневий запуск, що виконує розворот, скидає перший ступінь і рухається за інерцією до орбітальної швидкості.

1. Стан ракети та константи

Ми моделюємо ракету як матеріальну точку з позицією (дальність, висота), швидкістю та кутом тангажу. Кожен ступінь несе власну суху масу, масу палива, тягу та питомий імпульс (Isp) — двоступенева конструкція відображає реальні апарати на кшталт Falcon 9, де легший, ефективніший другий ступінь бере на себе роботу, щойно важкий перший ступінь вичерпав паливо.

const G0 = 9.81;  // м/с^2, стандартна гравітація для перерахунку Isp

const stage1 = { dryMass: 25000, propMass: 395000, thrust: 7600000, isp: 282 };
const stage2 = { dryMass: 4000,  propMass: 92000,  thrust: 934000,  isp: 348 };
const payload = 8000; // кг

let rocket = {
  x: 0, y: 0,           // дальність (м), висота (м)
  vx: 0, vy: 0,         // компоненти швидкості (м/с)
  pitch: Math.PI / 2,     // радіани, pi/2 = прямо вгору
  stage: 1,
  mass: stage1.dryMass + stage1.propMass + stage2.dryMass + stage2.propMass + payload,
  propRemaining: stage1.propMass,
  t: 0,
};
Реальні числа: константи вище приблизно відповідають апарату класу Falcon 9 — Isp першого ступеня близько 282 с (двигун Merlin на рівні моря), Isp другого ступеня близько 348 с (оптимізований для вакууму двигун, вища ефективність без опору атмосфери).

2. Тяга і витрата маси

Тяга і витрата палива пов'язані через швидкість витоку: F = ṁ · v_e, де v_e = Isp · g₀. По мірі спалювання палива загальна маса зменшується щокроку — саме це зменшення маси й робить рівняння Ціолковського нелінійним, а поділ на ступені — виправданим.

Δv = v_e · ln(m_початкова / m_кінцева) = Isp · g₀ · ln(m₀/m₁)
function exhaustVelocity(isp) {
  return isp * G0;
}

function massFlowRate(thrust, isp) {
  return thrust / exhaustVelocity(isp); // кг/с
}

function currentStageConfig(rocket) {
  return rocket.stage === 1 ? stage1 : stage2;
}

3. Програма тангажу гравітаційного розвороту

Справжня ракета не летить прямо вгору весь час — це витратило б величезну кількість палива на боротьбу з гравітацією замість набору горизонтальної (орбітальної) швидкості. Гравітаційний розворот починається майже вертикально, а потім поступово нахиляється до горизонталі зі зростанням висоти й швидкості, дозволяючи самій гравітації виконати більшу частину повороту.

// Проста емпірична програма тангажу: вертикально нижче 1 км, потім плавний розворот
function pitchProgram(altitude, t) {
  if (altitude < 1000) return Math.PI / 2; // прямо вгору зі стартового майданчика
  const turnProgress = Math.min(1, (t - 10) / 120); // розворот протягом ~2 хвилин
  const targetPitch = (Math.PI / 2) * (1 - turnProgress) + 0.12 * turnProgress;
  return Math.max(0.05, targetPitch);
}
Примітка про спрощення: справжні бортові комп'ютери наведення розв'язують задачу оптимального керування із зворотним зв'язком (мінімізуючи витрату палива для цільової орбіти); цей розімкнений графік — спрощення, яке все ж дає впізнаваний, фізично обґрунтований профіль підйому.

4. Рівняння руху

На кожному кроці підсумовуються три сили — тяга вздовж поточного напрямку тангажу, гравітація прямо вниз і спрощений доданок опору — а потім прискорення інтегрується у швидкість і позицію.

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); // проста експоненційна атмосфера
  const speed = Math.hypot(rocket.vx, rocket.vy);
  const dragMag = 0.5 * rho * speed * speed * 0.3 * 10; // Cd*A ≈ 3 м^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. Відділення ступеня

Коли паливо першого ступеня закінчується, ми повністю скидаємо його суху масу — далі це мертвий вантаж — і перемикаємо облік на тягу, Isp та запас палива другого ступеня. Саме це стрибкоподібне зменшення маси й пояснює, чому поділ на ступені кращий за один «усереднений» двигун: апарат позбувається тепер уже марних порожніх баків і конструкції, замість того щоб тягнути їх аж до орбіти.

function checkStaging() {
  if (rocket.stage === 1 && rocket.propRemaining <= 0) {
    rocket.mass -= stage1.dryMass; // скидання порожнього першого ступеня
    rocket.stage = 2;
    rocket.propRemaining = stage2.propMass;
    console.log(`Відділення ступеня на t=${rocket.t.toFixed(1)}с, висота=${(rocket.y/1000).toFixed(1)}км`);
  }
}
Чому поділ на ступені працює: згідно з рівнянням ракети, Δv залежить від ln(m₀/m₁). Одноступеневий апарат мусить нести всю свою порожню конструкцію аж до орбітальної швидкості; двоступеневий апарат скидає мертву вагу першого ступеня на півшляху, тож та сама загальна маса палива дає значно вищу сумарну Δv.

6. Графік траєкторії

Простий графік на canvas дальності відносно висоти показує класичну дугу запуску ракети: майже вертикальний підйом, викривлений гравітаційний розворот, а потім довга полога траєкторія по мірі зростання горизонтальної швидкості до орбітальної (~7,8 км/с для кругової орбіти НЗО).

function renderTrajectory(ctx, trace) {
  ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
  ctx.beginPath();
  const scaleX = ctx.canvas.width / 500000;   // 500 км дальності вміщуються у ширину canvas
  const scaleY = ctx.canvas.height / 150000;  // 150 км висоти вміщуються у висоту canvas
  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. Збираємо все разом — головний цикл

Повна симуляція виконує крок фізики кілька разів за кожен відрендерений кадр для числової стабільності, записує слід траєкторії й перемальовує графік — повний запуск двоступеневої ракети менш ніж за 100 рядків, включно з налаштуванням, логікою відділення ступенів та рендерингом.

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 підкроків на кадр, dt=0,05с
  trace.push({ x: rocket.x, y: rocket.y });
  renderTrajectory(ctx, trace);
  if (rocket.y >= 0 && rocket.t < 600) requestAnimationFrame(loop);
}
loop();
Читання результату: приблизно на t≈150с траєкторія показує помітний злам, де відбувається відділення ступеня — набір висоти майже не переривається, оскільки легший, ефективніший другий ступінь продовжує прискорення практично одразу. Про математику рівняння ракети, яка пояснює, чому поділ на ступені примножує досяжну Δv, читайте у статті Рівняння Ціолковського та межі ракетної техніки. Про те, як та сама дельта-v перетворюється на реальну орбіту після завершення тяги, читайте у статті Гоманнівський перехід.
▶ Демо наживо

Часті запитання

Чого я навчуся в цьому уроці?

Симулюйте запуск ступеневої ракети з нуля приблизно за 100 рядків JavaScript: масові співвідношення Ціолковського, гравітаційний розворот, відділення ступеня та графік траєкторії на canvas.

Які теми розглядаються в цьому уроці?

Цей урок охоплює такі теми: Стан ракети та константи, Тяга і витрата маси, Програма тангажу гравітаційного розвороту, Рівняння руху, Відділення ступеня, Графік траєкторії, Збираємо все разом — головний цикл.

Скільки часу займає цей урок?

Цей урок займає приблизно 25 хв.

Які попередні знання потрібні?

Це урок рівня «Середній рівень» — окрема попередня підготовка, крім базового JavaScript, не потрібна.