aeronautic

3. Driving it from code

Throttle, aim and altitude every frame through refs, without re-rendering.

Props are for settings. Some values change every frame: a throttle on a flight model, a vectoring nozzle, the aircraft's altitude. Write those through a ref, and nothing re-renders.

Throttle and aim, per engine

<Afterburner ref> gives you an AfterburnerHandle. Its throttle is a plain field, cheap to write every frame. setDirection turns the nozzle:

import { useFrame } from "@react-three/fiber";
import {
  Afterburner,
  type AfterburnerHandle,
} from "@aeronautic/afterburner/react";

const engine = useRef<AfterburnerHandle>(null);

useFrame(({ clock }) => {
  if (!engine.current) return;

  const t = clock.elapsedTime;

  engine.current.throttle = 0.55 + 0.55 * Math.sin(t * 0.9);
  engine.current.setDirection([1, 0.08 * Math.sin(t * 0.6), 0]);
});

<Afterburner ref={engine} preset="afterburner" />;
throttle-ref.tsx
Starting…

The handle is stable for the component's life. You can write to it before the nozzle is in a batch: the value is kept and applied once it is.

The air, per batch

Altitude, airspeed and the day's temperature belong to the profile, which all of a batch's nozzles share. Take a ref to an <AfterburnerBatch> and call updateProfile. It merges the fields you give and only writes uniforms:

import {
  Afterburner,
  AfterburnerBatch,
  type AfterburnerBatchHandle,
} from "@aeronautic/afterburner/react";

const batch = useRef<AfterburnerBatchHandle>(null);

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

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

Watch the colour as it climbs: orange at the ground, blue-violet near 11 km.

altitude-sweep.tsx
Starting…

Flight and altitude explains why it changes.

Changing the engine

updateParams on the handle merges new params into the nozzle's and keeps the rest. It rewrites that one instance: cheap, but not free. Call it when something changes, not every frame:

engine.current?.updateParams({ exitMach: 1.6, sootPerM: 1.2 });

setParams replaces them all.

A prop wins again whenever it changes. If you pass params and also call updateParams, the next change to the params prop overwrites what you wrote. Give each field one owner.

That's the whole API. The guides go deeper on each part, and the reference lists everything.

On this page