Aircraft control
Aircraft objects provide methods for issuing clearances. All methods return this for chaining.
aircraft.climb(altitude)
Issue climb clearance.
Real-world usage: Controllers use climb clearances to establish vertical separation, allow aircraft to reach more efficient cruise altitudes, or clear aircraft above weather. Standard phraseology: "Climb and maintain FL350."
Parameters:
altitude: number- Target altitude in feet OR usefl()helper for flight levels
Returns: this: Aircraft
Examples:
// Using feet
aircraft.climb(35000);
// Using flight level helper
aircraft.climb(fl(350)); // FL350 = 35,000ft
// Climb to cruise altitude
aircraft.climb(fl(aircraft.plan.cruiseFL));
// Establish vertical separation in conflict
onConflict((pair) => {
const acA = traffic.byCallsign(pair.a);
const acB = traffic.byCallsign(pair.b);
if (acA && acB && Math.abs(acA.altFt - acB.altFt) < 1000) {
// Climb higher aircraft to establish 2000ft separation
if (acA.altFt > acB.altFt) {
acA.climb(acA.altFt + 2000);
}
}
});
// Method chaining
aircraft.climb(fl(350)).speed(420);aircraft.descend(altitude)
Issue descent clearance.
Real-world usage: Controllers issue descent clearances to prepare aircraft for arrival, establish vertical separation below conflicting traffic, or comply with airspace restrictions. Standard phraseology: "Descend and maintain FL240."
Parameters:
altitude: number- Target altitude (feet or usefl()helper)
Returns: this: Aircraft
Examples:
aircraft.descend(24000);
aircraft.descend(fl(240)); // FL240
// Descend to exit altitude for next sector
if (aircraft.plan.exitFL) {
aircraft.descend(fl(aircraft.plan.exitFL));
}
// Descend below conflicting traffic
onConflict((pair) => {
const acA = traffic.byCallsign(pair.a);
const acB = traffic.byCallsign(pair.b);
if (acA && acB) {
// Descend lower aircraft below the conflict
if (acA.altFt < acB.altFt) {
acA.descend(acA.altFt - 2000);
}
}
});aircraft.speed(speed)
Issue speed restriction.
Real-world usage: Speed control is one of the most important tools for maintaining separation. Controllers use speed adjustments to manage in-trail spacing, sequence arrivals, or slow aircraft before a turn. Standard phraseology: "Reduce speed to 320 knots" or "Maintain maximum forward speed."
Parameters:
speed: number- Target indicated airspeed in knots (180-450 typical range)
Returns: this: Aircraft
Examples:
// Standard cruise speed
aircraft.speed(400);
// Maintain in-trail separation (common technique)
const lead = traffic.byCallsign("AAL123");
const trail = traffic.byCallsign("DAL456");
if (lead && trail) {
const dist = distance(lead.pos, trail.pos);
// Target 8-10nm spacing
if (dist < 8) {
// Slow trailing aircraft to open spacing
trail.speed(Math.max(lead.targetIASKts - 30, 250));
} else if (dist > 12) {
// Speed up to close gap
trail.speed(Math.min(lead.targetIASKts + 20, 450));
}
}
// Speed restriction before waypoint (arrival sequencing)
aircraft.before("MERGE", 40, (ac) => {
ac.speed(280); // Reduce to approach speed
});aircraft.direct(...fixes) | aircraft.direct(fixes[])
Clear aircraft direct to waypoint(s), rerouting the flight plan. Accepts variadic args or a single array.
Real-world usage: Direct routings are issued to shorten flight paths, expedite traffic flow, or provide separation through lateral spacing. This is one of the most common pilot requests and controller efficiency tools. Standard phraseology: "Proceed direct KMART" or "When able, direct ROBUC."
Parameters:
...fixes: string[]- One or more waypoint names (variadic)fixes: string[]- Or pass a single array of waypoint names
Returns: this: Aircraft
Behavior:
The direct() method intelligently modifies the aircraft's flight plan based on whether the waypoint(s) are in the current route:
- Single waypoint in route: If the waypoint is already in the aircraft's route, truncates the route from that waypoint onward
- Route
[A, B, C, D]→direct(C)→[C, D]
- Route
- Single waypoint not in route: Replaces entire route with just that waypoint
- Route
[A, B, C, D]→direct(E)→[E]
- Route
- Multiple waypoints: Replaces entire route with specified waypoints
- Route
[A, B, C, D, E]→direct(C, E)→[C, E] - Route
[A, B, C, D]→direct(E, F)→[E, F]
- Route
Examples:
// Direct to waypoint - truncates route from that point
aircraft.direct("KMART");
// Direct to exit fix - useful for shortcuts
aircraft.direct(aircraft.plan.exitPoint);
// Multiple waypoints - creates custom route
aircraft.direct("KMART", "ROBUC", "HIBER");
// Array form
aircraft.direct(["KMART", "ROBUC", "HIBER"]);
// Provide lateral separation via different routing
onConflict((pair) => {
const acA = traffic.byCallsign(pair.a);
const acB = traffic.byCallsign(pair.b);
if (acA && acB) {
// Route one aircraft to alternate fix for lateral separation
const altFix = "ROBUC";
if (distance(acA.pos, fixes.get(altFix).pos) < 30) {
acA.direct(altFix);
log(`${acA.cs} direct ${altFix} for separation`);
}
}
});
// Shortcut when aircraft is close to downstream fix
const targetFix = fixes.get("ROBUC");
if (targetFix) {
const dist = distance(aircraft.pos, targetFix.pos);
if (dist < 50) {
aircraft.direct("ROBUC");
log(`${aircraft.cs} cleared direct ROBUC`);
}
}
// Send all aircraft direct to exit, bypassing intermediate fixes
onSpawn((aircraft) => {
aircraft.direct(aircraft.plan.exitPoint);
});Important Notes:
- Clears any heading vector (returns aircraft to route following)
- Resets active leg index to 0
- The route displayed on the radar will update to show the new flight plan
- The aircraft will fly the new route, with turn anticipation if enabled
aircraft.offset(offsetNm)
Apply lateral offset from route centerline.
Real-world usage: Strategic lateral offsets (SLO) are used in oceanic and high-altitude airspace to reduce the probability of collision, provide wake turbulence avoidance, and maintain separation on parallel tracks. Controllers may also use offsets to laterally separate aircraft on the same route. Standard phraseology: "Offset 2 miles right of course."
Parameters:
offsetNm: number- Offset in nautical miles (positive = right, negative = left)
Returns: this: Aircraft
Examples:
// Offset right for lateral separation on same route
aircraft.offset(2);
// Offset left for wake turbulence avoidance
onSpawn((aircraft) => {
const ahead = traffic.all().find(ac =>
ac.wake === "H" && // Heavy aircraft ahead
distance(ac.pos, aircraft.pos) < 10
);
if (ahead) {
aircraft.offset(-2); // Offset to avoid wake
log(`${aircraft.cs} offset for wake turbulence`);
}
});
// Return to centerline when clear
aircraft.offset(0);
// Parallel routing for multiple aircraft on same airway
const flights = traffic.all()
.filter(ac => ac.plan.route.includes("KMART"))
.sort((a, b) => a.altFL - b.altFL);
flights.forEach((ac, i) => {
ac.offset(i * 2); // 0, 2, 4, 6 nm lateral spacing
});aircraft.handover()
Clear aircraft for handoff to next sector. Validates altitude is within exit fix constraints.
Real-world usage: Handoffs (or handovers) transfer control and communication of an aircraft between sectors or facilities. Controllers coordinate to ensure the receiving controller accepts the aircraft and that all altitude/routing requirements are met. Standard phraseology: "Contact [next sector] on [frequency]."
Returns: this: Aircraft
Examples:
// Handover when aircraft reaches exit fix (standard practice)
onSpawn((aircraft) => {
aircraft.over(aircraft.plan.exitPoint, (ac) => {
// Ensure aircraft meets exit requirements
if (ac.altFt === ac.targetAltFt) {
ac.handover();
log(`${ac.cs} handed to next sector at ${aircraft.plan.exitPoint}`);
}
});
});
// Ensure aircraft is stable before handoff
const ac = traffic.byCallsign("AAL123");
if (ac) {
const stable = ac.altFt === ac.targetAltFt &&
ac.gsKts === ac.targetIASKts;
if (stable && ac.plan.exitPoint) {
const exitFix = fixes.get(ac.plan.exitPoint);
if (exitFix && distance(ac.pos, exitFix.pos) < 5) {
ac.handover();
}
}
}aircraft.setRoute(fixes)
Set a new route (list of waypoints) for the aircraft.
Parameters:
fixes: string[]- Array of waypoint names in order
Returns: this: Aircraft
Examples:
aircraft.setRoute(['MERIT', 'GREKI', 'JUDDS']);aircraft.hold(fix, options?) | aircraft.hold({ fix, ... })
Enter holding pattern at a waypoint.
Parameters (positional):
fix: string- Waypoint to hold atoptions?: object- Optional hold configurationlegNm?: number- Leg length in nm (default: 4)turn?: 'L' | 'R'- Turn direction (default: 'R')efc?: number- Expect further clearance time (simulation seconds)
Parameters (named):
{ fix, legNm?, turn?, efc? }- All options in a single object
Returns: this: Aircraft
aircraft.hold({ fix: 'MERIT' });
aircraft.hold({ fix: 'GREKI', legNm: 8, turn: 'L' });
aircraft.hold({ fix: 'JUDDS', legNm: 4, turn: 'R', efc: 3600 });aircraft.hold('MERIT');
aircraft.hold('GREKI', { legNm: 8, turn: 'L' });
aircraft.hold('JUDDS', { legNm: 4, turn: 'R', efc: 3600 });aircraft.exitHold()
Exit holding pattern and resume route navigation.
Returns: this: Aircraft
Examples:
aircraft.exitHold();
aircraft.hold('MERIT').exitHold(); // Hold then immediately exit (for testing)aircraft.sta(fix, time) | aircraft.sta({ fix, time })
Assign scheduled time of arrival at a waypoint.
Parameters (positional):
fix: string- Waypoint nametime: number- Target time in simulation seconds
Parameters (named):
{ fix, time }- Waypoint and time in a single object
Returns: this: Aircraft
aircraft.sta({ fix: 'MERIT', time: 3600 });aircraft.sta('MERIT', 3600);aircraft.clearILS(runwayId)
Clear the aircraft for an ILS approach to a runway.
Parameters:
runwayId: string- Runway identifier (e.g., '27L', '09R')
Returns: this: Aircraft
Examples:
aircraft.clearILS('27L');
aircraft.climb(fl(30)).clearILS('27L'); // Descend then clearaircraft.contactTower()
Hand the aircraft off to tower frequency for landing.
Returns: this: Aircraft
Examples:
aircraft.clearILS('27L');
// ... after ILS established ...
aircraft.contactTower();aircraft.goAround()
Initiate a go-around / missed approach.
Returns: this: Aircraft
Examples:
aircraft.goAround();aircraft.descendVia()
Activate "descend via" for a STAR. The aircraft will follow published altitude and speed constraints, beginning descent at the calculated top-of-descent point.
Real-world usage: "Descend via the PARCH4 arrival" - the pilot follows all published altitude and speed restrictions on the STAR. This is the standard way arrivals are managed in busy terminal areas. The controller can cancel it at any time by assigning a specific altitude.
Returns: this: Aircraft
Examples:
// Activate descend via on spawn
onSpawn((aircraft) => {
if (aircraft.procedureState?.procedureType === 'STAR') {
aircraft.descendVia();
}
});aircraft.climbVia()
Activate "climb via" for a SID. The aircraft will follow published altitude restrictions on the departure procedure.
Real-world usage: "Climb via the DEEZZ5 departure" - the pilot climbs meeting all altitude gates on the SID.
Returns: this: Aircraft
Examples:
aircraft.climbVia();aircraft.cancelProcedure()
Cancel all procedure altitude and speed restrictions. The aircraft stops following the published SID/STAR constraints. The procedure state is preserved so you can re-engage with descendVia() or climbVia() later.
Returns: this: Aircraft
Examples:
// Cancel procedure, assign specific altitude for spacing
aircraft.cancelProcedure().climb(280);
// Later, resume the STAR
aircraft.descendVia();aircraft.crossFix(fix, altFt) | aircraft.crossFix({ fix, altFt })
Override a specific procedure constraint with a controller-assigned crossing altitude. The constraint at the specified fix is changed to an "at" constraint with the given altitude.
Parameters (positional):
fix: string- Waypoint name (must be a constraint fix on the procedure)altFt: number- Altitude in feet or flight level (values < 2000 treated as flight levels)
Parameters (named):
{ fix, altFt }- Waypoint and altitude in a single object
Returns: this: Aircraft
aircraft.crossFix({ fix: 'GREKI', altFt: 20000 });
aircraft.crossFix({ fix: 'GREKI', altFt: 200 }); // FL200aircraft.crossFix('GREKI', 20000);
aircraft.crossFix('GREKI', 200); // FL200 = 20,000ftaircraft.turn.right(degrees)
Turn aircraft right by specified degrees.
Real-world usage: Heading assignments (vectors) are used for separation, sequencing, weather avoidance, and intercepts. Controllers typically use 10-30 degree turns for small adjustments and up to 90 degrees for significant lateral separation. Standard phraseology: "Turn right heading 090" or "Turn right 30 degrees."
Parameters:
degrees: number- Degrees to turn right
Returns: this: Aircraft
Examples:
// Small turn for lateral separation
aircraft.turn.right(20);
// Larger turn to avoid traffic
aircraft.turn.right(45);
// 90-degree turn for significant separation
onConflict((pair) => {
const acA = traffic.byCallsign(pair.a);
if (acA) {
acA.turn.right(90);
log(`${acA.cs} turn right 90 for traffic`);
}
});
// Turn and climb together
aircraft.turn.right(30).climb(fl(370));aircraft.turn.left(degrees)
Turn aircraft left by specified degrees.
Real-world usage: Left turns follow the same principles as right turns - used for separation, sequencing, and traffic flow. Controllers consider wind, aircraft performance, and traffic when choosing turn direction. Standard phraseology: "Turn left heading 270" or "Turn left 20 degrees."
Parameters:
degrees: number- Degrees to turn left
Returns: this: Aircraft
Examples:
// Small adjustment for spacing
aircraft.turn.left(15);
// Turn for separation
aircraft.turn.left(30);
// Downwind turn for sequencing
aircraft.turn.left(45).speed(280);
// Turn away from traffic
onConflict((pair) => {
const acA = traffic.byCallsign(pair.a);
const acB = traffic.byCallsign(pair.b);
if (acA && acB) {
// Turn aircraft in opposite directions
acA.turn.left(30);
acB.turn.right(30);
log(`${acA.cs} and ${acB.cs} diverging turns for separation`);
}
});aircraft.turn.rightTo(heading)
Turn aircraft right to specified heading.
Real-world usage: When controllers need to specify both the turn direction and final heading, they use phraseology like "Turn right heading 090" to ensure the aircraft turns the intended direction (useful when the heading could be reached by either direction).
Parameters:
heading: number- Target heading in degrees (0-360)
Returns: this: Aircraft
Examples:
// Turn right to heading 090
aircraft.turn.rightTo(90);
// Ensure right turn for traffic flow
aircraft.turn.rightTo(180);
// Method chaining
aircraft.turn.rightTo(270).climb(fl(370));aircraft.turn.leftTo(heading)
Turn aircraft left to specified heading.
Real-world usage: Like rightTo, but explicitly specifies a left turn. Used when the turn direction matters for separation or traffic flow. Standard phraseology: "Turn left heading 270."
Parameters:
heading: number- Target heading in degrees (0-360)
Returns: this: Aircraft
Examples:
// Turn left to heading 270
aircraft.turn.leftTo(270);
// Ensure left turn for separation
aircraft.turn.leftTo(180);
// Diverging turns with explicit directions
onConflict((pair) => {
const acA = traffic.byCallsign(pair.a);
const acB = traffic.byCallsign(pair.b);
if (acA && acB) {
acA.turn.leftTo(270);
acB.turn.rightTo(090);
log(`${acA.cs} and ${acB.cs} diverging on reciprocal headings`);
}
});aircraft.turn.to(heading)
Turn aircraft to specified heading (shortest turn).
Real-world usage: The most common heading assignment - aircraft takes the shortest turn to reach the heading. Standard phraseology: "Fly heading 090" or "Turn heading 270."
Parameters:
heading: number- Target heading in degrees (0-360)
Returns: this: Aircraft
Examples:
// Fly heading 090 (shortest turn)
aircraft.turn.to(90);
// Vector for intercept
const fix = fixes.get("KMART");
if (fix) {
const hdg = headingTo(aircraft.pos, fix.pos);
aircraft.turn.to(hdg);
}
// Conflict resolution
onConflict((pair) => {
const acA = traffic.byCallsign(pair.a);
if (acA) {
// Turn 30 degrees left from current heading
const newHdg = (acA.hdgDeg - 30 + 360) % 360;
acA.turn.to(newHdg);
}
});
// Method chaining
aircraft.turn.to(180).speed(420).climb(fl(350));aircraft.over(waypoint, callback) | aircraft.over({ waypoint, callback })
Register callback when aircraft passes over waypoint.
Parameters (positional):
waypoint: string- Waypoint namecallback: (ac: Aircraft) => void- Function called when aircraft reaches waypoint
Parameters (named):
{ waypoint, callback }- Waypoint and callback in a single object
Returns: this: Aircraft
aircraft.over({
waypoint: aircraft.plan.exitPoint,
callback: (ac) => ac.handover()
});
aircraft.over({
waypoint: "KMART",
callback: (ac) => ac.climb(fl(380))
});aircraft.over(aircraft.plan.exitPoint, (ac) => {
ac.handover();
});
aircraft.over("KMART", (ac) => {
ac.climb(fl(380));
});aircraft.before(waypoint, distanceNm, callback) | aircraft.before({ waypoint, distanceNm, callback })
Register callback before reaching waypoint.
Parameters (positional):
waypoint: string- Waypoint namedistanceNm: number- Distance before waypoint (nm)callback: (ac: Aircraft) => void- Function called when aircraft is within distance
Parameters (named):
{ waypoint, distanceNm, callback }- All parameters in a single object
Returns: this: Aircraft
aircraft.before({
waypoint: "KMART",
distanceNm: 30,
callback: (ac) => ac.descend(fl(240))
});
aircraft.before({
waypoint: "MERGE",
distanceNm: 20,
callback: (ac) => ac.speed(320)
});aircraft.before("KMART", 30, (ac) => {
ac.descend(fl(240));
});
aircraft.before("MERGE", 20, (ac) => {
ac.speed(320);
});aircraft.reach(waypoint)
Build route with altitude/speed constraints at waypoint. Returns a ConstraintBuilder.
Returns: ConstraintBuilder (with .altitude() and .speed() methods)
Examples:
// Ensure aircraft reaches exit altitude before exit fix
aircraft.reach(aircraft.plan.exitPoint).altitude(aircraft.plan.exitAltitude);
// Altitude constraint at waypoint
aircraft.reach("KMART").altitude(fl(280));
// Speed constraint at waypoint
aircraft.reach("MERGE").speed(320);
// Both altitude and speed constraints (chaining)
aircraft
.reach("KMART").altitude(fl(280))
.reach("MERGE").speed(320);ConstraintBuilder Methods:
.altitude(altFt)
Set altitude constraint - aircraft must reach altitude before waypoint.
Parameters:
altFt: number- Altitude in feet (or usefl()helper)
Returns: this: Aircraft
.speed(kts)
Set speed constraint - aircraft must reach speed before waypoint.
Parameters:
kts: number- Speed in knots
Returns: this: Aircraft
Aircraft object
Read-only properties:
{
cs: string, // Callsign (e.g., "AAL123")
type: string, // Aircraft type ("A320", "B738", etc.)
wake: "L"|"M"|"H"|"J", // Wake turbulence category
pos: {x, y}, // Position (nautical miles)
hdgDeg: number, // Heading 0-360
iasKts: number, // Indicated airspeed (what pilot flies)
gsKts: number, // Ground speed (affected by wind)
altFt: number, // Current altitude (feet)
altFL: number, // Current altitude (flight level)
vsFpm: number, // Vertical speed (feet per minute)
plan: {
route: string[], // Waypoint names
cruiseAltFt: number, // Planned cruise altitude (feet)
cruiseFL: number, // Planned cruise altitude (flight level)
exitPoint: string, // Last waypoint (exit)
exitAltitude?: number, // Required altitude at exit (feet)
exitFL?: number // Required altitude at exit (flight level)
},
activeLegIdx: number, // Current leg in route
targetAltFt: number, // Cleared altitude (feet)
targetFL: number, // Cleared altitude (flight level)
targetIASKts: number, // Cleared airspeed
targetHdgDeg?: number, // Cleared heading (if vectoring)
directTo?: string, // Direct-to override
offsetNm: number, // Lateral offset from route
sectorId: string, // Current sector
// Procedure state (SID/STAR) - present if aircraft has a procedure
procedureState?: {
procedureId: string, // Procedure name (e.g., "PARCH4")
procedureType: string, // "SID" or "STAR"
viaActive: boolean, // True if descend/climb via is active
constraints: [{
fix: string, // Waypoint name
altType?: string, // "at" | "above" | "below" | "between"
alt1?: number, // Primary altitude (feet)
alt2?: number, // Secondary altitude (for "between")
spdType?: string, // "at" | "atOrBelow"
speed?: number, // Speed (knots)
satisfied: boolean, // Constraint met
busted: boolean // Constraint failed
}]
}
}Key Features:
- Flight level properties:
altFL,targetFL,plan.cruiseFL,plan.exitFL - Exit constraints:
plan.exitPoint,plan.exitAltitude,plan.exitFL - Procedure state:
procedureStatewith constraint tracking - Aircraft objects have control methods:
climb(),speed(),direct(),descendVia(), etc.
IAS vs ground speed
Aircraft have both indicated airspeed (IAS) and ground speed (GS):
- IAS (
iasKts) - What the pilot flies, displayed in aircraft panel - GS (
gsKts) - Actual speed over ground, affected by wind
onTick(({ traffic, weather }) => {
traffic.all().forEach(ac => {
const wind = weather.windAt(ac.altFt);
log(`${ac.cs}: IAS ${ac.iasKts}kts, GS ${ac.gsKts}kts`);
log(` Wind: ${wind.dirDeg}° at ${wind.kts}kts`);
});
});