- Qt 6.10
- Product information
- Supported Platforms
- Qt for WebAssembly
- Qt 6.10.0 Reference Documentation

Qt for VxWorks ◦ Qt for webOS

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

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 supported version of Emscripten for 6.10.0 is Emscripten 4.0.7.

Refer to the Emscripten documentation for more information about installing the Emscripten SDK.

Use `emsdk` to install specific `Emscripten` versions. For example, to install it for 6.10.0 enter:

- ./emsdk install 4.0.7
- ./emsdk activate 4.0.7

After installation, you should have the Emscripten compiler in your path. Check this with the following command:

     em++ --version

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 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. Also, set the path to the host build 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:

     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.

The qtwasmserver script lives in the sources at

     qtbase/util/wasm/qtwasmserver/qtwasmserver.py

or can be installed using pip:

     pip install qtwasmserver

For more information on qtwasmserver qtwasmserver

Using qtwasmserver:

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

1. Include the `app.js` (JavaScript runtime generated by Emscripten) from the loading html file.

        <script src="app.js"></script>

   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.

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

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.

- Qt Core
- Qt GUI
- Qt Network
- Qt Widgets
- Qt Qml
- Qt Quick
- Qt Quick Controls
- Qt Quick Layouts
- Qt 5 Core Compatibility APIs
- Qt Image Formats
- Qt OpenGL
- Qt SVG
- Qt WebSockets
- Qt Concurrent
- Qt Charts
- Qt Graphs

The following modules are in tech preview. They may have limited functionality or may change significantly in future releases.

- Qt Multimedia
- Qt Quick 3D

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.

## 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 for WebAssembly supports hardware accelerated rendering using https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API WebGL.

WebGL closely conforms to OpenGL ES, with the following version mapping:

OpenGL | WebGL
--- | ---
OpengL ES 2.0 | WebGL 1
OpengL ES 3.0 | WebGL 2

Qt uses the highest available WebGL version. This is typically WebGL 2 on today's browsers, but might be WebGL 1 if limited by hardware. We recommend targeting devices which support WebGL 2 with Qt for WebAssembly.

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(<your target> PRIVATE -s FULL_ES2=1)

and/or full ES3 emulation by adding

     target_link_options(<your target> PRIVATE -s FULL_ES3=1)

to your project's `CMakeLists.txt`.

For more information regarding Emscripten's OpenGL support visit https://emscripten.org/docs/porting/multimedia_and_graphics/OpenGL-support.html

#### OpenGL Context Limitations

WebGL does not support multiple contexts per surface. This has implications for applications that use QOpenGLContext either directly or indirectly via other classes such as QOpenGLWidget.

Each QOpenGLContext instance should be used with a single surface only. In practice, the context gets associated with the surface on the first makeCurrent() call. Calling makeCurrent() on a different surface afterward, or from a different QOpenGLContext with the same surface, is not supported.

OpenGL context sharing is not supported. Calling QOpenGLContext::setShareContext() has no effect, and QOpenGLContext::shareContext() always returns nullptr.

Destroying a surface (for example, a QWindow) causes context loss for the associated context. The application should handle this by recreating the context.

QOpenGLWidget and other classes which uses context sharing internally are not supported.

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

Note: QWebSocketServer is not supported due to browser limitations. Browsers restrict server-side socket functionality to ensure security within the web sandbox. Consequently, any functionality relying on QWebSocketServer for accepting incoming network connections cannot be utilized within the web environment.

Note: QtMqtt and QtRemoteObjects modules can be used with QtWebSockets as transports. They are not officially supported and may or may not work, or have missing functionality.

#### Cross-Origin Resource Sharing (CORS) and Policy (CORP)

WebAssembly apps that use networking may require Cross-Origin Resource Sharing (CORS) and Cross-Origin Resource Policy (CORP) response headers to be set by the server. These restrict HTTP requests that originate from different domains, which present a security risk.

