Qt for WebAssembly▲
Qt for Webassembly lets you to run Qt applications on the web.
WebAssembly (abbreviated Wasm) is a binary instruction format intended to be executed in a virtual machine, for example in a web browser.
With Qt for WebAssembly, you can distribute your application as a web application that runs in a browser sandbox. This approach is suitable for web distributed applications that do not require full access to host device capabilities.
Qt for WebAssembly is a supported platform, but some modules are not yet supported or are in Tech Preview. See Supported Qt Modules.
Getting Started with Qt for WebAssembly▲
Building Qt applications for WebAssembly is similar to building Qt for other platforms. You need to install an SDK (Emscripten), install Qt (or build Qt from source), and finally, build the application. Some differences exist, for example, Qt for WebAssembly supports fewer modules and less features than other Qt builds.
Installing Emscripten▲
Emscripten is a toolchain for compiling to WebAssembly. It lets you run Qt on the web at near-native speed without browser plugins.
Refer to the Emscripten documentation for more information about installing the Emscripten SDK.
After installation, you should have the Emscripten compiler in your path. Check this with the following command:
em++
--
version
Each minor version of Qt targets a specific Emcsripten version, which remains unchanged in patch releases. Qt's binary packages are built using the target Emscripten version. Applications should use the same version since Emscripten does not guarantee ABI compatibility between versions.
The Emcsripten versions are:
-
Qt 6.2: 2.0.14
-
Qt 6.3: 3.0.0
-
Qt 6.4: 3.1.14
-
Qt 6.5: 3.1.25
-
Qt 6.6: 3.1.37
Use emsdk to install specific Emscripten versions. For example, to install it for Qt 6.6 enter:
-
./emsdk install 3.1.37
-
./emsdk activate 3.1.37
On Windows, Emscripten is in your path after installation. On macOS or Linux you need to add it to your path, like this:
source /
path/
to/
emsdk/
emsdk_env.sh
Check this with the following command:
em++
--
version
You can build Qt from source if you require more flexibility when selecting the Emcsripten version. In this case the versions above are minimum versions. Later versions are expected to work but may introduce behavior changes which require making changes to Qt.
Installing Qt▲
Download Qt from the Downloads section of your Qt account. We provide builds for Linux, macOS, and Windows as development platforms.
The binary builds are designed to run on as many browsers as possible, and come in single-threaded and multi-threaded versions. Non-standard features such as Wasm SIMD and Wasm exceptions are not supported by the binary builds.
Building Qt from Source▲
Building from source lets you set Qt configuration options such as thread support, OpenGL ES level, or SIMD support. Download the Qt sources from the Downloads section of your Qt account.
Configure Qt as a cross-compile build for the wasm-emscripten platform. This sets the -static, -no-feature-thread, and -no-make examples configure options. You can enable thread support with the -feature-thread, configure option. Shared library builds are not supported.
You need a host build of the same version of Qt and specify that path in the QT_HOST_PATH CMake variable or by using the -qt-host-path configure argument.
Although it should be detected, you may optionally set the CMAKE_TOOLCHAIN_FILE CMake variable to the Emscripten.cmake toolchain file that comes with Emscripten SDK. This can be done by setting the environment variable CMAKE_TOOLCHAIN_FILE or by passing CMAKE_TOOLCHAIN_FILE=/path/to/Emscripten.cmake to configure.
./
configure -
qt-
host-
path /
path/
to/
Qt -
platform wasm-
emscripten -
prefix $PWD/
qtbase
configure always uses the Ninja generator and build tool if a ninja executable is available. Ninja is cross-platform, feature-rich, performant, and recommended on all platforms. The use of other generators might work but is not officially supported.
On Windows, make sure you have MinGW in your PATH and configure with the following:
configure -
qt-
host-
path C:\Path\to\Qt -
no-
warnings-
are-
errors -
platform wasm-
emscripten -
prefix %
CD%
\qtbase
Then build the required modules:
cmake --
build . -
t qtbase -
t qtdeclarative [-
t another_module]
Building Applications on the Command Line▲
Qt for WebAssembly supports building applications using qmake and make, or CMake with ninja or make.
$ /
path/
to/
qt-
wasm/
qtbase/
bin/
qt-
cmake .
$ cmake --
build .
Building the application generates several output files, including a .wasm file that contains the application and Qt code (statically linked), a .html file that can be opened in the browser to run the application.
Emscripten produces relatively large .wasm files at the "-g" debug level. Consider linking with "-g2" for debug builds.
Running Applications▲
Running the application requires a web server. The build output files are all static content, so any web server will do. Some use cases might require special server configuration, such as providing https certificates or setting http headers required to enable multithreading support.
Emrun▲
Emscripten provides the emrun utility for test-running applications. Emrun starts a web server, launches a browser, and will also capture and forward stdout/stderr (which will normally go to the JavaScript console).
/
path/
to/
emscripten/
emrun --
browser=
firefox appname.html
Python http.server▲
Another option is to start a development web server and then launch the web browser separately. One of the simplest options is http.server from Python:
python -
m http.server
Note that this is only a simple webserver and does not support SharedArrayBuffer required for threading, as the required COOP and COED headers mentioned below are not sent.
qtwasmserver▲
Qt provides a developer web server which uses mkcert to generate https certificates. This allows testing web features which require a secure context. Note that delivery over http://localhost is also considered secure, without requiring a certificate.
The web server also sets the COOP and COEP headers to values which enables support for SharedArrayBuffer and multi-threading.
The qtwasmserver script starts one server which binds to localhost by default. You may add additional addresses using the -a command-line argument, or use --all to bind to all available addresses.
python /
path/
to/
qtbase/
util/
wasm/
qtwasmserver/
qtwasmserver.py --
all
Building Applications using Qt Creator▲
Deploying Applications on the web▲
Building an application generates several files (substitute "app" with the application name in the following table).
Generated file |
Brief Description |
---|---|
app.html |
HTML container |
qtloader.js |
JavaScript API for loading Qt applications |
app.js |
JavaScript runtime generated by Emscripten |
app.wasm |
app binary |
You can deploy app.html as-is, or discard it in favor of a custom HTML file. Smaller adjustments, such as changing the splash screen image from the Qt logo to the app logo, is also possible. In both cases, qtloader.js provides a JavaScript API for loading the application.
Compress the Wasm file using either gzip or brotli before deploying, as they offer better compression ratio than the other tools. See Minimizing the size of binaries for more information.
Enabling certain features, such as multi-threading and SIMD, produces .wasm binaries that are incompatible with browsers that do not support the enabled feature. It is possible to work around this limitation by building multiple .wasm files and then use JavaScript feature detection to select the correct one, but note that Qt does not provide any functionality for doing this.
Using qtloader▲
Qt provides a JavaScript API for downloading, compiling, and instantiating Qt for WebAssembly applications. This loading API wraps loading functionality provided by Emscripten, and provides additional features useful for Qt-based applications. It is implemented in the qtloader.js file. A copy of this file is written to the build directoty at build time.
Typical usage looks like the following:
const
app_container_element =
...;
const
instance =
await qtLoad({
qt
:
{
containerElements
:
[ app_container_element ],
onLoaded
:
() =&
gt; {
/* handle application load completed */
}
,
onExit
:
() =&
gt; {
/* handle application exit */
}
,
}
}
);
The code calls the qtLoad() loader function with a configuration object. This configuration object can contain any emscripten configuration options, as well as a special "qt" configuration object. The qt configuration object supports the following properties:
Property |
Brief Description |
---|---|
containerElements |
Array of HTML container elements. The application sees these as QScreens. |
onLoaded |
Callback for when the application has completed loading. |
onExit |
Callback for when the applicataion exits. |
The containerElements array is the main interface between Qt and the web page, where the html elements in this array (typically <div> elements) specify the location of the application content on the web page.
The application sees each container element as a QScreen instance, and can place application windows on the screen instances as usual. Windows with the Qt::WindowFullScreen state set use the entire screen area, while non-"fullscreen" windows get window decorations.
The qtLoad() function returns a promise, which yelds an Emscripten instance when awaited. The instance provides access to Embind exported functions. Qt exports several such functions, and these functions make up the instance API.
Using the Qt instance API▲
Qt provides several instance functions. Currently, these support adding and removing container elements at runtime.
Property |
Brief Description |
---|---|
qtAddContainerElement |
Add a container element. Adding an element will add a new QScreen. |
qtRemoveContainerElement |
Remove a container element, and its corresponding screen. |
qtSetContainerElements |
Sets all container elements |
qtResizeContainerElement |
Make Qt pick up changes to container element size. |
Porting to the Qt 6.6 qtloader▲
Qt 6.6 includes a new qtloader with a simplified implementation and a smaller scope. This includes API changes which may require porting application JavaScript code. Qt provides a compatibility API to ease the transition. Depending use case there are several ways forward:
-
If you are using the generated app.html file directly then this file will be updated as well at build time. No action is needed.
-
If you are using the basic qtloader feature set then you may use the compatibility API included in Qt 6.6 as a temporary maesure. This API will be removed in a future release; you should plan on updating to use the new qtloader. Porting step 1 below is needed.
-
If you are using advanced features (such as adding container elements at runtime), then porting to the new loader or instance API required. Porting steps 1 and 2 below are needed.
Porting steps
-
Include the app.js (JavaScript runtime generated by Emscripten) from the loading html file.
Sélectionnez&
lt;script src=
"app.js"
&
gt;&
lt;/
script&
gt;Before Qt 6.6, qtloader would load and evaluate this JavaScript file. This is no longer done, and the file must be included using a <script> tag.
-
Port to using the new JavaScript and instance API.
See documentation sections above.
Supported Browsers▲
Desktop▲
Qt for WebAssembly is developed and tested on the following browsers:
-
Chrome
-
Firefox
-
Safari
-
Edge
Qt should run if the browser supports WebAssembly. Qt has a fixed WebGL requirement, even if the application itself does not use hardware accelerated graphics. Browsers that support WebAssembly often support WebGL, though some browsers blacklist older or unsupported GPUs. s/qtloader.js provides APIs to check if WebGL is available.
Qt does not make direct use of operating system features and it makes no difference if, for example, FireFox runs on Windows or macOS. Qt does use some operating system adaptations, for example for ctrl/cmd key handling on macOS.
Mobile▲
Supported Qt Modules▲
Qt for WebAssembly supports a subset of the Qt modules and features. Tested modules are listed below, other modules may or may not work.
In all cases, module support may not be complete and there may be additional limitations, either due to the browser sandbox or due to incompleteness of the Qt platform port. See Developing with Qt for WebAssembly for further info.
Qt for WebAssembly Technology Preview modules and features. These features may require to reconfigure and build Qt. They may contain features that are still experimental in the browsers or Emscripten.
Developing with Qt for WebAssembly▲
Building with CMake▲
If there is a need for Emscripten-specific configuration in CMake, the following code can be utilized:
if
(EMSCRIPTEN)
# WebAssembly specific code
else
()
# other platforms
endif()
This code allows for the accommodation of Emscripten-specific configurations while ensuring compatibility with other platforms.
OpenGL and WebGL▲
Qt requires WebGL, also for applications which do not use OpenGL directly. All relevant browsers support WebGL, but note that some browsers blacklist certain older GPUs. The Qt loader will detect this and display an error message.
Qt detects WebGL as OpenGL ES, with the following version mapping:
OpenGL |
WebGL |
---|---|
OpengL ES 2 |
WebGL 1 |
OpengL ES 3 |
WebGL 2 |
OpenGL ES 2 and OpenGL ES 3 are enabled by default, and can be selected through the QSurfaceFormat::setMajorVersion() function.
Web and Desktop OpenGL differences are documented in WebGL and OpenGL Differences. There are additional differences between WebGL 1.0 and WebGL 2.0, documented in the WebGL 2.0 Specification.
A WebGL-friendly subset of ES2 (and ES3) is used by default. If you need to use glDrawArrays and glDrawElements without bound buffers, you can enable full ES2 support by adding
target_link_options(&
lt;your target&
gt; PRIVATE -
s FULL_ES2=
1
)
and/or full ES3 emulation by adding
target_link_options(&
lt;your target&
gt; PRIVATE -
s FULL_ES3=
1
)
to your project's CMakeLists.txt.
Multithreading▲
Qt for WebAssembly supports multithreading using Emscripten's Pthreads support, where each thread is backed by a web worker. Enable multithreading by installing the "WebAssembly (multi-threaded)" component from Qt Maintenance Tool, or by building Qt from source and passing the "-feature-thread" flag to configure.
Existing threading code can generally be reused, but may need to be modified to work around specifics of the pthread implementation. Some Emscripten and Qt features are not supported, this includes the thread proxying feature and the Qt Quick threaded render loop.
Be aware that it is especially important to not block the main thread on Qt for WebAssembly, since the main thread might be required to service requests from secondary threads. For example, all timers in Qt are scheduled on the main thread, and will not fire if the main thread is blocked. Another example is that creating a new web worker (for a thread) can only be done from the main thread.
Emscripten provides some mitigations for this. Short-term waits such as acquiring a mutex lock is supported by busy-waiting and processing events while waiting for the lock. Longer waits on the main thread should be avoided. In particular, the common practice of calling QThread::wait() or pthread_join() to wait for a secondary thread will not work, unless the application can guarantee that the thread (and web worker) has already been started, and will be able to complete without assistance from the main thread at the time that the wait() or join() call is made.
The multithreading feature requires browser support for the SharedArrayBuffer API. (Normally, Emscripten stores the heap in an ArrayBuffer object. For multithreading, the heap must be shared with web workers and a SharedArrayBuffer is needed) This API is generally available in all modern browsers, but may be disabled if certain security requirements are not met. WebAssembly binaries with thread support enabled will then fail to run, also if the binary does not actually start a thread.
Enabling SharedArrayBuffer requires a secure browsing context (where the page is served over https:// or http://localhost), and that the page is in cross-origin isolated mode. The latter can be done by setting the so called COOP and COEP headers on the web server:
-
Cross-Origin-Opener-Policy: same-origin
-
Cross-Origin-Embedder-Policy: require-corp
SIMD▲
Emscripten supports WebAssembly SIMD, which provides 128-bit SIMD types and operations for WebAssembly.
Build Qt from source and configure with the -feature-wasm-simd128 flag to enable; this will pass the -msimd128 flag at compile and link time. Note that Qt does not contain wasm-simd optimized code paths at this point, however enabling wasm-simd will enable compiler auto-vectorization where the compiler can use the SIMD instructions.
You can target WebAssembly SIMD directly using either GCC/Clang SIMD Vector Extensions or WASM SIMD128 intrinsics. For more information, see the Emscripten SIMD documentation.
In addition, Emscripten supports emulating/translating x86 SSE instructions to Wasm SIMD instructions. Qt does not use this emulation, as the use of SSE SIMD instructions that have no native Wasm SIMD equivalent may cause reduced performance.
Note that SIMD-enabled binaries are incompatible with browsers that do not support WebAssembly SIMD, also if the SIMD code paths are not called at run-time. SIMD support may need to be enabled in the browsers advanced configurations, such as 'about:config' or 'chrome:flags'
Networking▲
Qt provides limited support for networking. In general, network protocols which are already in use on the web can be use also from Qt, while others are not directly available due to the web sandbox.
The following protocols are supported:
-
QNetworkAccessManager http requests to the web page origin server, or to a server which supports CORS. This includes XMLHttpRequest from QML.
-
QWebSocket connections to any host. Note that web pages served over the secure https protocol allows websockets connections over secure wss protocol only.
-
Emulated POSIX TCP Sockets over WebSockets, using functionality provided by Emscripten. Note that this requires running a forwarding server which handles socket translation.
All other network protocols are not supported.
Local File Access▲
File system access is sandboxed on the web, and this has implications for how the application works with files. The Web platform provides APIs for accessing the local file system in a way which is under user control, as well as APIs for accessing persistent storage. Emscripten and Qt wraps these features and provides APIs which are easier to use from C++ and Qt-based applications.
The web platform provides features for accessing local files and persistent storage:
-
<input type="file"> for showing a native open-file dialog where the user can pick a file.
-
IndexedDB provides persistent local storage (not accessible outside the browser)
Emscripten provides several file systems with a POSIX like API. These include:
-
the MEMFS ephemeral file system which stores files in-memory
-
the IDBFS persistent file system which stores files using IndexedDB
Emscripten mounts a temporary MEMFS filesystem to "/" at app startup. This means that QFile can be used, and will read and write files to memory by default. Qt provides other API as well:
-
QSettings has an IndexedDB-based backend; Note that QSettings is asynchronous on WebAssembly.
-
QFileDialog::getOpenFileContent() opens a native file dialog where the user can pick a file
-
QFileDialog::saveFileContent() saves a file to the local file system via file download}
Clipboard Access▲
Qt supports copying and pasting text to the system clipboard, with some differences d