Qt for WebAssembly
WebAssembly (or Wasm) is a binary bytecode format intended to be executed in a virtual machine inside a web browser. This allows an application to be deployed to a device with a compliant web browser without going through any installation steps. The application will run inside a secure sandbox in the web browser. This format is nearly as fast as native machine code, and is now supported by all major web browsers. This makes it appropriate for applications that do not need full access to the device capabilities, but benefits from a swift and uncomplicated installation process.
Qt for WebAssembly is a platform plugin that lets you build Qt applications, which can be integrated into your web pages. It doesn't require any client-side installations and reduces the server-side resource usage.
Note: Qt for WebAssembly is currently in Tech Preview.
Getting Started with Qt for WebAssembly
Install Emscripten
emscripten is a toolchain for compiling to WebAssembly. It lets you run Qt on the web at near-native speed without 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 Qt minor version targets a specific minimum Emcsripten version, which will not change for the lifetime of that Qt minor version. Qt's binary packages are built using this version of the Emscripten SDK. We recommend installing the minimum Emcsripten version corresponding to the Qt version you are using, especially if you are using the binary pckages.
Newer versions of Emscripten above the minimum version may work (and often do), but may introduce behavior changes which require changes to Qt.
The minimum versions are:
- Qt 6.2.0: 2.0.14
Use emsdk
to install specific emscripten
versions. For example, to install it for Qt 6.2 enter:
- ./emsdk install 2.0.14
- ./emsdk activate 2.0.14
After installation, on Windows, you should have emscripten in your path. 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
Download the binaries
The binary builds can be downloaded in the Downloads section using your Qt account.
Build Qt from the sources
Alternatively, you could download the Qt sources in the Downloads section, and build Qt from it.
Configure Qt as a cross-compile build for the wasm-emscripten
platform. This will implicitly set the -static
, -no-feature-thread
, and -no-make examples
configure options. Thread support and building of the examples can be enabled by removing the corresponding disabling option. Shared library builds are not supported at this time.
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
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
Build required modules:
cmake --build . -t qtbase -t qtdeclarative [-t another_module]
Build and run your application
$ /path/to/qt-wasm/qtbase/bin/qt-cmake $ make
This generates the following files:
Generated file | Brief Description |
---|---|
app.html | HTML container |
qtloader.js | JS API for loading Qt apps |
app.js | JS API for loading Qt apps |
app.wasm | emscripten app binary |
When deploying the app, the compression is typically handled on the server side. We recommend to compress the wasm binaries because this typically reduces the size of the binary by 50 %.
Additional configuration
Qt for Webassembly has several additional configuration arguments available.
Configure Argument | Brief Description |
---|---|
-sse2 | This enables autovectorization and Wasm SIMD support by adding the compiler argument -msimd128. In addition, emulated and native optcode SSE instructions are utilized (at which point Qt's SSE code paths will also be used). SSE1, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, and 128-bit AVX instructions only. Simd support may need to be enabled in browsers advanced configurations, such as 'about:config' or 'chrome:flags'. There may be performance issues when used with certain SIMD instructions that are emulated and that have no native optcode supported by the browser. For more information see https://emscripten.org/docs/porting/simd.html |
-feature-thread | Multi-threaded wasm |
-device-option QT_WASM_SOURCE_MAP=1 | Debugging option for creating source maps |
-feature-opengles3 | Use opengles3 in addition to the default opengles2 |
Test-run your application
You can test-run your application in the following way:
/path/to/emscripten/emrun --browser=firefox appname.html
Supported target browsers and devices
Desktop
- Chrome
- Firefox
- Safari
- Edge(Chrome)
If the browser supports WebAssembly, then Qt should run.
Note: Qt has a fixed WebGL requirement, also for apps that do not use WebGL directly. Browsers often blacklist WebGL for older/unsupported GPUs.
Mobile
- Android Chrome Browser
- iPhone / iPad Mobile Safari
Note: There is currently no support for text input using the virtual keyboard. Safari currently does not support wasm modules of the size Qt produces.
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.
Supported Qt Modules
Qt for WebAssembly supports a subset of the Qt modules and features. The list below lists the currently tested modules.
- QtBase (see section for limitations below)
- QtShaderTools
- QtDeclarative (see section for limitations below)
- QtQuickControls2
- QtWebSockets
- QQtSvg
- QtMqtt
Other modules are untested and may or may not work.
Note that changes and updates to browsers may also change Wasm platform behavior, thus this documentation and listed limitations below may not be up-to-date.
- Web browsers get regular updates and they are configurable and extensible, sometimes with surprising side effects for WebAssembly-based applications:
- JavaScript blockers may block JavaScript without enabling NoScript. This means that <noscript> content is not displayed, and that the application appears to be stuck at the loading screen.
- Some ad-blockers block all .wasm files from specific hosts, such as github.com
- privacy.resistFingerprinting=true (FireFox) disables high-dpi support - the browser will appear to be running on a standard-dpi display.
QtBase limitations and not supported features
QtBase on OpenGL Desktop, Vulkan or Metal
- WebGL is required, even for applications which do not use OpenGL themselves. 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.
- Mixing OpenGL and raster content is not supported
- Child OpenGL windows are not supported. The window compositor (in the Qt for Wasm platform plugin) supports raster windows only.
- QOpenGLWidget is not supported: QTBUG-66944
- Qt will detect OpenGL support as OpenGL ES. In reality the browser will be providing WebGL. WebGL 1.0 is based on OpenGL ES 2, and WebGL 2.0 based on OpenGL ES 3 are very similar, but there are some incompatibilities. See WebGL and OpenGL Differences There are additional differences between WebGL 1.0 and WebGL 2.0 which are documented in: WebGL 2.0 Specification
Multithreading with QThread, QConcurrent, QFuture as part of QtBase
Threads on Qt for WebAssembly are not officially supported yet and may not work exactly like posix threads do. Refer to Pthreads Support.
Qt on WebAssembly can run multithreads, however support is disabled by default in order to be compatible with as many browsers as possible. Thread support can be enabled by building Qt from source and using the "-feature-thread" configure flag.
The Qt for WebAssembly binary release packages do not support multithreading.
The minimum Emscripten SDK version is 1.38.30. The Emscripten pthreads documentation contains relevant documentation for multithreading.
Threads are supported by some (but not all) browsers. Configuration changes may be required. The [1] demo can be used to determine if thread support is available. Note that it is the build mode which determines if browser thread support is required, not whether the application starts a thread or not.
In Firefox versions before 79, open about:config and make sure the following option is enabled:
- javascript.options.shared_memory = true
Thread support will only be enabled provided that that the web server sets two additional headers:
- Cross-Origin-Opener-Policy: same-origin
- Cross-Origin-Embedder-Policy: require-corp
(These are the COOP and COEP headers, respectively)
Mozilla bug 1619649 tracks the Firefox defaults change. In the mean time, its possible to manually bypass or enable the header check:
Firefox Nightly or Beta - bypass header check:
- dom.postMessage.sharedArrayBuffer.bypassCOOP_COEP.insecure.enabled = true
Firefox Release - enable header check:
- browser.tabs.remote.useCrossOriginEmbedderPolicy = true
- browser.tabs.remote.useCrossOriginOpenerPolicy = true
After enabling the header check, make sure your web server sets the required headers. See QTBUG-79087 for an example python-based development server.
https://bugreports.qt.io/browse/QTBUG-79087
Application developers may need to add two settings to its .pro or CMakeFiles.txt file when enabling threads:
- Thread Pool Size: Applications should set the expected number of concurrent threads at build time. This can be done by setting QT_WASM_PTHREAD_POOL_SIZE in the .pro or CMakeFIles.txt file (maps to Emscripten PTHREAD_POOL_SIZE). Applications can exceed PTHREAD_POOL_SIZE, provided they return main thread control to the browser before waiting on the new thread, for example by returning from the event handler that started the new thread. This allows the browser to start another web worker. Immediately waiting for the new thread on the main thread (using QThread::wait() or similar) will deadlock. Qt sets PTHREAD_POOL_SIZE to 4 by default.
- Heap Memory Size: Applications should set the heap memory size at build time, since growing the heap is not supported with pthreads enabled. This can be be done by setting QT_WASM_INITIAL_MEMORY in the CMakeFiles.txt file (maps to Emscripten INITIAL_MEMORY). Browsers typically limit the initial WASM memory allocation size to 1GB. Qt sets INITIAL_MEMORY to 1GB by default (for -feature-thread enabled builds)
Using cmake, such as:
set(QT_WASM_PTHREAD_POOL_SIZE, 10)
Limited Network access due to web sandbox
The web sandbox limits network access to a subset of what is available for native apps.
- QNetworkAccessManager http requests to the web page origin server, or to a server which supports CORS.
- QWebSocket connections to any host.
- TCP and UDP socked tunneling using over WebSockets using a websockify server [3].
- Websockify v0.8.0 can be used to tunnel TCP connections with QT5.12 but it is MANDATORY to specify the base64 or binary subprotocols before calling QWebSocket::open().
Files and local file system access limited due to the web sandbox
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. See usage example at [2]
- 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 with only text content
Qt supports copying and pasting text to the system clipboard but there are browser specific differences you need to take into account in your code.
- Browsers that support the Clipboard API are preferred. Note that a requirement for this API is that the web page is served over a secure connection (e.g. https).
- Chrome supports the Clipboard API
- Firefox supports the Clipboard API behind a flag: dom.events.asyncClipboard.dataTransfer
- Browsers that will send clipboard events to Qt's canvas element are also supported
- This mode supports the CTRL+x/c/v keyboard shortcuts only
- Ongoing work. Firefox works well, other browsers have some hiccups.
- text only at this time
Other QtBase known limitations
- Nested event loops are not supported. Applications should not call, for example, QDialog::exec() or create a new QEventLoop object.
- Drag and Drop is not supported
- Printing is not supported
- QDnsLookup lookups, QTcpSocket, QSsl do not work and are not supported on Wasm due to the platform sandbox
- Accessibility: Wasm as a platform limits the access to canvas and this cannot be circumvented by Qt. Qt renders application content to a canvas element, and does not use (other) native DOM elements. This means accessibility (screen readers) are not supported and that text inputs won't trigger virtual keyboards.
- Fonts: Wasm sandbox does not allow access to system fonts. Font files must be distributed with the application, for example in Qt resources or downloading. Qt for WebAssembly itself embeds one such font.
- High-DPI and scaling: High-DPI rendering is supported, and so is setting the overall UI visual size using the browser zoom feature. Browser font size (and type) settings have no effect on Qt applications.
QtQuickControls2 known issues and limitations
Known limitations and issues with Qt Quick Controls 2:
- There may be artifacts of uninitialized graphics memory on some Qt Quick Controls 2 components, such as checkboxes. This can sometimes be seen on HighDPi displays.
- Native styles for Windows and macOS are not supported as Wasm as a platform is not providing that capability
Debugging and profiling
Wasm debugging is done on browser javascript console, debugging applications on Wasm directly within Qt Creator is not possible.
- Qt debug and logging output is printed on the JavaScript console, which can be accessed via browser "Developer Tools" or similar.
- Source maps for stepping through code, can be created by reconfiguring Qt with the --device-option QT_WASM_SOURCE_MAP=1, and building a debug build.
- Mobile browsers can use remote debugging
- To stop execution on a certain line and popup the browser debugger programmatically, you can add the function emscripten_debugger(); to the application source code.
- Profiling can be accomplished by using a debug build and the javascript console profiling features. Qt adds --profiling-funcs to the linker arguments in debug builds, which preserve function names in profiling
Footprint and file size
Expected footprint (download size): Wasm modules as produced by the compiler can be large, but compress well:
Example | gzip | brotli |
---|---|---|
helloglwindow (QtCore + QtGui) | 2.8M | 2.1M |
wiggly widget (QtCore + QtGui + QtWidgets) | 4.3M | 3.2M |
SensorTag (QtCore + QtGui + QtWidgets + QtQuick + QtCharts) | 8.6M | 6.3M |
Compression is typically handled on the web server side, using standard compression features: the server compresses automatically or picks up pre-compressed versions of the files. There's generally no need to have special handling of wasm files.
Other known issues, limitations and general notes
- Supported on all development host systems: Linux, MacOS, and Windows (MinGW)
- Link time error such as "wasm-ld: error: initial memory too small", requires adjustment of the initial memory size. Use QT_WASM_TOTAL_MEMORY to set the initial size in kb, which must be a multiple of 64KB (65536). In CMakeFiles.txt: set(QT_WASM_TOTAL_MEMORY, xxxxx);
- To target WebAssembly platform specifically in qmake, use emscripten as the platform name, such as: emscripten { message("Building for WebAssembly") }
- Open known bugs and issues on Wasm can be found on Qt issue tracking JIRA on this link: https://bugreports.qt.io/secure/RapidBoard.jspa?rapidView=258&quickFilter=2352
Some examples
- Industrial Panel Demo
- QMainWindow app
- A gallery of available controls in Qt Quick Controls
- Web app for prdering pizzas
External resources
- Qt for WebAssembly Technology Preview
- Qt and WebAssembly
- Qt for WebAssembly wiki
- Getting Started with Qt for WebAssembly
- Remote UIs with WebGL and WebAssembly
- WebAssembly Resource site
- Emscripten Documentation
Licenses
Qt for WebAssembly is available under commercial licenses from The Qt Company. In addition, it is available under the GNU General Public License, version 3. See Qt Licensing for further details.
See also WebAssembly Resource site, Getting Started with Qt for WebAssembly, and Remote UIs with WebGL and WebAssembly.
© 2024 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.