Using QHttpServer, the following is an example for setting these headers:

     auto headers = response.headers();
     headers.append("Access-Control-Allow-Origin", "*");
     headers.append("Access-Control-Allow-Origin", "localhost");
     headers.append("Access-Control-Allow-Methods", "POST", "GET","OPTIONS");

     headers.append("Cross-Origin-Opener-Policy", "same-origin");
     headers.append("Cross-Origin-Embedder-Policy", "require-corp");
     headers.append("Cross-Origin-Resource-Policy", "cross-origin");

Other well known servers have their own way to configure sending these headers.

The use of a wildcard "*" for Access-Control-Allow-Origin, will result in an error, if used with Access-Control-Allow-Credentials.

The included utility script qtwasmserver.py will set these headers if the –cross-origin-isolation option is used.

### 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. It is not preserved over a browser reload.

Since web pages do not have direct access to the local filesystem, Qt provides a QFileDialog API for use with Qt for 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, urls, known file types and images with the system clipboard, with some differences due to the web sandbox. It does not support arbitrary application/octet-stream binary data. 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.

- Chrome version 66 and Safari version 13.1 support the Clipboard API
- Firefox version 90 supports the Clipboard API if you enable the following flags in 'about:config':

       dom.events.asyncClipboard.read
       dom.events.asyncClipboard.clipboardItem

### Fonts

The Qt WASM module contains 3 embedded fonts: "Bitstream Vera Sans" (fallback font), "DejaVu Sans", "DejaVu Sans Mono".

These fonts provide a limited character set. Qt provides several options for adding additional fonts:

One is using FontLoader in QML, which can either fetch a font by URL or using The Qt Resource System (the same way the usual desktop apps work).

The other way to use font is to add it via QFontDatabase::addApplicationFontFromData.

### Accessibility and Screen Readers

Qt for WebAssembly provides basic support for screen readers. Simple UI elements such as buttons and check boxes work, while more complex UI elments such as table or tree views may have missing support. Both Qt Widgets and Qt Quick are supported.

The following screen reader / browsers configurations are tested and are known to be functional. Other browsers and screen readers may work.

- VoiceOver with Safari on macOS
- VoiceOver with Chrome on macOS

The accessibility implementation works by creating "shadow" html elements which provides the accessiblity information for the Qt UI elements. This functionality is disabled by default. The end user can activate it by selecting an "activate screen reader" button using the screen reader. When activated this will populate the web page with accessibility elements.

### Application Startup and the Event Loop

Qt for WebAssembly supports the standard Qt startup approach, where the application creates a QApplication object and calls the exec function:

     int main(int argc, char **argv)
     {
         QApplication app(argc, argv);

         QWindow appWindow;

         return app.exec();
     }

The exec() call above normally blocks and processes events until application shutdown. Unfortunately this is not possible on the web platform where blocking the main thread is not allowed. Instead, control must be returned to the browser's event loop after processing each event.

Qt works around this by making exec() return main thread control to the browser, while preserving the stack. From the point of view of application code, the exec() function is entered and event processing happens as usual. However, the exec() call never returns, also not on application exit.

This behavior is usually acceptable since the browser will free up application memory at app shutdown time. It does mean that shutdown code does not run, since the application object is leaked and its destructor does not run.

You can avoid this by rewriting main() to be asynchronous, which is possible since Emscripten does not exit the runtime when main() returns. Application code then omits making the exec() call, and can shut down Qt cleanly by deleting the top-level window and application objects.

     QApplication *g_app = nullptr;
     AppWindow *g_appWindow = nullptr;

     int main(int argc, char **argv)
     {
         g_app = new QApplication(argc, argv);
         g_appWindow = new AppWindow();
         return 0;
     }

#### Asyncify and JSPI

The default Qt build for WebAssembly does not support reentering the event loop, such as by calling QEventLoop::exec() or QDialog::exec(), due to web platform restrictions that prevent synchronous C++ code from invoking asynchronous JavaScript APIs.

