FlightGear 6 min read

What is Nasal scripting in FlightGear, and how do you use it?

Adam McEnroe
In short

Learn FlightGear Nasal scripting: run code, read the property tree, load .nas files, and fix common console, path and callback errors.

Nasal is FlightGear’s embedded, dynamically typed scripting language. It lets aircraft and add-ons read or change the simulator’s property tree, respond to events, schedule functions, and implement systems without recompiling FlightGear. Use the Nasal console for experiments, then place tested code in .nas files loaded by the aircraft or add-on.

What can Nasal control in FlightGear?

Nasal can control almost any simulator feature exposed through FlightGear’s property tree or scripting APIs. Aircraft developers use it for cockpit systems, instruments, electrical logic, failures, checklists, menus, animation-driving properties and interactions between subsystems.

The property tree is central to this. Values such as altitude, switch positions and lighting states live at paths such as /position/altitude-ft. Nasal’s getprop() and setprop() functions read and write those paths, while listeners and timers let code react without continuously polling them.

Nasal is part of FlightGear rather than a separate plug-in. Our explanation of FlightGear’s open-source aircraft and add-on architecture provides useful context for why scripts can interact so closely with the simulator.

Although its syntax has similarities to JavaScript, Nasal is not JavaScript. It uses constructs such as var, func and nil, and it has FlightGear-specific APIs rather than browser or Node.js APIs. Pasting JavaScript into the Nasal console rarely works unchanged.

How do you run your first Nasal script?

The Nasal console is the quickest place to test a few lines without changing an aircraft package. It is normally available through FlightGear’s debugging or developer menus, although the menu wording and visibility can differ between builds.

  1. Open the Nasal console. Keep FlightGear’s main log or terminal output visible as well, because parse errors and runtime exceptions are normally reported there.
  2. Confirm that code executes. Enter print("Nasal is running"); and check the console or log for the message.
  3. Read a property. Enter var altitude = getprop("/position/altitude-ft"); print(altitude);. This reads the aircraft’s altitude property without changing the simulation.
  4. Handle missing values. A property that does not exist may return nil, not zero. Test it before performing arithmetic: if (altitude == nil) { print("Altitude is unavailable"); }
  5. Test a safe write. Use a private property while learning, for example setprop("/sim/my-addon/enabled", 1);, rather than changing a flight-control or engine property.
  6. Schedule an action. A one-shot callback can be tested with settimer(func { print("Timer fired"); }, 1);, where the delay is expressed in seconds.

Use FlightGear’s property browser to find the live path and value type before writing production code. Guessing property names is a mistake we see constantly; one misplaced component or capital letter gives you a different property, sometimes creating a new node that no aircraft system reads.

Which Nasal execution method should you choose?

Choose the console for disposable tests, a loaded .nas module for persistent logic, and an XML binding only for a short event hook into that module.

MethodBest useMain limitation
Nasal consoleTesting syntax, properties and individual functionsCode is not retained when the session closes
Aircraft or add-on .nas fileSystems, instruments and reusable functionsMust be loaded from the correct package path and namespace
XML Nasal bindingCalling a module function from a button, key or menu actionLarge inline scripts become difficult to maintain and debug

How do you load a persistent .nas file?

A persistent aircraft script belongs inside the add-on aircraft package, not in FlightGear’s bundled base files. This keeps the modification portable and stops a simulator update from silently replacing it.

  1. Back up the aircraft package. Work on a separate copy if you are modifying an existing aircraft.
  2. Create the script file. Aircraft packages commonly keep scripts in a directory named Nasal, with filenames ending in .nas.
  3. Add your functions. For example, var report_altitude = func { print(getprop("/position/altitude-ft")); } defines a function without calling it.
  4. Register the module. Aircraft configuration normally loads the file from a named entry inside its <nasal> section. A simplified example is <nasal><my_module><file>Aircraft/MyAircraft/Nasal/my-script.nas</file></my_module></nasal>.
  5. Match the real package path. The example path is not universal. Follow the path convention already used by that aircraft and preserve letter case, especially on case-sensitive operating systems.
  6. Restart and inspect the log. A syntax error can prevent the entire module from loading. Once loaded, its public functions can be called through the module namespace, such as my_module.report_altitude().

Do not edit FlightGear’s shared Nasal directory merely to customise one aircraft. If scripts depend on a particular simulator build or property layout, preserve your add-on files and follow our procedure to back up and update FlightGear safely.

How do listeners and timers work?

Listeners react when a property changes, while timers run a function after a specified delay. A listener can be attached with code such as setlistener("/sim/my-addon/enabled", func { print("State changed"); });.

Listeners are usually preferable when code only needs to respond to a switch or state change. Timers suit delayed actions and modest periodic updates. Avoid repeatedly scheduling expensive functions at very short intervals; heavy calculations can reduce frame rate and are better handled by a dedicated subsystem or a lower-frequency design.

Reloading a script can create a second listener or timer while the old callback is still active. Retain listener handles so they can be removed during shutdown, or restart FlightGear after structural changes when you are unsure what remains registered.

Why is a Nasal script not working?

Most Nasal failures come from an incorrect path, a load error, unexpected nil, or code running before the aircraft has created the property it needs.

  • No output: check FlightGear’s log for a parse error, an unknown symbol or a missing script file. One syntax error can stop module initialisation.
  • Unknown module: confirm that the module name in the aircraft configuration matches the name used by the caller.
  • Property always returns nil: inspect it in the property browser while the affected aircraft is loaded. Aircraft-specific properties may not exist in another model.
  • A property changes but immediately changes back: another subsystem probably owns that value and rewrites it every frame. Find the corresponding control, command or input property rather than forcing an output property.
  • Works in the console but not at startup: the script may run before the relevant subsystem is ready. Defer the operation with a timer, an initialisation function or a listener for the required state.
  • Works on one computer only: check filename case, package search paths and any simulator-specific APIs used by the script.
  • Runs twice: look for duplicate module entries, repeated XML bindings or listeners left behind after a manual reload.

Treat every downloaded .nas file as executable add-on logic. Read unfamiliar scripts before loading them, particularly when they write control properties, register recurring callbacks or call specialised FlightGear APIs.

AI Assistant New

Still stuck? Ask Fly Away

Ask Fly Away is our AI flight-sim assistant. Ask your exact question and get a direct, step-by-step answer in seconds — free to try.

Ask Fly Away Free preview · unlimited for PRO members