AirPanel lets you design interactive 2D aircraft cockpit panels (overhead, pedestal, glareshield, MCDU, etc.) and connect them to Microsoft Flight Simulator (2020/2024) via SimConnect. Define your panel in an XML file, use Lua for logic, and AirPanel draws the window and handles every switch, knob, button, and light – no coding of graphics or input handling required. Includes XML Schema validation for error-free development.

Screenshot of the Airbus A320 glareshield panel available in the /Samples/A320 directory

Screenshot of the classic six pack panel available in the /Samples/SixPack directory
- Rich control set – Buttons, knobs, multi-position switches and rotary selectors, potentiometers (rotary & linear), wheels, and more.
- Vector & texture based – Use pure vector graphics or textured animations (e.g., switch levers, rotating knobs).
- Dynamic properties – Color, position, text, visibility driven by Lua expressions – update in real time.
- Flexible layout – Absolute/relative positioning, polar offsets, alignment, plus series containers (
RectangularSeries,CircularArray). - Templates – Reuse common components (annunciators, switches, buttons) with custom parameters.
- SimConnect integration – Read simulator variables, trigger events, set input events (B: vars), and even execute calculator code via MobiFlight WASM.
- Touch & mouse – Fully supports mouse (click, drag, wheel) and touch (tap, long press, drag).
- Multiple panels – Combine several panels in one project; each has its own resizable, movable window.
- Developer tools – Live reload, bounding boxes, grid overlay, output console, input event inspector.
- Download the latest
AirPanelSetup.exefrom the Releases page. - Run the installer – it creates the program folder and subfolders:
Manuals/– full tutorial and reference guideSamples/– example projects (Basic, A320, G1000, SixPack)XSD/– XML schemas for validation
- Via welcome window – Double‑click
AirPanel.exe. Use the file browser or recent projects list to open a.pnlfile. - Direct open – Double‑click any
.pnlfile, or pass it as command line argument:
AirPanel.exe MyPanel.pnl
Once open, right‑click a panel to access the context menu (connect to MSFS, zoom, saved views, developer options, etc.).
Start from one of the samples in the Samples/Basic folder, e.g., Simple_Switch.pnl. It shows a 3‑position switch controlling two annunciators – entirely defined in XML + Lua, no textures required.
A .pnl file is XML with a root <AirPanel> element containing:
<Import>– include other panel files<Resources>– global textures, fonts, colors, animations<Communication>– register SimVars and events<Panel>– one or more panels, each with:<LuaOnce>– initialization code<LuaLoop>– periodic scripts (with<Delay>)<Layout>– window and scene (grid, background)<Entities>– all visual elements (controls, shapes, text)
This project defines a 5-position rotary selector for the EFIS map mode control on an Airbus A320 / A330 aircraft; works with IniBuilds Airbus family.
<?xml version="1.0" encoding="utf-8"?>
<!-- ****************** A320 EFIS map mode rotary selector example ********************** -->
<!-- Root element with XML Schema validation -->
<AirPanel xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://github.com/hal971/AirPanel ../../XSD/AirPanel.xsd"
xmlns="https://github.com/hal971/AirPanel" >
<!-- GLOBAL RESOURCE DEFINITIONS -->
<!-- These resources are common to all panels defined in this file or in imported files -->
<!-- Assets like textures and fonts are preferably defined here -->
<Resources>
<!-- Texture for the EFIS knob graphic -->
<Texture Name="TexEfisKnob" Path="images/EFIS_knob.png" />
<!-- Custom font for panel text -->
<Font Name="Gordon" Path="fonts/URW Gordon W01 Medium.ttf" />
</Resources>
<!-- SIMCONNECT COMMUNICATION -->
<Communication>
<!-- Defines the SimConnect variable for EFIS map mode
This particular variable refers to the A320/A330 family by IniBuilds
The variable can be referenced via an user-defined alias -->
<SimVar Name="L:INI_MAP_MODE_CAPT_SWITCH" Alias="map_mode_cpt_sel" Units="number" Type="double" />
</Communication>
<!-- PANEL CONFIGURATION -->
<Panel Name="A320 EFIS mode ">
<!-- LOCAL RESOURCE DEFINITIONS
These resources are local to each panel
Colors and Styles are preferably defined here
Templates can be defined only locally -->
<Resources>
<!-- Text styling for panel labels -->
<Style Name="PanelText" Font="Gordon" Size="14" FillColor="White" Bold="True" LetterSpacing="1" />
<!-- Custom color definitions -->
<Color Name="EngravingColor" R="255" G="190" B="0" /> <!-- Amber color for text and graphic -->
<Color Name="PanelColor" R="80" G="120" B="145" /> <!-- Airbus panel color -->
</Resources>
<!-- VISUAL LAYOUT -->
<Layout>
<!-- Resizable window without tilebar -->
<Window Width="200" Height="180" Left="200" Top="100" Antialiasing="8" Resize="true" />
<!--
Scene properties:
- Width="0" and Height="0" means same size as window
- Background color matches Airbus panel gray (PanelColor)
- 10x10 grid
-->
<Scene Width="0" Height="0" BackgroundColor="PanelColor" GridX="10" GridY="10" />
</Layout>
<!-- PANEL ELEMENTS -->
<Entities>
<!-- Default settings for all child elements -->
<Default GridMode="Relative" Layer="11" Text.Layer="20"
UpdateInterval="100" Text.Style="PanelText">
<!-- 5-POSITION ROTARY SELECTOR -->
<!-- dedicated Knob graphic -->
<!-- 5 Total positions -->
<!-- Initial rotation at 9 o'clock = -90° -->
<!-- 45° between positions -->
<!-- 200 ms Animation duration (rotation time between steps) -->
<!-- Center position on (10,10) grid coordinates-->
<!-- Use absolute grid positioning -->
<Selector Texture="TexEfisKnob" Positions="5" RotationStart="-90" RotationStep="45" RotationTime="200" GridX="10" GridY="10" GridMode="Absolute">
<!--
POSITION DEFINITIONS
Braces {} enclose Lua script expressions
Action (Execute when selected):
- Runs when selector enters this position
- Sets the sim variable to corresponding mode value
Trigger (Conditional rotation):
- Continuously evaluated (every 100ms)
- When returns true, forces selector to rotate to this position
- Synchronizes physical position with sim state
-->
<!-- Position 0: LS (Landing System) -->
<Position Index="0"
Action="{setSimVar('map_mode_cpt_sel', 0)}"
Trigger="{value = (getSimVar('map_mode_cpt_sel') == 0)}" />
<!-- Position 1: VOR -->
<Position Index="1"
Action="{setSimVar('map_mode_cpt_sel', 1)}"
Trigger="{value = (getSimVar('map_mode_cpt_sel') == 1)}" />
<!-- Position 2: NAV (Navigation) -->
<Position Index="2"
Action="{setSimVar('map_mode_cpt_sel', 2)}"
Trigger="{value = (getSimVar('map_mode_cpt_sel') == 2)}" />
<!-- Position 3: ARC -->
<Position Index="3"
Action="{setSimVar('map_mode_cpt_sel', 3)}"
Trigger="{value = (getSimVar('map_mode_cpt_sel') == 3)}" />
<!-- Position 4: PLAN -->
<Position Index="4"
Action="{setSimVar('map_mode_cpt_sel', 4)}"
Trigger="{value = (getSimVar('map_mode_cpt_sel') == 4)}" />
<!-- Amber arc indicator behind the knob
- Since animation propagates to children elements,
mark this as Fixed (doesn't rotate with the knob) -->
<Arc Color="EngravingColor" Radius="45" Angle="180" Thickness="2" Rotation="180" Fixed="True" Layer="20" />
<!--
Position markers around the knob:
- Circular array of 5 rectangles
- 47px radius (just outside the knob)
- 45° spacing starting at 180° (left center)
-->
<CircularArray Radius="47" Space="45" StartAngle="180" ElementsCount="5" RotateElements="true">
<Rectangle FillColor="EngravingColor" Width="6" Height="2" OutlineThickness="0" Layer="20" Fixed="True" />
</CircularArray>
</Selector>
<!-- MODE LABELS AROUND THE KNOB -->
<!-- A CircularSeries places different elements in a regular circular pattern -->
<CircularSeries Radius="60" Space="45" StartAngle="180" RotateElements="False">
<Text FillColor="EngravingColor" String="LS" /> <!-- Position 0 -->
<Text FillColor="EngravingColor" String="VOR" DeltaX="-5" DeltaY="5" /> <!-- Position 1 -->
<Text FillColor="EngravingColor" String="NAV" /> <!-- Position 2 -->
<Text FillColor="EngravingColor" String="ARC" DeltaX="7" DeltaY="5" /> <!-- Position 3 -->
<Text FillColor="EngravingColor" String="PLAN" DeltaX="8" /> <!-- Position 4 -->
</CircularSeries>
</Default>
</Entities>
</Panel>
</AirPanel>

Screenshot of the rendered selector
- Full Tutorial & Reference – Installed with AirPanel (
Manuals/folder) or available on the GitHub repo. - XML Schema – Use
XSD/AirPanel.xsdin your editor for auto‑completion and validation.
Check out sample panels in the /Samples directory:
Basic Samples- basic panels ranging from a simple 3-position switch to a more complex panel with multiple switches and annunciatorsA320_Glareshield.pnl- Airbus A320 FCU, EFIS and warning lights in a single panelA320_MCDU.pnl- Airbus A320 MCDU with transparent background to overlay the panel onto the MCDU screen detached from the virtual cockpitG1000_PFD.pnl and G1000_MFD.pnl- Garmin G1000 PFD and MFD units with transparent backgroundSixPack.pnl- Panel with the classic six primary instruments of an aircraft
AirPanel would not exist without these great open‑source projects:
- SFML – Graphics, windowing, input
- pugixml – Fast XML parsing
- Dear ImGui – Menus & dialogs
- Lua – Embedded scripting
AirPanel is released under the GPLv3 License. See LICENSE for details.
Have questions or ideas? Open an issue on GitHub.