# 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.

Note: Qt for WebAssembly is in Tech Preview.

## 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 WebAssambly 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.

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 minimum Emcsripten version, which will not change for the lifetime of that minor version. Qt's binary packages are built using this version of the Emscripten SDK. Install the minimum Emcsripten version that corresponds to the Qt version you use, especially if you use the binary packages.

Later versions of Emscripten may work (and often do), but may introduce behavior changes which require changes to Qt.

The minimum versions are:

• Qt 6.2: 2.0.14
• Qt 6.3: 3.0.0
• Qt 6.4: 3.1.10

Use emsdk to install specific emscripten versions. For example, to install it for Qt 6.4 enter:

• ./emsdk install 3.1.10
• ./emsdk activate 3.1.10

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

### Installing Qt

The binary builds are designed to run on as many browsers as possible, and do not enable features such as threads or SIMD support.

### Building Qt from Source

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

Note: 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

#### 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 enalbles 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

### Deploying Applications on the web

Building an application generates several files (substitute "app" with the application name in the following table).

Generated fileBrief Description
app.htmlHTML container
app.wasmapp binary

You can deploy app.html as-is, or discard it in favor 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.

We recommend compressing the wasm file using e.g. gzip or brotli before deployment, as this can provide a significant reductiton in file size.

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.

## 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

Qt for WebAssembly applications runs on mobile browsers such as mobile Safari and Android Chrome.

## 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 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

### 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:

OpenGLWebGL
OpengL ES 2WebGL 1
OpengL ES 3WebGL 2

OpengL ES 2 is selected by default, OpenGL ES 3 can be enabled by configuring Qt with the -feature-opengles3 option.

Qt for WebAssembly does support mixing raster and OpenGL content. Supported use cases are pure raster apps using QWigets, and pure OpenGL apps using Qt Quick or QOpenGLWindow. QOpenGLWidget is not supported at this point (QTBUG-66944).

Web and Desktop OpenGL differences are documented in: WebGL and OpenGL Differences There are additional differences between WebGL 1.0 and WebGL 2.0 which are documented in: 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(<your target> PRIVATE "SHELL:-s FULL_ES2=1") and/or full ES3 emulation by adding target_link_options(<your target> PRIVATE "SHELL:-s FULL_ES3=1") to your projects CMakeFiles.txt

Qt supports multithreading on WebAssembly, however this feature is experimental and is not enabled by default. Thread support can be enabled by building Qt from source and using the "-feature-thread" configure flag.

The Qt for WebAssembly binary packages do not support multithreading.

Emscripten implements support for pthreads using web workers, and this abstraction is not completely leak free. See Pthreads Support for further info.

Multithreading requires browser support for SharedArrayBuffer, see caniuse sharedarraybuffer for current supported status. If suppoerted, SharedArrayBuffer will be enabled provided the web server sets the COOP and and COEP headers:

• Cross-Origin-Opener-Policy: same-origin
• Cross-Origin-Embedder-Policy: require-corp

### SIMD

The LLVM compiler supports generating WebAssembly SIMD. Pass the -msimd128 flag at compile time to enable. This enables LLVM autovectorization, which makes it possible to benefit from SIMD without making source code modifications.

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. Enable by building Qt from source and configure with the "-sse2" option. This adds support for SSE1, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, and 128-bit AVX instructions, and enables Qt's SSE code paths. 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.

All other network protocols are not supported.

In addition Emscripten supports emulated POSIX TCP sockets over WebSockets. This is not currently supported by Qt's socket classes.

### 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:

### Clipboard Access

Qt supports copying and pasting text to the system clipboard, with some differences due to the web sandbox. In general clipboard access require user permission, which can be obtained by handling an input event (e.g. CTRL+c), or by using the Clipboard API.

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), and that some browsers my require changing configuration flags.

At the time of writing the following browsers support the Clipboard API, see caniuse for the current support level.

• Chrome supports the Clipboard API
• Firefox supports the Clipboard API behind a flag: dom.events.asyncClipboard.dataTransfer

### 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

You can add more verbosity to help debug using Emscripten linker arguments:

• -s LIBRARY_DEBUG=1 (print out library calls)
• -s SYSCALL_DEBUG=1 (print out sys calls)
• -s FS_LOG=1 (print out filesystem operations)
• -s SOCKET_DEBUG (print out socket, network data transfer)

Using cmake:

target_link_options(<your target> PRIVATE "SHELL:-s LIBRARY_DEBUG=1")

Using qmake:

QMAKE_LFLAGS_DEBUG += -s LIBRARY_DEBUG=1

### Asyncify

Asyncify is an Emscripten feature which enables synchronous C++ code to interact with asynchronous JavaScript.

Qt uses it to allow calling blocking APIs like one level of nested QEventLoop::exec() and returning normally.

Asyncify's unwinding and rewinding the stack add overhead to executable size as well as performance. To enable asyncify, configure Qt using:

-device-option QT_EMSCRIPTEN_ASYNCIFY=1

### Known Issues

• Nested event loops are not supported. Applications should not call API like QDialog::exec() and QEventLoop::exec(). Experimental feature Asyncify could be used.
• Printing is not supported
• QDnsLookup lookups, QTcpSocket, QSsl do not work and are not supported due to the web sandbox
• Accessibility is not supported: Qt draws application content to a canvas element and does not use (other) native DOM elements.
• 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.
• 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
• Link time error such as "wasm-ld: error: initial memory too small", requires adjustment of the initial memory size. Use QT_WASM_INITIAL_MEMORY to set the initial size in kb, which must be a multiple of 64KB (65536). Default is 50 MB. In CMakeFiles.txt: set(QT_WASM_INITIAL_MEMORY, "50MB");

## Other Topics

### Qt Configure Options Reference

The following configure options are relevant when building Qt for WebAssembly from source.

Configure ArgumentBrief Description
-sse2Enables SIMD support.
-device-option QT_WASM_SOURCE_MAP=1Debugging option for creating source maps
-feature-opengles3Use opengles3 in addition to the default opengles2
-device-option QT_EMSCRIPTEN_ASYNCIFY=1Use asyncify

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.