Some people have been confused in what this is so I’ll try to explain what I know about FS2020 and what I have built. Some of this is well known.
FS2020 is built on ESP (FSX/P3D) which is why there are documents explaining the same concepts with slight differences around the web. The new FS uses CoherentGT, which is a system for enabling web technologies (HTML/CSS/JS) to be used within the sim, with a C++ interop layer so that the technologies can be mixed and matched and still have a high frame rate.
The engine within the sim maintains a set a variables that are available to most components. Things like
INDICATED AIRSPEED and also things like
LIGHT TAXI ON are maintained and communicated through these global variables. The aircraft variables in the SimConnect SDK are known as
A: variables, as they can be prefixed with A: when used in the xml gauge system. There are also
L: (local) variables like
L:HUD_AP_SELECTED_ALTITUDE that are still global, but created and used independently by a gauge or model xml. The real difference is that they are defined and used freely vs. being predefined and filled in by the system.
Within the sim, Coherent loads essentially one webpage per entry in
The above instructs the game to load the gauge from
NavSystems/AS3X_Touch/AS3X_Touch.htm at 1280x800.
size_mm are used for scaling the rendered web content into the texture in the game.
Inside the game they maintain a VFS, or virtual file system (see the MSFS SDK for further detail). The VFS is very simple, it is a layered data structure where all the packages share the same file paths, but last-writer-wins when there is a conflict. So if you install a package with a duplicate
NavSystems/AS3X_Touch/AS3X_Touch.html then it will load your version instead of whatever package was registered before you.
The game uses this VFS layout:
- Base packages (Packages folder within the game directory)
- Official packages (Downloaded the first time you ran the game)
- Community packages (Addons)
When we run the gauges externally, we add one additional package source to our VFS, which enables having a layer that is added ONLY for external use. The
ExternalPackages folder will always be loaded in Chrome but never loaded within the game itself.
The sim will navigate a browser to
VCockpit.html for each gauge. Asobo either uses or left us a hook in
vcockpit.js that automatically loads
window.globalPanelData. The global data has all the information in the
[VCOCKPITXX] section from the
Ok now we see that
panel.cfg instructs the sim to load
VCockpit.html on the sim VFS with some fixed data that tells it to load a component tree at
NavSystems/AS3X_Touch/AS3X_Touch.html with a size of 1280x800.
This will work, but first you need to do some fixes:
html imports are deprecated. We need a polyfill. I used GitHub - webcomponents/html-imports: HTML Imports polyfill - this goes at the top of VCockpit.html. Without this all the
text/html templates won’t be loaded.
- It will try to load coui://html_ui/ URLs. These need to be cut off as the browser needs to get a regular relative path request instead, otherwise you’ll see errors in the console about trying to load coui:// URLs.
- Name_Z::isValid would pop on transitions with an undefined
a.str. Didn’t investigate fully.
fastToFixed because it produces number like
0.-7 on Chrome/Windows. WorkingTitle mods also include this and that’s why I have an edit for
CommonPFD_MFD.js in the external package, to nop it’s copy of
These changes are isolated in this commit, so it will be easier to redo them on the next sim update.
And now VCockpit.html with global data will display any gauge in the game! The VFS must be hosted on a webserver and with the above fixes and some static configuration data, you can load any gauge.
One last note here, is that all these paths will exceed Windows’
MAX_PATH limit, so I work around this by using
SUBST command to map a drive and chop off most of the path.
So now the gauge loads but we don’t have any live sim data or real data at all. So we need to bridge in live sim data. All the sim data comes through these methods in the game, from
simvar.js in one of the base packages:
You can use
SimVar.GetSimVarValue to read any
A: (without prefix) or
SimVar.SetSimVarValue can set
L: values and probably
A:'s that are defined as readwrite in the SDK.
SimVar.GetGameVarValue is a catch-all for things that are used like variables but aren’t defined the usual way. Some of these read from
cockpit.cfg or some interpreted version of
All I’ve seen out of
ZULU TIME in
SimVar.GetSimVarArrayValues is very special, it’s used only for fs9gps calls, and this is currently not implemented due to the complexity of getting this data across the channel. This is used to set the current position an then query for nearest airports, VOR, etc. This drives the map and the flight planner.
So we need all these to work somehow. I replace the game’s version of
SimVar.js with my own which points to
SimVarBridge.js which implements a table of ‘known vars’ and makes requests through a websocket to the server to read/write values. The server continually sends updated data and thus the table of values is updated for the next calls to
The server serves the web resources and also uses SimConnect to connect to FS2020, then uses the client data regions to establish a connection with
BridgeGauge.wasm which is loaded via the community folder. The
execute_calculator_code to read and write L:vars. Right now this needs improvement:
- Source A: vars via external SimConnect directly.
- Replace with
get_named_variable_value instead of
- Register strings instead of passing them over and over.
These changes will result in high fps.
The last part is
InGameBridge.js which gets loaded via the Community folder into the game, and continually copies some values into
L: vars so the bridge can pick them up in a predictable place. This is how the
AIRCRAFT ORIENTATION AXIS GameVar is coming through. The bridge can ONLY read
A: so anything else needs to be proxied through
In summary this covers:
- How to get all the VFS package sources (Anzu)
- How to work around file paths being longer then MAX_PATH
- How the packages are composed
- Which files drive the HTML gauge system
- A set of bridge components into the FS that read/writes data