LVGL Live Preview
Live preview for LVGL C/C++ code with automatic hot-reload in VS Code

Overview
LVGL Live Preview is a Visual Studio Code extension that provides real-time preview of LVGL (Light and Versatile Graphics Library) UIs. It compiles your LVGL code using Emscripten to WebAssembly and displays the result in a live webview panel with automatic reloading on file changes.
The LVGL UI itself is written against LVGL's C API, but your entry point ("glue") file may be C or C++ — a common layout for firmware where a C++ application layer initializes a C UI. See C++ Projects below.
Features
- 🚀 Live Preview: See your LVGL UI in real-time as you code
- 🔄 Hot Reload: Automatic recompilation and refresh on file save with full WASM module reloading
- 🎨 Interactive: Full mouse/touch input support
- 🛠️ In-Preview Settings: Adjust display size, LVGL version, optimization, and more from a panel in the preview window — edits apply when you click Save (Reset Cache / Reset to Defaults apply immediately)
- 🔀 Orientation Toggle: A Rotate button swaps display width/height (portrait ⇄ landscape) for the current session, without changing your saved settings
- 🔍 Fit to Window: The preview scales to fit the window (up or down) while preserving aspect ratio; can be toggled off in settings
- 📦 Zero Setup: Emscripten SDK is downloaded and installed automatically
- ➕ C and C++: Preview a single file or a multi-file project — a C entry point, or a C++ entry point that drives a C LVGL UI
- 📁 Automatic Dependency Detection: Multi-file projects build with no config — the entry point and its
#include-graph dependencies are found automatically (override via .lvgl-live-preview.json when needed), with incremental compilation and smart caching
- 🔧 Custom Defines: Add global preprocessor defines to your project
- 🔍 Error Reporting: Inline diagnostics for compilation errors
- 📝 Runtime Logs in VS Code: Your app's
printf and LV_LOG_* output is routed to a dedicated "LVGL Runtime" output channel
Installation
From VS Code Marketplace
- Open VS Code
- Go to Extensions (Ctrl+Shift+X)
- Search for "LVGL Live Preview"
- Click Install
From Source
git clone https://github.com/TheMasterCoder007/lvgl-live-preview.git
cd lvgl-live-preview
npm install
npm run compile
Then press F5 to run the extension in development mode.
Quick Start
New here? Open the Get Started with LVGL Live Preview walkthrough from the VS Code Welcome page (or run Welcome: Open Walkthrough) for a guided setup. It also opens automatically the first time the extension activates.
- Create a new C (or C++) file with LVGL code
- Define a
lvgl_live_preview_init() function wrapped in #ifdef LVGL_LIVE_PREVIEW that calls your own UI initialization (required entry point; in a C++ file declare it extern "C")
- Press
Ctrl+Shift+L or run "LVGL: Start Live Preview" from the command palette
- Wait for the Emscripten toolchain to install (first time only; downloads and installs ~1–2 GB and requires Python 3 on your PATH). The very first preview also downloads the SDL2 port.
- Your LVGL UI will appear in a webview panel!
Example Code
#include "lvgl.h"
// initializes your UI (entry point of your application)
static void ui_init(void) {
// Create a simple button
lv_obj_t *btn = lv_btn_create(lv_scr_act());
lv_obj_set_size(btn, 120, 50);
lv_obj_center(btn);
lv_obj_t *label = lv_label_create(btn);
lv_label_set_text(label, "Hello LVGL!");
lv_obj_center(label);
}
#ifdef LVGL_LIVE_PREVIEW
// gives the live preview tool a way to initialize your UI
void lvgl_live_preview_init(void) {
ui_init();
}
#endif
C++ Projects
The extension also previews C++ entry points. This suits the common firmware layout where the LVGL UI is written in C (because that is how LVGL is designed) but the glue layer that initializes it lives in a .cpp file.
Two things to know:
- The entry point must be
extern "C". The generated harness calls lvgl_live_preview_init by its C name, so declaring it extern "C" prevents C++ name mangling from hiding it at link time.
- Mixed C and C++ builds just work. Emscripten selects each file's language by extension (
.c → C, .cpp/.cc/.cxx/.c++ → C++) and links the C++ standard library on demand, so your C++ entry point and C UI compile together with no extra configuration.
Run LVGL: Open C++ Sample File from the Command Palette for a ready-to-run example, or use this layout:
#include "lvgl.h"
// C++ application/glue layer. The UI is still built with LVGL's C API.
class App {
public:
void buildUi() {
lv_obj_t *btn = lv_btn_create(lv_scr_act());
lv_obj_set_size(btn, 140, 50);
lv_obj_center(btn);
lv_obj_t *label = lv_label_create(btn);
lv_label_set_text(label, "Hello from C++!");
lv_obj_center(label);
}
};
#ifdef LVGL_LIVE_PREVIEW
// MUST be extern "C" so the name isn't mangled and the harness can link it.
extern "C" void lvgl_live_preview_init(void) {
static App app;
app.buildUi();
}
#endif
For a mixed project (C++ entry point and C UI files) this works with no configuration: the extension finds the file that defines lvgl_live_preview_init() and pulls in its .c/.cpp dependencies by following the #include graph (see Usage Modes).
Requirements
- Python 3: Must be installed and on your system PATH. It is required both to install the Emscripten SDK and to run
emcc (which is a Python program), so it is needed for every build, not just setup. Download Python
- Disk space: The Emscripten toolchain installs to the extension's storage and needs ~1–2 GB free. It is pinned to a specific, tested version for reproducible builds.
- Setup diagnostics: Run LVGL: Check Setup from the Command Palette to verify Python, Emscripten, disk space, and network connectivity.
- Required Entry Point: Your main file must define a
void lvgl_live_preview_init(void) function wrapped in #ifdef LVGL_LIVE_PREVIEW. Call your application's own UI initialization from it (see the example above) so the same code drives both the preview and your firmware. The LVGL_LIVE_PREVIEW define is automatically provided by the extension during compilation, ensuring the function is only visible when using the live preview feature. In a C++ entry-point file, declare it extern "C" (see C++ Projects).
- LVGL API: Use standard LVGL API calls. The extension supports LVGL v8.x and v9.x.
Usage Modes
Automatic Detection (default)
No configuration file is required. When you start the preview, the extension:
- Finds the main file — the C/C++ source that defines
void lvgl_live_preview_init(void). If the file you started the preview from defines it, that file is used; otherwise the workspace is scanned for the single source that does.
- Resolves dependencies — it follows the local
#include "…" graph from the main file. For each quoted header it resolves, the header's directory is added as an include path, and a sibling source of the same basename (foo.h → foo.c/foo.cpp) is compiled as a dependency. Angle-bracket includes (<lvgl.h>, the standard library, SDL) are provided by the toolchain and ignored.
This covers most projects — a C++ entry point calling into C UI files "just works". Detected dependencies are cached and watched for hot reload exactly like the explicit modes below.
Add a .lvgl-live-preview.json (next section) only when you need something detection can't infer: preprocessor defines, include directories that aren't reachable through the include graph, or a dependency whose source basename differs from its header. When present, the config file takes precedence over automatic detection.
Multi-File Mode (Project Configuration)
To override automatic detection, create a .lvgl-live-preview.json file at your project root:
{
"mainFile": "myLvglApp.c",
"dependencies": [
"helpers.c",
"utils.c",
"drivers/display.c"
],
"includePaths": [
"./include",
"../common/headers"
],
"defines": [
"MY_CUSTOM_DEFINE",
"DEBUG_MODE=1"
]
}
Configuration Options:
mainFile (required): Path to the main source file (.c or .cpp) containing lvgl_live_preview_init(). Paths are relative to the config file location.
dependencies (optional): Array of source files (.c and/or .cpp) to compile with the main file. These are compiled to .o files and cached.
includePaths (optional): Array of include directory paths for header files. Paths are relative to the config file location.
defines (optional): Array of preprocessor defines to add during compilation.
Features:
- Incremental Compilation: Dependency files are cached as
.o files and only recompiled when changed
- File Watching: All source files (main + dependencies) are watched for changes
- Hot Reload: Any change to any source file triggers recompilation with smart caching
Configuration
All settings are managed from the preview window — they are stored by the extension itself and do not appear in the VS Code Settings UI. Click the gear button in the top-right corner of the preview to open the settings panel. Edit any value and click Save to apply.
- Changes are only applied when you click Save. Closing the panel (via Cancel, the ✕,
Esc, or clicking outside it) discards your edits and leaves the current settings untouched.
- The preview rebuilds and reloads only when saved — and only once per Save, no matter how many fields you edited.
- Settings persist across sessions. If you used an earlier version that stored these under
lvglPreview.* in VS Code settings, your values are migrated automatically the first time you open the panel.
| Setting |
Default |
Description |
| LVGL Version |
9.4.0 |
LVGL library version to use |
| Display Width |
480 |
Display width in pixels |
| Display Height |
320 |
Display height in pixels |
| Scale preview to fit the window |
true |
Scale the preview to fit the window while preserving aspect ratio (scales up or down) |
| Emscripten Optimization |
-O1 |
Emscripten optimization level (-O0, -O1, -O2, -O3, -Os, -Oz) |
| Auto Reload |
true |
Automatically reload preview on file changes |
| Debounce Delay |
100 |
Delay in ms before recompiling after file changes |
| LVGL Memory Size |
256 |
LVGL internal heap memory size in KB (64, 128, 256, 512, 1024, 2048) |
| WASM Memory Size |
128 |
WebAssembly total memory size in MB (64, 128, 256, 512, 1024) |
Commands
| Command |
Shortcut |
Description |
LVGL: Start Live Preview |
Ctrl+Shift+L |
Start preview for the current C/C++ file |
LVGL: Stop Preview |
- |
Stop the preview and file watcher |
LVGL: Force Rebuild |
- |
Force full rebuild including LVGL library |
LVGL: Clear Cache |
- |
Clear compiled cache |
LVGL: Open Sample File |
- |
Open a ready-to-run C sample |
LVGL: Open C++ Sample File |
- |
Open a ready-to-run C++ sample (C++ glue driving a C UI) |
LVGL: Check Setup |
- |
Diagnose the environment (Python, Emscripten, disk space, network) |
LVGL: Install Emscripten Toolchain |
- |
Install the bundled Emscripten toolchain |
LVGL: Reinstall Emscripten Toolchain |
- |
Delete and reinstall the Emscripten toolchain |
LVGL: Open Get Started Walkthrough |
- |
Reopen the Get Started walkthrough |
Logging
Anything your code prints — via printf, LV_LOG_USER, or the other LV_LOG_* macros — is routed to the LVGL Runtime output channel in VS Code (View → Output, then select "LVGL Runtime" from the dropdown). The channel opens automatically the first time your app logs something in a preview session.
LV_LOG_USER("Button clicked, value = %d", value);
printf("Hello from LVGL\n");
Logging uses LVGL's built-in printf log target, so no extra setup is required. Note that the default log level is LV_LOG_LEVEL_WARN, so LV_LOG_INFO/LV_LOG_TRACE messages are suppressed while LV_LOG_USER, LV_LOG_WARN, and LV_LOG_ERROR are shown.
How It Works
- Emscripten Setup: Downloads and installs Emscripten SDK on first use
- LVGL Download: Downloads a specified LVGL version from GitHub
- Library Compilation: Compiles LVGL library to a static library (cached per version/settings)
- User Code Compilation: Compiles your C/C++ code with LVGL (extension selects each file's language)
- WASM Generation: Links everything into WebAssembly + JS glue code
- Preview: Displays in webview with SDL2 canvas rendering
- Hot Reload: Watches file changes, recompiles, and fully reloads the WASM module through webview recreation
Technical Details: Hot Reload Architecture
The extension uses webview recreation to ensure reliable WASM module reloading:
- Initial load creates a webview panel with the WASM module
- On file changes, the entire webview is disposed and recreated
- Each reload gets a completely fresh JavaScript execution context
- This prevents runtime state conflicts and memory leaks from Emscripten's persistent globals
- The recreation is fast (~100ms) and provides a clean slate for each reload
This approach solves the common problem of Emscripten modules failing to reload due to persistent global state by letting VS Code handle the cleanup automatically.
Troubleshooting
Python Not Found Error
If you see "Python is required to install Emscripten SDK":
- Install Python from python.org/downloads
- Ensure Python is added to your system PATH during installation
- Restart VS Code after installing Python
- Verify installation by running
python --version or python3 --version in a terminal
Emscripten Download Fails
- Check your internet connection
- Try clearing the cache: Run "LVGL: Clear Cache"
- Check the LVGL Preview output channel for details
Compilation Errors
- Ensure you have defined
lvgl_live_preview_init() function wrapped in #ifdef LVGL_LIVE_PREVIEW
- In a C++ entry point, declare it
extern "C" — otherwise the linker reports lvgl_live_preview_init as undefined
- Check the Problems panel (Ctrl+Shift+M) for detailed errors
- View the LVGL Preview output channel for compiler messages
Preview Not Updating
- Check that Auto Reload is enabled in settings
- Manually save the file (Ctrl+S)
- Try "LVGL: Force Rebuild" command
- Lower the optimization level to
-O0 for faster compilation
- Reduce display dimensions in settings
- Close other resource-intensive applications
Roadmap
- [x] Multi-file project support
- [x] Dependency caching and incremental compilation
- [x] Custom preprocessor defines
- [x] C++ entry point support
- [ ] Custom
lv_conf.h editor
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature)
- Commit your changes (
git commit -m 'Add amazing feature')
- Push to the branch (
git push origin feature/amazing-feature)
- Open a Pull Request
License
This project is licensed under the GNU General Public License - see the LICENSE file for details.
Acknowledgments
- LVGL - The awesome graphics library
- Emscripten - The WebAssembly compiler toolchain
- VS Code Extension API
Support
Made with ❤️ for the LVGL community