aeronautic

Flight and altitude

What altitude, airspeed and the day do to a plume, and how to fly one.

The profile puts the engines in the air. Three of its fields describe flight:

fielddefaultwhat it is
altitudeM0Metres above sea level.
airspeedMPerS0How fast the engines move through the air.
temperatureOffsetK0How much warmer the day is than the standard day.

From these, the International Standard Atmosphere gives the air's pressure, temperature and density, to 86 km. atmosphere(profile) returns them on the main thread if you want them yourself.

altitude-sweep.tsx
Starting…

Why a reheat flame turns blue up high

The yellow-orange of an afterburner at the runway is nearly all soot, glowing at the flame's temperature. Its violet-blue is the gas's own band emission (CH and C₂ radicals), and that doesn't depend on the soot.

A hydrocarbon flame makes soot roughly as the pressure it burns at, to a power between one and two. An afterburner burns at about the ambient pressure times its inlet's ram. So for an air-breathing engine, the library scales sootPerM by (pressure × ram)^1.5, capped at three times the sea-level value:

conditionsoot made, against sea level
sea level, standing1 (the preset's look)
9 km, standingabout ⅙
9 km at 250 m/sabout ¼

With less soot glowing, the band emission shows through: the flame turns violet-blue, and the diamonds stay. At sea level, standing, the factor is exactly one, so every preset looks there as it was tuned.

That soot is the burner flame's, so a jet only makes it while its exhaust is hot enough to be a flame: none at 1200 K, all of it by 1500 K (PLUME_SOOT_FLAME_K). On dry thrust or with the burner just lighting, it makes none. A turbine's combustor runs lean and burns its soot out before the nozzle, so a modern engine on dry thrust leaves no visible smoke.

Inside the plume the same holds sample by sample. Soot is drawn only where the gas is hot enough to glow: in the flame and behind each Mach disk. Where the exhaust has expanded or mixed below that, only the sootSurvival share is left. Cold soot can't glow, so all it could do is darken the sky behind the plume, and a jet doesn't leave that kind of smoke. To draw an old, smoky turbojet, raise sootSurvival.

A rocket doesn't breathe air. Its chamber sets its own pressure, so its soot doesn't change with altitude. Something else does: its exit pressure is fixed, so as the air thins the pressure ratio climbs and the plume balloons wide and thin. The fuel left in its exhaust also stops burning once there is too little oxygen, so a kerosene rocket's bright sheath fades as it climbs.

Whether an engine "breathes" comes out of its own params. An engine whose dryTemperatureK is well below its exitTemperatureK has a burner, so it is treated as a jet. Equal temperatures mean a rocket.

Airspeed

Air already moving with the jet shears it less. The mixing layer grows more slowly, so the potential core and the shock train last further: a jet in cruise draws a longer, tighter plume than the same engine on the stand. Airspeed also rams the inlet, which raises the pressure in a jet's pipe and, through the formula above, its soot.

A fighter's nozzle opens up to match. As the ram raises the pipe's pressure, its petals widen, so the gas leaves faster and cooler, still near the pressure the nozzle was designed for. The library does the same for an air-breathing engine, up to an exit of Mach 2.2 (PLUME_NOZZLE_MAX_MACH). Past that the ram outruns the nozzle and the jet leaves it underexpanded, but nothing like a fixed nozzle would. At Mach 1.65 the exit pressure ratio is about 1.8 rather than 6, and the gas leaves at about 1,300 K rather than 1,700 K. The bright plug of gas that would expand at the lip shrinks, and the diamond train carries on behind it. Standing, the nozzle is as built, and a rocket's bell never changes.

The reheat flame itself burns inside the jet pipe, from the spray bars to the lip. There the gas is still slow and near its stagnation temperature, a few hundred kelvin hotter than it is once it has left the nozzle, so seen from astern the exit is the brightest thing in the plume. It is as black as the soot a ray crosses in it, 1 − e^(−sootPerM × path): the most looking straight up the tail, less across the pipe from the side. Dry, only the turbine and the pipe's wall glow, dull red (dryGlow).

Air moving with the jet also stretches its shock train, which lasts as long as the jet is supersonic. Past the potential core the axis' speed over the air and its heat both fall away, and no shock can stand once what is left of that speed no longer outruns the sound in the gas there. The train has faded to a twentieth of its strength by that point. On the ground a fighter's axis goes sonic about two cores down, so it shows five or so diamonds over a few metres; in supersonic flight, with the air shearing it half as hard, it lasts about twice as long.

Flying it

Inside a <FlightProvider>, the batch reads the flight's altitude, airspeed and temperature offset whenever it changes, and every plume left without a throttle runs at the flight's:

const flight = useFlightStore();

useFrame(() => {
  flight.set({
    altitudeM: aircraft.position.y,
    airspeedMPerS: aircraft.velocity.length(),
    throttle: input.throttle,
  });
});

<Afterburner target={left_engine} />
<Afterburner target={right_engine} />

The flight wins over profile for the fields it holds. Inside an <Engine>, each plume runs at that engine's throttle instead, and sits on its exhaust.

Without a flight

The profile belongs to the batch. Write it every frame through a batch ref:

const batch = useRef<AfterburnerBatchHandle>(null);

useFrame(() => {
  batch.current?.updateProfile({
    altitudeM: aircraft.position.y,
    airspeedMPerS: aircraft.velocity.length(),
  });
});

<AfterburnerBatch ref={batch} preset="afterburner">
  <Afterburner target={left_engine} />
  <Afterburner target={right_engine} />
</AfterburnerBatch>;

updateProfile only writes uniforms. Calling it every frame is cheap.

For a fixed altitude, use the prop:

<AfterburnerBatch preset="afterburner" profile={{ altitudeM: 9000, airspeedMPerS: 250 }}>

Engines that go the other way

The trend above is the general one, but engines differ. Some burners run cleaner at the ground and smokier up high: blue on the runway, orange at altitude. That depends on how the engine schedules its fuel, which the air alone can't tell.

To draw one, schedule the engine's own soot with altitude. updateParams rewrites one instance, so call it when the value has moved enough to matter, not every frame:

const engine = useRef<AfterburnerHandle>(null);
const last = useRef(-Infinity);

useFrame(() => {
  const altitude_m = aircraft.position.y;

  if (Math.abs(altitude_m - last.current) < 200) return;
  last.current = altitude_m;

  // Clean at the ground, sootier with height. Counters the thinning air,
  // which on its own takes soot away
  const height = Math.min(altitude_m / 11000, 1);
  engine.current?.updateParams({ sootPerM: 0.15 + 4 * height });
});

sootPerM is the soot at sea level, standing. The pressure scaling is applied on top of it, so this curve has to outrun it to turn the flame orange. Watch the result and tune the numbers by eye.

The day

temperatureOffsetK warms the air at the same pressure. A hot day is a thinner one, and that changes the expansion and the haze a little. It matters most for rockets standing on the pad.

On this page