Features that require asyncify/JSPI include:

- QDialogs, QMessageBoxes with return values.
- Drag and drop (specifically drag).
- Nested/secondary event loops exec().

Emscripten provides support for working around these limitations using the Asyncify feature. This feature is available in two versions:

- Asyncify, implemented via a WebAssembly code post-processing step.
- JSPI, implemented using the WebAssembly JS Promise Integration feature.

See the sections below for details on how to enable each option and the trade-offs involved. In brief, Asyncify is available today but introduces overhead in the form of longer build times, larger binary sizes, and runtime performance costs. JSPI has minimal or no overhead, but it is not yet supported by all browsers.

#### Asyncify

Enable asyncify by adding the "-sASYNCIFY -Os" flags to the linker options:

CMake:

     target_link_options(<your target> PUBLIC -sASYNCIFY -Os)

qmake:

     QMAKE_LFLAGS += -sASYNCIFY -Os

Enabling asyncify adds overhead in the form of increased binary sizes and increased CPU usage. Build with optimizations enabled to minimize the overhead.

#### JSPI (JS Promise Integration)

Unlike Asyncify, using JSPI requires building Qt from source. Pass the -feature-wasm-jspi -feature-wasm-exceptions flags to the Qt configure script to enable it. (JSPI is not compatible with the default Emscripten emulated exceptions.)

Then, enable JSPI for your application by adding the -sJSPI flag to the linker options:

CMake:

     target_link_options(<your target> PUBLIC -sJSPI)

qmake:

     QMAKE_LFLAGS += -sJSPI

### Debugging and Profiling

Wasm debugging is done on the 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 re-configuring Qt with the –device-option QT_WASM_SOURCE_MAP=1, and building a debug build.
  - https://developer.mozilla.org/en-US/docs/Tools/Debugger/How_to/Use_a_source_map
  - https://developers.google.com/web/tools/chrome-devtools/javascript/source-maps#source_maps_in_devtools_sources_panel
- Debug symbols via DWARF are also enabled if the program is linked with the -g flag (tested with Chrome)
- This will require this extension: https://goo.gle/wasm-debugging-extension
- See also https://developer.chrome.com/blog/wasm-debugging-2020/
- Mobile browsers can use remote debugging
  - https://developer.mozilla.org/en-US/docs/Tools/Remote_Debugging
  - https://developers.google.com/web/tools/chrome-devtools/remote-debugging
  - https://developer.apple.com/safari/tools/
- 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)

CMake:

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

qmake:

     QMAKE_LFLAGS_DEBUG += -s LIBRARY_DEBUG=1

### Optimizing

Qt for WebAssembly uses the Emscripten toolchain to generate binaries, and there are many flags that may impact performance and the size of binaries. See Emscripten: Optimizing Code for more information.

You can pass linker and compiler flags just as for normal C++ applications:

## Using CMake

     target_compile_options(<your target> PRIVATE -oz -flto)
     target_link_options(<your target> PRIVATE -flto)

## Using qmake

     QMAKE_CXXFLAGS += -oz -flto
     QMAKE_LFLAGS += -flto

#### Minimizing the size of binaries

In order to provide a seamless user experience, it's important to reduce the time to download and load WebAssembly applications. Smaller application binary is one of the important aspects that enable faster download. Use the following alternatives to reduce the binary size:

- Make sure to distribute release builds. Debug builds contain debug symbols and are much bigger.
- Enable compression on a server. The most common algorithms like `gzip` and Brotli work well on Wasm binaries and can drastically reduce their size.
- Try compiler and linker flags that may results in generating smaller binaries (i.e. '-os', '-oz'). Results will vary depending on the particular application.
- Disable unsued features when compiling Qt for WebAssembly from source (see below).

##### Opting out of features

