07-31-2026 01:11 PM
I've heard about people over the years making their UI's in a different language (generally web-based), and having LabVIEW talk to it at the backend. I've never delved into that, but I'd like to give it a shot. How has everyone's experience been using that method?
07-31-2026 01:29 PM
I'm just about 90% ready to pull the trigger on this approach too.
Anyone using Grafana in a webcontrol?
07-31-2026 01:34 PM
In lv 2026 there is any web control. An earlier versions that is the sklien control too.
The new web control has a method which can be used to execute JavaScript within the page and return data. If you combine that with the jdpscience JSON toolkit it becomes extremely powerful because you don't need a web server you can directly from your block diagram execute JavaScript within the control and data back from it. The web control supports all standard modern HTML5 so you can make very rich uis. I have a JavaScript function to queue up button presses and poll for clicks, and I make functions to update controls and indicators. It is extremely fast because it is not using a web server.
The key is LLMs are brilliant but making uis 🙂 I start with the llm and I ask it to give me a test harness toolbar and then I can test this in Chrome with developer tools and then I can edit the code myself which helps me to learn how the code works. I'll try and make a small example over the weekend. I'm pretty sure I can do a better job of the polling response. But it works as is.
07-31-2026 01:42 PM
I've looked briefly at Grafana though not in a web control. My two issues are 1- virtually all of my data is in relative time format, not absolute, which most of Grafana is designed for, and 2- either having to host a Grafana instance on our server (which means you can't use this software off-network at a test facility) or having to locally host a Grafana instance on your local machine, which complicates installers, debugging, etc.
07-31-2026 01:43 PM
@MichaelS78 wrote:
In lv 2026 there is any web control. An earlier versions that is the sklien control too.
The new web control has a method which can be used to execute JavaScript within the page and return data. If you combine that with the jdpscience JSON toolkit it becomes extremely powerful because you don't need a web server you can directly from your block diagram execute JavaScript within the control and data back from it. The web control supports all standard modern HTML5 so you can make very rich uis. I have a JavaScript function to queue up button presses and poll for clicks, and I make functions to update controls and indicators. It is extremely fast because it is not using a web server.
The key is LLMs are brilliant but making uis 🙂 I start with the llm and I ask it to give me a test harness toolbar and then I can test this in Chrome with developer tools and then I can edit the code myself which helps me to learn how the code works. I'll try and make a small example over the weekend. I'm pretty sure I can do a better job of the polling response. But it works as is.
I'd love to see an example of this. I'm totally new to JavaScript/HTML5.
07-31-2026 01:54 PM
It's Friday evening now in the UK. What I will try and do is get my json viewer simplified and I might try and improve the LabVIEW to make it a good example and then post it here as a starting point and I would welcome any feedback! I'll try and get it posted by the end of the weekend!
08-03-2026 03:12 AM
@BertMcMahan wrote:I'd love to see an example of this. I'm totally new to JavaScript/HTML5.
In general, I'm not a huge enthusiast of web-based user interfaces because of the additional overhead involved. You have LabVIEW, then a web container, then a browser running inside it, and finally the application code executing inside the browser's runtime environment, with all the associated limitations and performance penalties...
That said, however, I'm seeing more and more web-based GUIs these days, and some of them are surprisingly responsive, CPUs are fast enough nowadays.
Back to the original topic: if we're talking about such GUI enhancements, especially themed UIs with support for dark mode, there are many options available using JavaScript, HTML5, and related technologies. Personally, I would probably choose an approach based on WebAssembly (WASM). It is a relatively modern solution and offers good performance.
I would use Rust together with a GUI framework that supports WASM (egui in this case), wrap the entire GUI into a web application, and then publish it through the NI Application Web Server.
Source code (a trivial Sieve of Eratosthenes example):
//==============================================================================
//
// Title: Sieve of Eratosthenes (Rust / egui / WASM Example)
// Purpose: Demonstrates a simple cross-platform GUI application built
// with Rust and egui/eframe. The application computes prime
// numbers using the Sieve of Eratosthenes algorithm and can be
// compiled either as a native desktop application (Windows,
// Linux, macOS) or as a WebAssembly (WASM) web application.
//
// Created on: 03.08.2026 at 09:29:29 by Andrey Dmitriev.
//
// Notes:
// - Native desktop build uses eframe::run_native().
// - Web build uses eframe::WebRunner and can be bundled with Trunk.
// - Compatible with NI Web Application Server and Web Browser Control
// - Supports runtime switching between light and dark themes.
//
// [dependencies]
// eframe = "0.35.0"
// egui = "0.35.0"
// wasm-bindgen = "0.2.126"
// wasm-bindgen-futures = "0.4.76"
// web-sys = "0.3.103"
//
//==============================================================================
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
use eframe::egui;
//==============================================================================
// Desktop version
//==============================================================================
#[cfg(not(target_arch = "wasm32"))]
fn main() -> eframe::Result<()> {
// Force DX12 on Windows to avoid WGPU crash on older GPUs (e.g., Dell M6800)
#[cfg(target_os = "windows")]
unsafe {
std::env::set_var("WGPU_BACKEND", "dx12");
}
let options = eframe::NativeOptions {
viewport: egui::ViewportBuilder::default()
.with_inner_size([480.0, 320.0]),
..Default::default()
};
eframe::run_native(
"Sieve of Eratosthenes",
options,
Box::new(|cc| {
cc.egui_ctx.set_visuals(egui::Visuals::light());
Ok(Box::new(MyApp::default()))
}),
)
}
//==============================================================================
// Web version (trunk)
//==============================================================================
#[cfg(target_arch = "wasm32")]
fn main() {
use wasm_bindgen::JsCast;
let web_options = eframe::WebOptions::default();
wasm_bindgen_futures::spawn_local(async {
let document = web_sys::window()
.expect("No window")
.document()
.expect("No document");
let canvas = document
.get_element_by_id("the_canvas_id")
.expect("Failed to find canvas")
.dyn_into::<web_sys::HtmlCanvasElement>()
.expect("Not a canvas");
let start_result = eframe::WebRunner::new()
.start(
canvas,
web_options,
Box::new(|cc| {
cc.egui_ctx.set_visuals(egui::Visuals::light());
Ok(Box::new(MyApp::default()))
}),
)
.await;
if let Some(loading_text) = document.get_element_by_id("loading_text") {
match start_result {
Ok(_) => loading_text.remove(),
Err(e) => {
loading_text.set_inner_html(
"<p> The app crashed. See console for details. </p>",
);
panic!("Failed to start eframe: {e:?}");
}
}
}
});
}
//==============================================================================
// Idiomatic sieve of Eratosthenes - Prime number calculation - O(n log log n)
//==============================================================================
pub fn sieve_of_eratosthenes(n: usize) -> Vec<usize> {
if n < 2 {
return Vec::new();
}
let mut is_prime = vec![true; n + 1];
is_prime[0] = false;
is_prime[1] = false;
let limit = (n as f64).sqrt() as usize;
for i in 2..=limit {
if is_prime[i] {
for j in (i * i..=n).step_by(i) {
is_prime[j] = false;
}
}
}
let mut primes = Vec::with_capacity(n / 10);
for (i, &prime) in is_prime.iter().enumerate() {
if prime {
primes.push(i);
}
}
primes
}
//==============================================================================
// App
//==============================================================================
// #[derive(Default)]
pub struct MyApp {
input_n: usize,
primes: Vec<usize>,
}
impl Default for MyApp {
fn default() -> Self {
Self {
input_n: 1000, // ← default slider value
primes: Vec::new(),
}
}
}
impl eframe::App for MyApp {
fn clear_color(&self, visuals: &egui::Visuals) -> [f32; 4] {
if visuals.dark_mode {
[0.05, 0.05, 0.05, 1.0]
} else {
[0.9, 0.9, 1.0, 1.0]
}
}
fn ui(&mut self, ui: &mut egui::Ui, _frame: &mut eframe::Frame) {
ui.add_space(6.0);
ui.heading("Sieve of Eratosthenes");
ui.add_space(6.0);
ui.add(egui::Slider::new(&mut self.input_n, 10..=10000).text("Max N"));
ui.add_space(6.0);
if ui.button("Toggle Theme").clicked() {
let dark = ui.style().visuals.dark_mode;
ui.ctx().set_visuals(if dark {
egui::Visuals::light()
} else {
egui::Visuals::dark()
});
}
if ui.button("Compute").clicked() {
let n = self.input_n.max(10);
self.primes = sieve_of_eratosthenes(n);
}
ui.separator();
ui.label(format!("Primes found: {}", self.primes.len()));
ui.separator();
if self.primes.is_empty() {
ui.label("No primes computed yet.");
} else {
let primes_str = self
.primes
.iter()
.map(|p| p.to_string())
.collect::<Vec<_>>()
.join(", ");
egui::ScrollArea::vertical().max_height(200.0).show(ui, |ui| {
ui.label(primes_str);
});
}
}
}
Then it works, for example:
The complete project is attached, including the compiled WASM binaries, assuming you trust me. 🙂
To build it, first download and install Rust (it's free).
Then add the WASM target (this only needs to be done once):
rustup target add wasm32-unknown-unknown
I also recommend installing Trunk, which is a WASM application bundler. This is also a one-time installation.
cargo install --locked trunk
It simplifies the build process, performs optimization and bundling automatically, and makes development and debugging more convenient. For example, it can run a local development server directly from your project.
Alternatively, you can install wasm-pack:
cargo install wasm-pack
If you're starting a project from scratch, create the project and add the required dependencies (they are already included in the attached example project):
cargo new sieve
cd sieve
cargo add eframe egui wasm-bindgen wasm-bindgen-futures web-sys
Place the base index.html file in the project root and copy the source code into src/main.rs.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>egui web</title>
<style>
html, body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
overflow: hidden;
}
canvas {
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<canvas id="the_canvas_id"></canvas>
</body>
</html>As a development environment, I can recommend JetBrains RustRover, but you can also use Zed, Visual Studio Code with the appropriate Rust extensions, or even Notepad if you're feeling adventurous.
Now you can build the WASM project with:
trunk build --release --public-url . --no-default-features
After the build completes, the dist folder will contain three files: HTML, JavaScript, and WASM, for example:
1.113 index.html
145.258 sieve-fb1f2a328e2145ad.js
9.403.112 sieve-fb1f2a328e2145ad_bg.wasm
3 File(s) 9.549.483 bytes
Just copy the entire dist folder to the NI Web Server. In case of a 64-bit server, the default location is:
C:\Program Files\National Instruments\Shared\NI WebServer\www\dist
By the way, the server architecture bitness (32-bit by default) and the default port (8080) can be configured by navigating to:
http://localhost:3582/web-config/web-servers
The application is then accessible at:
http://localhost:8080/dist/
And in LabVIEW:
The result is shown in the animated GIF above.
As you can see, even this simple GUI requires around 200 lines of code. If you build something more sophisticated, the amount of code grows quickly. You will also need to determine how to interact with this component from LabVIEW, perhaps by wrapping the GUI into a library or communicating through JavaScript directly, as such integrations are seems to be possible.
There are also some restrictions and limitations. For example, file access is not as straightforward as in desktop applications, drag-and-drop is disabled by default, and browser security restrictions must be taken into account.
One advantage of this approach is that you can also build a native Windows, Linux, or macOS application from the same codebase:
cargo build --release
The executable can then be found in the target\release directory.
Anyway, if the LabVIEW VI is merely a wrapper around a Web Control hosting the GUI, it may not make much sense compared to running the native application directly. However, depending on the use case, this approach can still be quite useful when integrating with LabVIEW.
08-03-2026 03:46 AM
Also very much interested!
08-03-2026 10:13 AM
As promised I have put a lot of time into making this demo so it has a lot of features to demonstrate and show use cases. In doing so, i found the ExecuteJavascript note cannot wait on a Promise so I had to revert to polling. Ticket raised with NI to discuss. I hope this sparks interest and NI may consider it?
This demo uses the JDP Science JsonText toolkit. LabVIEW 2026 is needed for the browser control. Unzip the web compenents folder.
I do not know any (much) html, javascript or CSS. I will be learning! all of the code here has been tested by me. It was all created with Prompt Engineering which I believe is VALID for things like UI layout and behaviour as we maintain control over the architecture and business logic. We then benefit from patterns and suggestions that are in common use everywhere and have no IP value: we can make familiar useable UI's easily and spend the time on the true value: the business logic.
The demo is a Single VI. My hope is this get traction and between us folks here we can make a re-useable wrapper for UI control.
The web page can be loaded in a browser and tested. There is a full LabVIEW test toolbar to test all LV functions without LV. Chrome developer tools can then be used to debug layout issues. The toolbar at the bottom of the page lets you drive almost every function. The LabVIEW app as it is drives some of the functions. like menu and dark mode 🙂
I'm looking forward to feedback. this is a fully (partially) functional demo. I hope some of you have fun!
08-03-2026 10:15 AM
Added note: there is no webserver needed for this at all. It is entirely possible to add an API to be able to use both the JS interface and a webAPI but this demo was to show the JS node and the power of no webserver! I have already made multi UI apps with multiple VI's all of which are using the browser as the full page UI. And it works on Linux 🙂