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.

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.

For Qt 6, you will need a host build of the same version of Qt and add that path to QT_HOST_PATH by using the -qt-host-path configure argument.

Although it should be detected, you may optionally set the CMAKE_TOOLCHAIN_PATH to the Emscripten.cmake toolchain file that comes with Emscripten sdk, by using the -DCMAKE_TOOLCHAIN_FILE= configure argument.

./configure -qt-host-path /path/to/Qt/6.2.0/platform -xplatform 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\Qt6 -no-warnings-are-errors -xplatform wasm-emscripten -platform win32-g++ -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 fileBrief Description
app.htmlHTML container
qtloader.jsJS API for loading Qt apps
app.jsJS API for loading Qt apps
app.wasmemscripten 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 %.

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.

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:

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.

Footprint and file size

Expected footprint (download size): Wasm modules as produced by the compiler can be large, but compress well:

Examplegzipbrotli
helloglwindow (QtCore + QtGui)2.8M2.1M
wiggly widget (QtCore + QtGui + QtWidgets)4.3M3.2M
SensorTag (QtCore + QtGui + QtWidgets + QtQuick + QtCharts)8.6M6.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

External resources

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.

© 2021 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.