A WebAssembly application by default links statically to the Qt libraries, enabling the compiler to eliminate dead code. However, due to Qt's dynamic nature it's not always possible for the compiler to perform such optimizations.

If you build Qt for WebAssembly from source, you can disable features to reduce the size of Qt binaries, and — as a result — the size of .wasm binaries. Qt disables some features by default for the WebAssembly platform, but you can also disable features that your application does not use. See disabled features for more information.

You can disable the following features to reduce binary size (usually by 10-15%):

Configure Argument | Brief Description
--- | ---
-no-feature-cssparser | Parser for Cascading Style Sheets.
-no-feature-datetimeedit | Editing dates and times (depends on datetimeparser).
-no-feature-datetimeparser | Parsing date-time texts.
-no-feature-dockwidget | Docking widgets inside a QMainWindow or floating them as top-level windows on the desktop.
-no-feature-gestures | Framework for gestures.
-no-feature-mimetype | Mimetype handling.
-no-feature-qml-network | Network transparency.
-no-feature-qml-list-model | ListModel QML type.
-no-feature-qml-table-model | TableModel QML type.
-no-feature-quick-canvas | Canvas item.
-no-feature-quick-path | Path elements.
-no-feature-quick-pathview | PathView item.
-no-feature-quick-treeview | TreeView item.
-no-feature-style-stylesheet | Widget style which is configurable via CSS.
-no-feature-tableview | Default model/view implementation of a table view.
-no-feature-texthtmlparser | Parser for HTML.
-no-feature-textmarkdownreader | Markdown (CommonMark and GitHub) reader.
-no-feature-textodfwriter | ODF writer.

### Wasm Exceptions

Qt is built without exception support by default, where throwing an exception will abort the program. WebAssembly exceptions can be enabled by building from source and passing the -feature-wasm-exceptions flag to Qt configure. This will pass the -fwasm-exceptions flag to the compiler at compile and link time. Qt does not support enabling Emscripten's support for the earlier JavaScript-based exception implementation.

Note that calling QApplication::exec() is not supported when exceptions are enabled, due to internal implementation details. Instead, write main() in the form where it returns early and does not call exec(), as described in Application Startup and the Event Loop.

### Shared Libraries and Dynamic Linking Developer Preview

Qt for WebAssembly uses static linking by default, where the application is deployed as a single WebAssembly file which contains the Qt libraries and application code. Dynamic linking is an alternative build mode where each library and plugin is distributed individually.

For instance, an application which uses Qt Quick may make use of the following libraries and plugins:

- <qtpath>/lib/libQt6Core.so
- <qtpath>/lib/libQt6Gui.so
- <qtpath>/lib/libQt6Qml.so
- <qtpath>/lib/libQt6Quick.so
- <qtpath>/plugins/imageformats/libqjpeg.so
- <qtpath>/plugins/imageformats/libqjgif.so
- <qtpath>/qml/QtQuick/Window/libquickwindowplugin.so

Dynamic linking support is currently in developer preview. The implementation is suitable for prototyping and evaluation, but is not suitable for production use. Current limitations and restrictions include:

- Multithreading is not supported.
- Asyncify is not supported.

Qt applications built with dynamic linking require two additional files to be present alongside the binaries: qt_plugins.json and qt_qml_imports.json. These files specify a list of shared libraries that will be loaded at application startup. There are helper scripts available to generate them: preload_qt_plugins.py and preload_qml_imports.py. To demonstrate how to use these scripts, a helper script named generate_default_preloads_for_<target>.sh will be provided.

The web server hosting the application must have the Qt shared libraries available. This can be accomplished by copying the contents of the Qt installation folder to the web server, or by creating a file system link.

#### Quick Start

The build and deployment procedure is slightly different than that from static wasm and shared desktop builds. Consider starting with a small example before progressing to a full application build.

