NCalc and JavaScript in SimHub
Last updated · SimHub 9.11.22
SimHub has two ways to compute values. NCalc is an expression language for one-line calculations, defined in .ini files in the NCalcScripts folder and loaded at startup. JavaScript runs on Jint with full ECMAScript 5.1, must return a value, reads telemetry through $prop("name"), and can keep state between calls on the root object. Use NCalc when you want a single expression with no state; use JavaScript the moment you need a variable that survives to the next call, a loop, or anything you would describe as logic rather than arithmetic.
Both languages are documented. What is not documented is when to reach for which, and what a working example looks like — which is what most of this page is.
Which one to use
| Use NCalc when | Use JavaScript when |
|---|---|
| It fits on one line | It needs more than one step |
| It is arithmetic or a comparison | It needs a loop or a branch tree |
| No memory of the last call is needed | You need state between calls |
| You want a formatted string | You need to build something conditionally |
| Blink or change detection is enough | You are tracking a value over time yourself |
The dividing line is state. NCalc computes an answer from the current telemetry. JavaScript can remember. If your problem contains the phrase "since the last lap" or "when it changes from", you want JavaScript.
There is a performance argument for NCalc — it is a smaller thing to evaluate — but at the rates these run, it is rarely the deciding factor. Choose on clarity.
NCalc
NCalc scripts live as .ini files in the NCalcScripts folder and are loaded automatically at startup. Each file contains blocks:
[Variable]- An intermediate value, used by other blocks in the same file. Not visible outside it.
[ExportProperty]- Publishes a value as a property that dashboards and LED effects can bind to. It appears as
DataCorePlugin.ExternalScript.name. [ExportLed]- Publishes a value specifically for LED use.
[ExportEvent]- Publishes an event that can trigger things elsewhere in SimHub.
Loaded at startup only. Edit an NCalc file and SimHub will not notice until it restarts. This is the first thing to check when a change appears to have no effect.
NCalc function reference
| Function | What it does |
|---|---|
isnull(value, fallback) | Returns the fallback when a telemetry value is missing. Use this constantly — missing values are normal. |
format(value, pattern) | Formats a number as a string. Decimal places, padding, units. |
padleft(value, length, char) | Pads a string. For fixed-width readouts on segment displays. |
blink(name, delay, status) | Produces a blinking on/off value. The named instance keeps its own timing. |
changed(delay, value) | True for delay after the value changes. The standard way to flash something on a gear change. |
isincreasing(value) | True while the value is rising. |
isdecreasing(value) | True while the value is falling. |
progress(start, end, current, steps) | Maps a value onto a number of steps. Exactly what an LED bar wants. |
replace(text, find, with) | String replacement. |
secondstotimespan(seconds) | Seconds into a time span, for lap times. |
timespantoseconds(span) | The reverse. |
progress and changed are the two that do the most work in practice. progress is the whole of a segmented bar in one call, and changed is how nearly every momentary flash is built.
JavaScript
SimHub runs JavaScript on Jint, supporting full ECMAScript 5.1. That is a real language — objects, arrays, closures, the standard library — with the caveat that it is ES5, so no arrow functions, no let and const, no template literals.
- It must return a value
- A script that computes something and does not
returnit produces nothing. This is the most common beginner mistake. $prop("name")- Reads a telemetry property. The bridge between the language and the car.
- The
rootobject - Persistent state between calls. Anything you attach to
rootsurvives; ordinary variables do not. This is the entire reason to choose JavaScript over NCalc. - Enabling it
- Either tick the "use javascript" checkbox, or prefix the expression with
js:. JavascriptExtensions- A folder for shared helper functions, available to all your scripts. Where anything you write twice belongs.
On errors, it re-initialises and retries every 30 seconds. So a broken script does not take SimHub down — it goes quiet and keeps trying. Useful for stability, and confusing when debugging: a script that does nothing may be erroring rather than returning nothing. Check for a null or missing property first, since that is the usual cause.
As of 9.11.20, HID read and write functions are available in both engines, which opens up talking to USB HID devices directly from a formula.
Recipes that actually come up
- Laps of fuel remaining, not litres
- Fuel divided by consumption per lap. This is the single most useful derived value in sim racing, because litres requires arithmetic while you are braking and laps does not. Needs state to average consumption over recent laps, so it is a JavaScript job.
- A flash on gear change
changed(300, [gear])in NCalc. One line, no state, the textbook case for NCalc.- A segmented rev bar
progress(start, end, rpm, ledcount). Also one line.- Delta to your own best
- Requires remembering the best, so JavaScript with the best stored on
root. - A safe value that never shows blank
isnull([something], 0). Wrap anything a game might not provide — which, per the games list, is a lot of things.- Tyre temperature colour banding
- A branch tree over four values. JavaScript, and a good candidate for a shared helper in
JavascriptExtensions.
A useful instinct: if you find yourself writing an NCalc expression with three nested conditionals, stop and rewrite it in JavaScript. It will be shorter and you will be able to read it in six months.
Debugging
- Check it returns. The most common JavaScript failure is a script that computes correctly and returns nothing.
- Wrap properties in
isnull. A missing telemetry value propagates as null through everything downstream, and the result is silence rather than an error. - Restart after editing NCalc files. They are loaded at startup only.
- Use the test data editor. Set RPM and fuel by hand and watch your formula respond, instead of driving to reproduce a condition.
- Publish intermediates temporarily. Export the middle of a calculation as its own property, look at it, then remove the export. It is the print statement of SimHub scripting.
- Remember the 30-second retry. A script that is erroring looks identical to one returning nothing, and it will keep quietly retrying rather than telling you.
Common questions
Should I use NCalc or JavaScript in SimHub?
NCalc for a single expression with no memory of previous calls — arithmetic, comparisons, formatting. JavaScript the moment you need state between calls, a loop or a branch tree. The dividing line is state: if your problem contains "since the last lap" or "when it changes from", use JavaScript.
Where do NCalc scripts go in SimHub?
As .ini files in the NCalcScripts folder, loaded automatically at startup. Because they are only read at startup, SimHub must be restarted after editing one — which is the first thing to check when a change appears to have done nothing.
What JavaScript version does SimHub support?
Full ECMAScript 5.1, running on the Jint engine. That means no arrow functions, no let or const, and no template literals — write ES5 syntax. Scripts must return a value, and telemetry is read with $prop("name").
How do I keep a value between calls in a SimHub formula?
Attach it to the root object in JavaScript. Ordinary variables do not survive between calls; anything on root does. This is the main reason to choose JavaScript over NCalc.
My SimHub script does nothing and shows no error.
Two likely causes. In JavaScript, the script may compute correctly and not return the result. Or a telemetry property is null and propagating silently — wrap properties in isnull with a fallback. Note also that a broken script re-initialises and retries every 30 seconds rather than reporting a failure.
How do I calculate laps of fuel remaining?
Fuel divided by consumption per lap, which needs the consumption averaged over recent laps — so it needs state, which makes it a JavaScript job with the running average stored on root. It is the most useful derived value in sim racing, because litres requires arithmetic while braking and laps does not.
What does DataCorePlugin.ExternalScript mean?
It is the prefix under which NCalc [ExportProperty] blocks publish their values. A property exported as "fuellaps" becomes DataCorePlugin.ExternalScript.fuellaps, and that is what you bind a dashboard element or an LED effect to.
Sources
Related guides
SimHub is developed independently by Wotever and is not affiliated with oesimracing. This page was checked against SimHub 9.11.22 on 4 August 2026; where the interface has moved on since, the version stamp tells you how far. More on what SimHub is and what it costs.