Developers
Native HMI and scripting
Create the screen, connect its events, and keep your process logic readable with setup() and loop().
JetFlight is also an application runtime. An Arduino-style structure gives a machine builder a familiar starting point: initialize once in setup(), then do a small amount of recurring work in loop(). Use the same application to show the operator what is happening, respond to controls, and coordinate supported devices.
The scripts use JavaScript with an ES5-compatible syntax. The familiar structure does not require an Arduino board. The application runs on the JetFlight host; expansion-board firmware is a separate layer.
A small program with clear responsibilities
| Part | What belongs here | Example |
|---|---|---|
setup() | Create widgets, load settings, subscribe to events | Build the screen and attach a button callback |
loop() | Short, repeatable checks and scheduled updates | Refresh a reading, detect a stale sensor |
| Event callbacks | React to a specific change | A button is pressed, serial data arrives, a program finishes |
| Modules | Keep device adapters and process logic separate | require('./devices/level-sensor') |
The runtime calls setup() once and repeatedly schedules loop(). Its nominal loop interval is about 1 ms; host scheduling and script workload determine the actual cadence. Use elapsed time for slower tasks and keep each iteration short. A script loop is not a replacement for the motion controller's timing loop or a hard real-time safety function.
Draw the HMI and connect the data
Create a native screen with labels, buttons, sliders, containers, a digital readout, or a G-code viewer. Position and style the widgets, then connect them directly to application state:
- State to display:
ui.setText(label, value)refreshes a reading. - Control to event:
ui.button(parent, text, callback)attaches the action to the button. - Value to logic:
ui.onValueChanged(slider, callback)receives the operator's chosen value. - Motion to HMI:
motion.getPosition()andmotion.on('COMPLETE', callback)expose the engine's state and events. Aui.dro()widget can display the motion readout.
This is a native, script-defined UI. You create the layout and its bindings together; a separate browser front end is not required. A drag-and-drop HMI editor is not part of this documented workflow.
Example: a sensor panel you can understand at a glance
Save this as main.js. It creates a native panel with a simulated level reading, a slider, and a button to pause incoming samples. Pause for more than two seconds and the display marks the reading unavailable. No controller is connected and no physical output is written.
var readingLabel, statusLabel;
var level = 42;
var receiving = true;
var lastSample = 0;
var nextPaint = 0;
function setup() {
var screen = ui.screen();
ui.setStyle(screen, { bgColor: '#101923' });
var title = ui.label(screen, 'Process station · simulated sensor');
ui.setPos(title, 24, 24);
readingLabel = ui.label(screen, 'Waiting for a sample');
ui.setPos(readingLabel, 24, 70);
statusLabel = ui.label(screen, 'SIMULATION · no physical outputs');
ui.setPos(statusLabel, 24, 110);
var slider = ui.slider(screen, 0, 100, level);
ui.setPos(slider, 24, 160);
ui.setSize(slider, 280, 20);
ui.onValueChanged(slider, function(value) {
level = value;
});
var toggle = ui.button(screen, 'Pause / resume sensor', function() {
receiving = !receiving;
});
ui.setPos(toggle, 24, 210);
ui.setSize(toggle, 280, 44);
}
function loop() {
var now = Date.now();
if (now < nextPaint) return;
nextPaint = now + 100;
if (receiving) lastSample = now;
var stale = now - lastSample > 2000;
ui.setText(readingLabel, stale ? 'Level: unavailable' : 'Level: ' + level + '%');
ui.setText(statusLabel, stale
? 'SENSOR STALE · inspect the connection'
: 'SIMULATION · no physical outputs');
}
Run it with the JetFlight executable for your platform:
JetFlight --script ./main.js --port 49397
Use a separate working directory and an unused port when JC3 is already running. The example opens a native window, so the selected build needs a compatible display backend. On Windows the executable is JetFlight.exe; on macOS/Linux an executable in the current directory can be launched as ./JetFlight.
The data flow is deliberately small: the slider changes level; the button changes receiving; loop() turns that state into visible labels. Replace the simulated sample source with an explicitly supported device adapter while retaining the same screen and stale-data handling.
From a panel to a machine application
Keep four things separate as the application grows:
- Device adapters translate device messages into named values and report disconnections.
- Process logic decides what those values mean, including permitted state transitions.
- The HMI displays the state and submits operator requests.
- Configuration holds machine-specific addresses, limits, units, and settings.
Modules, asynchronous file operations, SQLite, TCP, serial ports, and workers provide building blocks. A sensor or device with a new protocol still needs its own adapter; the presence of a USB or CAN connector does not supply that protocol automatically.
For motion applications, custom G/M handlers can add process-specific behavior. Keep GUI refresh work and ordinary device polling out of timing-critical control. See distributed automation for multiple stations and mixed drives for command versus feedback.
Package once, deploy deliberately
JetFlight compiles your scripts to bytecode and bundles them with assets in a .jfpkg application. Manufacturers can deliver their HMI and process logic without supplying the original readable source files, keeping their own application closed source.
JetFlight --build ./my-hmi -o ./MyMachine.jfpkg
JetFlight --run ./MyMachine.jfpkg --port 49397
The source directory needs a main.js entry point. Build and run with the same JetFlight binary; compiled packages are runtime-dependent. Bytecode provides a barrier to casual source inspection, but it does not encrypt the application or prevent copying the package. Keep credentials outside it.
See proprietary applications with .jfpkg for the complete workflow: what you retain privately, what you deliver, clean packaging, runtime compatibility, and the boundaries of source protection.
On a dedicated control computer, a startup script can launch the packaged HMI directly. JC3 Appliance controls explains both the integrated JC3 workstation and custom OEM paths.
Starter applications
A native plasma HMI already exists with a DRO, G-code viewer, and run controls. Its portable starter distribution is being prepared. Mill, router, and lathe starters are planned. Published starters will be open source with commercial reuse, their actual license, and third-party notices. The JetFlight runtime and core controller firmware remain closed source and free to download.
Reviewed 2026-11-06. Confirm the hardware catalog and current published release before connecting a machine.