1. Build Qt from source, pass the “-shared” option to the Qt configure script. Use the '-prefix' option to set the install directory.
2. Build your application using Qt from step 1.
3. Deploy the Qt installation by copying or linking to a directory named "qt" in the application directory
   - ln -s <qtpath> qt
   - cp -r <qtpath> qt
4. Create plugin preloading lists by running deployment scripts.
   - <qtpath>/qtbase/util/wasm/preload/preload_qt_plugins.py <qt_install_dir> <target_dir>
   - <qtpath>/qtbase/util/wasm/preload/preload_qml_imports.py <app_source dir> <qt_host_dir> <qt_install_dir> <target_dir>

#### Shared Libraries Deployment in Depth

The shared libraries build of Qt is deployed in two stages, where the first stage makes the Qt and application build available for download from the web server, and the second stage downloads required Qt plugins and Qt Quick imports at application startup.

In the first step, make the Qt installation available for download from the web server. Depending on the specifics of the web server setup there may be different ways to accomplish this. In common is that the Qt loader expects to find the Qt libraries and plugins in a directory name "qt", relative to the html file which loads the application.

If you are already copying the application to the web server as a part of deployment, then copying Qt as well is a possible option. If you are serving the application directly from its build directroy - often the case during development phases - then creating a symlink to Qt can work well.

Prepare for the second step by creating preload lists for Qt components such as plugins and Qt Quick imports. Preloading ensures that all required Qt components are availabe at application startup. Delayed loading, where components are dowloaded on demand, is also possbile but is not covered here.

Preloading is implemented by the Qt JavaScript loader, which downloads files from the web server to the in-memory file system provided by Emscripten. Which files to download is specified using json-formatted download lists. Qt provides two scripts for generating preload lists, see Quick Start section above.

### Known Issues

- Nested event loops are not supported. Applications should not call API like QDialog::exec() and QEventLoop::exec(). The experimental feature Asyncify could be used.
- Printing is not supported.
- QDnsLookup lookups and QSsl do not work and are not supported due to the web sandbox. The browser handles dns lookups and SSL certificates. DNS over HTTP could be used by applications needing dns lookups.
- QTcpSockets can be used but not all POSIX sockets functions are proxied. A Websockets server proxy like Websockify needs to be used.
- All Q*Server classes are not supported by the platform.
- QWebSocket connections are supported by Emscripten only on the main thread.
- QWebSockets for WebAssembly does not support sending ping or pong frames, as the API available to web pages and browsers does not expose this functionality.
- To use QtWebsockets, the subprotocol might need to be set to 'mqtt' for using QtMqtt. Use QWebSocketHandshakeOptions when you open the QWebSocket.
- 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 CMakeLists.txt: set_target_properties(<target> PROPERTIES QT_WASM_INITIAL_MEMORY "150MB")
- add_executable in CMakeLists.txt does not produce <target>.html or copy qtloader.js. Use qt_add_executable instead.

## Other Topics

### Qt Configure Options Reference

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

Configure Argument | Brief Description
--- | ---
-feature-thread | Multi-threaded Wasm.
-feature-wasm-simd128 | Enable WebAssembly SIMD support.
-feature-wasm-exceptions | Enable WebAssembly exceptions support.
-device-option QT_EMSCRIPTEN_ASYNCIFY=1 | Enable asyncify support.
-device-option QT_EMSCRIPTEN_ASYNCIFY=2 | Enable asyncify (JSPI) support.

Qt disables some features by default for the WebAssembly platform, to reduce the binary size. You can explicitly enable a feature when you configure Qt for WebAssembly:

Configure Argument | Brief Description
--- | ---
-feature-topleveldomain | Provides support for checking if a domain is a top-level domain.

### Typical Download Sizes

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.

For more information, see Minimizing the size of binaries.

### Examples

Examples and demos of Qt applications running in a web browser: Qt Demos

## External resources

- WebAssembly Resource site

## License

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 https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS and https://developer.mozilla.org/en-US/docs/Web/HTTP/Cross-Origin_Resource_Policy.