Qt for WebAssembly | Qt 6.9 (original) (raw)

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

Each minor version of Qt targets a specific Emscripten 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 Emscripten versions are:

Use emsdk to install specific Emscripten versions. For example, to install it for Qt 6.8 enter:

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:

You can build Qt from source if you require more flexibility when selecting the Emscripten 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.

./configure -qt-host-path /path/to/Qt -platform wasm-emscripten -prefix $PWD/qtbase

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

Note: When using vanilla CMake (as opposed to qt-cmake on Linux or qt-cmake.bat on Windows) remember to specify a toolchain file with "-DCMAKE_TOOLCHAIN_FILE", as for any other cross-platform build. For details see here: Getting started with CMake.

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:

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

Qt Creator: Build applications for the Web.

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 directory at build time.

Typical usage looks like the following:

const app_container_element = ...; const instance = await qtLoad({ qt: { containerElements: [ app_container_element ], onLoaded: () => { /* handle application load completed / }, onExit: () => { / 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 application exits.

The containerElements array is the main interface between Qt and the web page, where the html elements in this array (typically

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:

Porting steps

  1. Include the app.js (JavaScript runtime generated by Emscripten) from the loading html file. 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