このページでは

WebAssembly向けQt

Qt for WebAssembly を使用すると、Web上でQtアプリケーションを実行できます。

WebAssembly(略称:Wasm)は、Webブラウザなどの仮想マシン内で実行されることを目的としたバイナリ命令形式です。

Qt for WebAssembly を使用すると、ブラウザのサンドボックス内で動作する Web アプリケーションとして、アプリケーションを配布することができます。このアプローチは、ホストデバイスの機能への完全なアクセスを必要としない、Web ベースの分散アプリケーションに適しています。

注:Qt for WebAssemblyはサポート対象のプラットフォームですが、一部のモジュールはまだサポートされていないか、Tech Preview段階にあります。「サポートされているQtモジュール」を参照してください。

Qt for WebAssembly のはじめ方

WebAssembly 向けの Qt アプリケーションのビルドは、他のプラットフォーム向けの Qt のビルドと同様です。 SDK(Emscripten)をインストールし、Qtをインストール(またはソースからQtをビルド)し、最後にアプリケーションをビルドする必要があります。いくつかの違いがあり、例えば、Qt for WebAssemblyは他のQtビルドに比べてサポートされるモジュールや機能が限られています。

Emscriptenのインストール

Emscriptenは、WebAssembly向けにコンパイルするためのツールチェーンです。これにより、ブラウザプラグインを使用せずに、ネイティブに近い速度でWeb上でQtを実行できるようになります。

Qtの各マイナーバージョンは特定のエムスクリプテンバージョンを対象としており、パッチリリースではこのバージョンは変更されません。Qtのバイナリパッケージは、対象となるエムスクリプテンバージョンを使用してビルドされています。エムスクリプテンはバージョン間のABI互換性を保証していないため、アプリケーションでも同じバージョンを使用する必要があります。

6.12.0 でサポートされている Emscripten のバージョンは Emscripten 5.0.5 です。

Emscripten SDK のインストールに関する詳細については、Emscripten のドキュメントを参照してください。

特定のバージョンのEmscripten をインストールするには、`emsdk `を使用してください。たとえば、6.12.0用にインストールするには、次のように入力します:

  • ./emsdk install 5.0.5
  • ./emsdk activate 5.0.5

インストール後、Emscripten コンパイラがパスに追加されているはずです。次のコマンドでこれを確認してください:

em++ --version

Windows では、インストール後に Emscripten がパスに追加されます。macOS や Linux では、次のようにパスに追加する必要があります:

source /path/to/emsdk/emsdk_env.sh

次のコマンドで設定を確認してください:

em++ --version

Emscriptenのバージョン選択においてより柔軟性が必要な場合は、ソースからQtをビルドすることができます。この場合、上記のバージョンは最低要件となります。それ以降のバージョンでも動作する見込みですが、動作の変化が生じ、Qtへの変更が必要になる可能性があります。

Qtのインストール

Qtアカウントの「ダウンロード」セクションからQtをダウンロードしてください。開発プラットフォームとして、Linux、macOS、Windows向けのビルドを提供しています。

バイナリビルドは、できるだけ多くのブラウザで動作するように設計されており、シングルスレッド版とマルチスレッド版が用意されています。Wasm SIMD やWasm exceptions などの非標準機能は、バイナリビルドではサポートされていません。

ソースから Qt をビルドする

ソースからのビルドでは、スレッドのサポート、OpenGL ES レベル、SIMD サポートなどの Qt 設定オプションを設定できます。Qt アカウントの「ダウンロード」セクションから Qt ソースをダウンロードしてください。

wasm-emscripten プラットフォーム向けのクロスコンパイルビルドとしてQtを設定します。これにより、-static 、-no-feature-thread 、および-no-make examples というconfigureオプションが設定されます。-feature-thread というconfigureオプションを使用すると、スレッドサポートを有効にできます。共有ライブラリのビルドはサポートされていません。

同じバージョンの Qt のホストビルドが必要です。また、QT_HOST_PATHCMake 変数、または `-qt-host-path ` 設定引数を使用して、ホストビルドへのパスを設定してください。

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

注: ninja 実行ファイルが利用可能な場合、configure は 常にNinjaジェネレータおよびビルドツールを使用します。Ninja はクロスプラットフォームで、機能が豊富かつ高性能であり、すべてのプラットフォームで推奨されています。他のジェネレータを使用しても動作する可能性はありますが、公式にはサポートされていません。

Windows では、PATH に Mingw-w64 が含まれていることを確認し、次のように configure を実行してください:

configure -qt-host-path C:\Path\to\Qt -no-warnings-are-errors -platform wasm-emscripten -prefix %CD%\qtbase

その後、必要なモジュールをビルドしてください:

cmake --build . -t qtbase -t qtdeclarative [-t another_module]

コマンドラインでのアプリケーションのビルド

Qt for WebAssembly は、CMake と ninja または make を使用したアプリケーションのビルドをサポートしています。

$ /path/to/qt-wasm/qtbase/bin/qt-cmake .
$ cmake --build .

注: Linuxの「qt-cmake 」やWindowsの「qt-cmake.bat 」ではなく、標準のCMake を使用する場合は 、他のクロスプラットフォームビルドと同様に、「-DCMAKE_TOOLCHAIN_FILE」でツールチェーンファイルを指定することを忘れないでください。詳細については、こちらをご覧ください:CMakeの始め方。

アプリケーションをビルドすると、いくつかの出力ファイルが生成されます。これには、アプリケーションと Qt コード(静的リンク)を含む .wasm ファイルや、ブラウザで開いてアプリケーションを実行できる .html ファイルなどが含まれます。

注: Emscripten は 、「-g」デバッグレベルでは比較的大きな .wasm ファイルを生成します。デバッグビルドでは、「-g2」でリンクすることを検討してください。

アプリケーションの実行

アプリケーションを実行するには、Webサーバーが必要です。ビルド出力ファイルはすべて静的コンテンツであるため、どのWebサーバーでも問題ありません。ただし、HTTPS証明書の提供や、マルチスレッドサポートを有効にするために必要なHTTPヘッダーの設定など、特定のユースケースでは特別なサーバー設定が必要になる場合があります。

Emrun

Emscriptenには、アプリケーションのテスト実行を行うためのユーティリティ「emrun」が用意されています。emrunはWebサーバーを起動し、ブラウザを起動するほか、stdout/stderr(通常はJavaScriptコンソールに出力されるもの)をキャプチャして転送します。

/path/to/emscripten/emrun --browser=firefox appname.html

Pythonのhttp.server

もう 1 つの方法は、開発用 Web サーバーを起動し、Web ブラウザを別途起動することです。最も簡単な方法の 1 つは、Python の `http.server` を使用することです:

python -m http.server

なお、これは単なる簡易なWebサーバーであり、後述する必須のCOOP(Cross-Origin-Opener-Policy)およびCOEP(Cross-Origin-Embedder-Policy)ヘッダーが送信されないため、スレッド処理に必要なSharedArrayBufferには対応していません。

qtwasmserver

Qt には、mkcertを使用して HTTPS 証明書を生成する開発者向け Web サーバーが用意されています。これにより、セキュアなコンテキストを必要とする Web 機能のテストが可能になります。なお、http://localhost 経由での配信も、証明書を必要とせずにセキュアであるとみなされます。

また、このWebサーバーは、SharedArrayBufferおよびマルチスレッドのサポートを有効にする値に、COOPおよびCOEPヘッダーを設定します。

qtwasmserver スクリプトは、デフォルトで localhost にバインドするサーバーを 1 つ起動します。-aコマンドライン引数を使用して追加のアドレスを指定したり、--all を使用して利用可能なすべてのアドレスにバインドしたりすることができます。

qtwasmserver スクリプトは、以下のソースディレクトリにあります。

qtbase/util/wasm/qtwasmserver/qtwasmserver.py

にあり、pip を使用してインストールすることもできます:

pip install qtwasmserver

qtwasmserver に関する詳細情報については、qtwasmserverを参照してください。

qtwasmserver の使用方法:

python /path/to/qtbase/util/wasm/qtwasmserver/qtwasmserver.py --all

アプリケーションのビルドにはQt Creator

Qt Creator: Web 向けのアプリケーションをビルドします。

Web上へのアプリケーションのデプロイ

アプリケーションをビルドすると、いくつかのファイルが生成されます(以下の表では、「app」をアプリケーション名に置き換えてください)。

生成されるファイル概要
app.htmlHTML コンテナ
qtloader.jsQtアプリケーションを読み込むためのJavaScript API
app.jsEmscriptenによって生成されたJavaScriptランタイム
app.wasmアプリのバイナリ

app.htmlはそのままデプロイすることも、カスタムHTMLファイルに置き換えることもできます。また、スプラッシュ画面の画像をQtのロゴからアプリのロゴに変更するといった、小規模な調整も可能です。いずれの場合も、qtloader.jsがアプリケーションを読み込むためのJavaScript APIを提供します。

デプロイする前に、gzip またはbrotli を使用して Wasm ファイルを圧縮してください。これらのツールは他のツールよりも優れた圧縮率を提供します。詳細については、「バイナリサイズの最小化」を参照してください。

マルチスレッドやSIMDなどの特定の機能を有効にすると、その機能をサポートしていないブラウザとは互換性のない.wasmバイナリが生成されます。 この制限を回避するには、複数の .wasm ファイルをビルドし、JavaScript の機能検出を使用して適切なものを選択する方法がありますが、Qt にはこのための機能は提供されていない点に注意してください。

qtloaderの使用

Qt は、Qt for WebAssembly アプリケーションのダウンロード、コンパイル、およびインスタンス化を行うための JavaScript API を提供しています。このロード API は、Emscripten が提供するロード機能をラップしており、Qt ベースのアプリケーションに役立つ追加機能を提供します。これは qtloader.js ファイルに実装されています。 このファイルのコピーは、ビルド時にビルドディレクトリに書き込まれます。

一般的な使用例は以下のようになります:

const app_container_element = ...;
const instance = await qtLoad({
    qt: {
        containerElements: [ app_container_element ],
        onLoaded: () => { /* handle application load completed */  },
        onExit: () => {  /* handle application exit */ },
    }
});

このコードは、設定オブジェクトを引数としてqtLoad()ローダー関数を呼び出します。この設定オブジェクトには、あらゆるemscripten設定オプションに加え、特別な「qt」設定オブジェクトを含めることができます。qt設定オブジェクトは、以下のプロパティをサポートしています:

プロパティ概要
containerElementsHTMLコンテナ要素の配列。アプリケーションはこれらをQScreenとして認識します。
onLoadedアプリケーションの読み込みが完了した際のコールバック。
onExitアプリケーションが終了した際のコールバック。

containerElements 配列は、Qt と Web ページ間の主要なインターフェースであり、この配列内の HTML 要素(通常は <DIV> 要素)が、Web ページ上のアプリケーションコンテンツの位置を指定します。

アプリケーションは各コンテナ要素をQScreen のインスタンスとして認識し、通常通り画面インスタンス上にアプリケーションウィンドウを配置することができます。Qt::WindowFullScreen 状態が設定されたウィンドウは画面領域全体を使用しますが、"fullscreen"でないウィンドウにはウィンドウ装飾が表示されます。

qtLoad()関数はプロミスを返し、そのプロミスが解決されるとEmscriptenインスタンスが返されます。このインスタンスを通じて、Embindからエクスポートされた関数にアクセスできます。Qtはいくつかのそのような関数をエクスポートしており、これらの関数がインスタンスAPIを構成しています。

QtインスタンスAPIの使用

Qt はいくつかのインスタンス関数を提供しています。現在、これらは実行時にコンテナ要素の追加および削除をサポートしています。

プロパティ概要
qtAddContainerElementコンテナ要素を追加します。要素を追加すると、新しいQScreen が追加されます。
qtRemoveContainerElementコンテナ要素およびそれに対応する画面を削除します。
qtSetContainerElementsすべてのコンテナ要素を設定します
qtResizeContainerElementQtにコンテナ要素のサイズ変更を反映させます。

Qt 6.6 の qtloader への移植

Qt 6.6 には、実装が簡素化され、適用範囲が縮小された新しい qtloader が含まれています。これには API の変更が含まれており、アプリケーションの JavaScript コードの移植が必要になる場合があります。Qt では、移行を円滑にするための互換性 API が提供されています。ユースケースに応じて、いくつかの対応方法があります:

  • 生成されたapp.html ファイルを直接使用している場合、このファイルもビルド時に更新されます。特別な対応は必要ありません。
  • 基本的なqtloaderの機能セットを使用している場合は、暫定的な措置としてQt 6.6に含まれる互換性APIを使用できます。このAPIは将来のリリースで削除される予定ですので、新しいqtloaderを使用するように更新する計画を立てる必要があります。以下の「移植手順 1」が必要です。
  • 高度な機能(実行時にコンテナ要素を追加するなど)を使用している場合は、新しいローダーまたはインスタンス API への移植が必要です。以下の移植手順 1 および 2 が必要です。

移植手順

  1. 読み込み用 HTML ファイルから、app.js (Emscripten によって生成された JavaScript ランタイム)をインクルードしてください。
    <script src="app.js"></script>

    Qt 6.6 以前では、qtloader がこの JavaScript ファイルをロードして評価していました。現在ではこの処理は行われなくなったため、<script> タグを使用してこのファイルを組み込む必要があります。

  2. 新しい JavaScript およびインスタンス API を使用するように移植してください。

上記のドキュメントのセクションを参照してください。

対応ブラウザ

デスクトップ

Qt for WebAssembly は、以下のブラウザ上で開発およびテストされています:

  • Chrome
  • Firefox
  • Safari
  • Edge

ブラウザがWebAssemblyに対応していれば、Qtは動作するはずです。WebAssemblyに対応しているブラウザは、多くの場合WebGLにも対応していますが、一部のブラウザでは、古いGPUや未対応のGPUをブラックリストに登録している場合があります。s/qtloader.jsには、WebGLが利用可能かどうかを確認するためのAPIが用意されています。

Qtはオペレーティングシステムの機能を直接利用しないため、例えばFirefoxがWindows上で動作するかmacOS上で動作するかには違いがありません。ただし、macOSにおけるCtrl/Cmdキーの処理など、一部のオペレーティングシステムに合わせた調整は行われています。

モバイル

WebAssembly 向け Qt アプリケーションは、モバイル版 Safari や Android 版 Chrome などのモバイルブラウザ上で動作します。

サポートされているQtモジュール

Qt for WebAssembly は、Qt モジュールおよび機能の一部をサポートしています。テスト済みのモジュールは以下に挙げますが、その他のモジュールについては正常に動作する場合もあれば、動作しない場合もあります。

以下のモジュールはテクノロジープレビュー版です。機能が制限されている場合や、今後のリリースで大幅に変更される可能性があります。

いずれの場合も、モジュールのサポートが完全ではない可能性があり、ブラウザのサンドボックスやQtプラットフォームの移植が不完全であることに起因する追加の制限がある場合があります。詳細については、『Developing with Qt for WebAssembly』を参照してください。

『Qt for WebAssembly による開発』

CMake を使用したビルド

CMakeでEmscripten固有の設定が必要な場合は、以下のコードを利用できます:

if(EMSCRIPTEN)
    # WebAssembly specific code
else()
    # other platforms
endif()

このコードを使用することで、他のプラットフォームとの互換性を確保しつつ、Emscripten固有の設定に対応することができます。

OpenGL および WebGL

Qt for WebAssemblyは、https://developer.mozilla.org/en-US/docs/Web/API/WebGL_APIWebGLを使用したハードウェアアクセラレーションによるレンダリングをサポートしています。

WebGLはOpenGL ESに厳密に準拠しており、バージョンの対応関係は以下の通りです:

OpenGLWebGL
OpenGL ES 2.0WebGL 1
OpenGL ES 3.0WebGL 2

Qt は利用可能な WebGL の最高バージョンを使用します。現在のブラウザでは通常 WebGL 2 ですが、ハードウェアの制限により WebGL 1 になる場合もあります。Qt for WebAssembly を使用する際は、WebGL 2 をサポートするデバイスをターゲットにすることをお勧めします。

WebとデスクトップのOpenGLの違いについては、「WebGLとOpenGLの違い」に記述されています。また、WebGL 1.0とWebGL 2.0の間にはさらに違いがあり、それらは「WebGL 2.0仕様書」に記載されています。

デフォルトでは、ES2(およびES3)のWebGL対応サブセットが使用されます。バウンドバッファを使用せずにglDrawArrays およびglDrawElements を使用する必要がある場合は、以下を追加することで完全なES2サポートを有効にできます。

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

を追加して完全なES2エミュレーションを有効にしたり、

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

をプロジェクトのCMakeLists.txt に追加することで、ES3の完全なエミュレーションを実現できます。

EmscriptenのOpenGLサポートに関する詳細については、https://emscripten.org/docs/porting/multimedia_and_graphics/OpenGL-support.htmlをご覧ください。

OpenGLコンテキストの制限事項

WebGLでは、1つのサーフェスにつき複数のコンテキストをサポートしていません。これは、QOpenGLContext を直接、あるいはQOpenGLWidget などの他のクラスを介して間接的に使用するアプリケーションに影響を及ぼします。

各QOpenGLContext インスタンスは、1つのサーフェスのみと組み合わせて使用する必要があります。実際には、最初のmakeCurrent()の呼び出し時に、コンテキストがサーフェスに関連付けられます。その後、別のサーフェスに対してmakeCurrent()を呼び出したり、同じサーフェスを持つ別のQOpenGLContext からmakeCurrent()を呼び出したりすることはサポートされていません。

OpenGLコンテキストの共有はサポートされていません。QOpenGLContext::setShareContext()を呼び出しても何の効果もなく、QOpenGLContext::shareContext()は常にnullptrを返します。

サーフェス(例:QWindow )を破棄すると、関連付けられたコンテキストが失われます。アプリケーションは、コンテキストを再作成することでこの処理を行う必要があります。

QOpenGLWidget および内部でコンテキスト共有を使用するその他のクラスはサポートされていません。

マルチスレッド

Qt for WebAssembly は、Emscripten のPthreads サポートを利用したマルチスレッドに対応しており、各スレッドはWeb Worker によってサポートされています。 マルチスレッドを有効にするには、Qt Maintenance Tool から「WebAssembly (multi-threaded)」コンポーネントをインストールするか、ソースから Qt をビルドして configure に「-feature-thread」フラグを指定してください。

既存のスレッド関連コードは概ね再利用可能ですが、pthreadの実装上の特性を回避するために修正が必要になる場合があります。EmscriptenおよびQtの一部の機能はサポートされていません。これには、スレッドプロキシ機能や、Qt Quick のスレッド化されたレンダリングループが含まれます。

Qt for WebAssembly では、メインスレッドがセカンダリスレッドからのリクエストを処理する必要がある可能性があるため、メインスレッドをブロックしないことが特に重要です。例えば、Qt のすべてのタイマーはメインスレッド上でスケジュールされており、メインスレッドがブロックされていると発火しません。 また、新しい Web Worker(スレッド用)の作成は、メインスレッドからのみ行うことができるという点も例として挙げられます。

Emscripten では、この問題に対するいくつかの緩和策が用意されています。ミューテックスロックの取得などの短時間の待機については、ロック待ちの間にイベントを処理する「ビジー待機」がサポートされています。一方、メインスレッドでの長時間の待機は避けるべきです。 特に、QThread::wait() や pthread_join() を呼び出してセカンダリスレッドを待機するという一般的な手法は、アプリケーションが、そのスレッド(および Web ワーカー)がすでに起動済みであり、wait() や join() の呼び出しが行われる時点でメインスレッドの支援なしに完了できることを保証できない限り、機能しません。

このマルチスレッド機能を利用するには、ブラウザがSharedArrayBufferAPI をサポートしている必要があります。(通常、Emscripten はヒープを ArrayBuffer オブジェクトに格納します。 マルチスレッド化を行うには、ヒープを Web Worker と共有する必要があり、SharedArrayBuffer が必要となります。)この API は一般的にすべての最新ブラウザで利用可能ですが、特定のセキュリティ要件が満たされていない場合は無効化される可能性があります。その場合、スレッドサポートが有効になっている WebAssembly バイナリは、実際にスレッドを起動していなくても実行に失敗します。

SharedArrayBufferを有効にするには、セキュアなブラウジングコンテキスト(ページがhttps://またはhttp://localhost経由で配信されている状態)が必要であり、かつページがクロスオリジン隔離モードにある必要があります。後者は、Webサーバー上でいわゆるCOOPおよびCOEPヘッダーを設定することで実現できます:

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

SIMD

EmscriptenはWebAssembly SIMDをサポートしており、WebAssembly向けに128ビットのSIMD型および演算を提供します。

Qtをソースからビルドし、-feature-wasm-simd128フラグを指定して設定することで有効にできます。これにより、コンパイル時およびリンク時に-msimd128フラグが渡されます。 現時点では、Qtにはwasm-simdに最適化されたコードパスは含まれていませんが、wasm-simdを有効にすると、コンパイラがSIMD命令を使用できる場面でコンパイラの自動ベクトル化が有効になります。

GCC/ClangのSIMDベクトル拡張またはWASM SIMD128イントリンシックのいずれかを使用して、WebAssembly SIMDを直接ターゲットに設定できます。詳細については、Emscripten のSIMDドキュメント を参照してください 。

さらに、Emscripten は x86 SSE 命令を Wasm SIMD 命令にエミュレート/変換することをサポートしています。Qt はこのエミュレーションを使用しません。これは、ネイティブな Wasm SIMD 相当物がない SSE SIMD 命令を使用すると、パフォーマンスが低下する可能性があるためです。

なお、SIMD対応のバイナリは、WebAssembly SIMDをサポートしていないブラウザとは互換性がありません。これは、実行時にSIMDコードパスが呼び出されない場合でも同様です。SIMDサポートを有効にするには、ブラウザの詳細設定(「about:config」や「chrome:flags」など)で設定を行う必要がある場合があります。

ネットワーク

Qt Network はネットワーク機能に対して限定的なサポートを提供しています。一般的に、Web ですでに使用されているネットワークプロトコルは Qt からも利用できますが、Web サンドボックスの制約により、その他のプロトコルは直接利用できない場合があります。

以下のプロトコルがサポートされています:

  • QNetworkAccessManager Webページのオリジンサーバー、またはCORSをサポートするサーバーへのHTTPリクエスト。これには、QMLからのXMLHttpRequestも含まれます。
  • QWebSocket 任意のホストへの接続。なお、セキュアな https プロトコルで提供される Web ページでは、WebSocket 接続はセキュアな wss プロトコルを介してのみ許可される点に注意してください。
  • Emscriptenが提供する機能を使用した、WebSockets経由のPOSIX TCPソケットのエミュレーション。これには、ソケットの変換を処理するフォワーディングサーバーの実行が必要であることに注意してください。

その他のすべてのネットワークプロトコルはサポートされていません。

注: ブラウザの制限により、QWebSocketServer は サポートされていません。ブラウザは、Webサンドボックス内のセキュリティを確保するため、サーバー側のソケット機能を制限しています。その結果、着信ネットワーク接続の受け入れにQWebSocketServer に依存する機能は、Web環境内では利用できません。

注: Qt MQTT また、QtRemoteObjects モジュールは、QtWebSockets をトランスポートとして使用できます。これらは公式にはサポートされておらず、動作する場合もあれば動作しない場合もあり、機能が欠落している可能性があります。QMqttClient::connectToHostWebSocket およびQtRemoteObjects WebSockets Applicationsのサンプルを参照してください。

クロスオリジンリソース共有 (CORS) およびポリシー (CORP)

ネットワーク通信を行うWebAssemblyアプリでは、サーバー側でCross-Origin Resource Sharing (CORS)およびCross-Origin Resource Policy (CORP)のレスポンスヘッダーを設定する必要がある場合があります。これらは、異なるドメインから発信されるHTTPリクエストを制限するものであり、セキュリティ上のリスクをもたらします。

QHttpServer を使用した場合、これらのヘッダーを設定する例は以下の通りです:

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");

その他のよく知られたサーバーでは、これらのヘッダーの送信を設定する独自の方法があります。

Access-Control-Allow-Origin でワイルドカード「*」を使用すると、Access-Control-Allow-Credentials と併用した場合、エラーが発生します。

同梱されているユーティリティスクリプト qtwasmserver.py は、--cross-origin-isolation オプションが使用された場合に、これらのヘッダーを設定します。

ローカルファイルへのアクセス

Web ではファイルシステムへのアクセスはサンドボックス化されており、これはアプリケーションのファイル操作方法に影響を与えます。 Web プラットフォームは、ユーザーの制御下でローカルファイルシステムにアクセスするための API や、永続ストレージにアクセスするための API を提供しています。Emscripten と Qt はこれらの機能をラップし、C++ および Qt ベースのアプリケーションからより使いやすくするための API を提供しています。

Webプラットフォームは、ローカルファイルや永続ストレージにアクセスするための機能を提供しています:

  • <input type="file">:ユーザーがファイルを選択できるネイティブなファイル選択ダイアログを表示します。
  • IndexedDBは、永続的なローカルストレージ(ブラウザ外からはアクセスできない)を提供します。

Emscriptenは、POSIX風のAPIを備えたいくつかのファイルシステムを提供しています。これらには以下が含まれます:

  • ファイルをメモリ内に保存する一時ファイルシステム「MEMFS」
  • IndexedDB を使用してファイルを保存する永続ファイルシステム「IDBFS」

Emscriptenは、アプリの起動時に一時的なMEMFSファイルシステムを「/」にマウントします。これにより、QFile が使用可能となり、デフォルトではメモリへのファイルの読み書きが行われます。ただし、ブラウザを再読み込みすると、このファイルシステムは保持されません。

Webページはローカルファイルシステムに直接アクセスできないため、QtはQt for WebAssemblyで使用するためのQFileDialog APIを提供しています。

クリップボードへのアクセス

Qt は、システムのクリップボードを使用したテキスト、URL、既知のファイルタイプ、および画像のコピーと貼り付けをサポートしていますが、Web サンドボックスによるいくつかの違いがあります。任意の application/octet-stream バイナリデータはサポートしていません。 一般的に、クリップボードへのアクセスにはユーザーの許可が必要であり、これは入力イベント(例:CTRL+c)を処理するか、Clipboard API を使用することで取得できます。

フォント

Qt WASM モジュールには、「Bitstream Vera Sans」(フォールバックフォント)、「DejaVu Sans」、「DejaVu Sans Mono」の 3 種類の組み込みフォントが含まれています。

これらのフォントは、限られた文字セットしか提供していません。Qt では、フォントを追加するためのいくつかのオプションが用意されています:

その一つは、QMLでFontLoader を使用することです。これにより、URLからフォントを取得するか、Qtリソースシステム(通常のデスクトップアプリケーションと同じ仕組み)を使用してフォントを取得することができます。

フォントを使用するもう一つの方法は、QFontDatabase::addApplicationFontFromData 経由でフォントを追加することです。

アクセシビリティとスクリーンリーダー

Qt for WebAssembly は、スクリーンリーダーに対する基本的なサポートを提供しています。ボタンやチェックボックスなどの単純な UI 要素は正常に動作しますが、テーブルやツリービューなどのより複雑な UI 要素については、サポートが不十分な場合があります。Qt Widgets およびQt Quick の両方がサポートされています。

以下のスクリーンリーダーおよびブラウザの組み合わせについてはテストが行われており、正常に動作することが確認されています。その他のブラウザやスクリーンリーダーでも動作する可能性があります。

  • macOS上のSafariとVoiceOver
  • macOS上のChromeとVoiceOver

アクセシビリティ機能は、Qt UI 要素のアクセシビリティ情報を提供する「shadow」HTML 要素を作成することで動作します。この機能はデフォルトでは無効になっています。エンドユーザーは、スクリーンリーダーを使用して「スクリーンリーダーを有効にする」ボタンを選択することで、この機能を有効にできます。有効にすると、Web ページにアクセシビリティ要素が追加されます。

ドラッグ&ドロップ

  • ドロップがサポートされています(ブラウザがサポートするファイルおよびMIMEタイプ)。
  • アプリケーション内部でのドラッグ&ドロップはサポートされています。
  • アプリケーション外へのドラッグはサポートされていません。

アプリケーションの起動とイベントループ

Qt for WebAssemblyは、アプリケーションがQApplication オブジェクトを作成し、exec関数を呼び出すという標準的なQtの起動方式をサポートしています:

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

    QWindow appWindow;

    return app.exec();
}

上記の exec() の呼び出しは通常、ブロック状態となり、アプリケーションが終了するまでイベントを処理し続けます。しかし、メインスレッドのブロックが許可されていないWebプラットフォームでは、残念ながらこの方法は使用できません。代わりに、各イベントを処理した後、ブラウザのイベントループに制御を戻す必要があります。

Qt はこの問題を、スタックを維持しつつ、exec() がメインスレッドの制御をブラウザに返還するようにすることで回避しています。アプリケーションコードの観点からは、exec() 関数に入り、イベント処理は通常通り行われます。しかし、exec() の呼び出しは決して戻らず、アプリケーションの終了時にも戻りません。

ブラウザはアプリケーションの終了時にアプリケーションのメモリを解放するため、この挙動は通常問題ありません。ただし、アプリケーションオブジェクトがリークされ、そのデストラクタが実行されないため、シャットダウンコードは実行されないことを意味します。

Emscripten では main() が戻ってもランタイムが終了しないため、main() を非同期に書き直すことでこの問題を回避できます。その場合、アプリケーションコードでは exec() の呼び出しを省略し、最上位のウィンドウおよびアプリケーションオブジェクトを削除することで、Qt を正常に終了させることができます。

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 または JSPI を使用しない場合に適用されます。Asyncify または JSPI を使用する場合は、main() 内で exec() を呼び出す必要があります。

Asyncify と JSPI

WebAssembly向けのデフォルトのQtビルドでは、Webプラットフォームの制限により、同期的なC++コードから非同期のJavaScript APIを呼び出すことが禁止されているため、QEventLoop::exec()やQDialog::exec()などの呼び出しによるイベントループへの再進入はサポートされていません。

asyncify/JSPIを必要とする機能には、以下が含まれます:

  • QDialog、戻り値を持つ QMessageBox。
  • ドラッグ&ドロップ(特にドラッグ)。
  • ネストされた/二次的なイベントループの exec()。

Emscripten では、Asyncify機能を使用してこれらの制限を回避するためのサポートが提供されています。この機能には 2 つのバージョンがあります:

  • WebAssemblyコードの後処理ステップを通じて実装されるAsyncify。
  • WebAssemblyのJS Promise統合機能を使用して実装されたJSPI。

各オプションの有効化方法およびそれに伴うトレードオフの詳細については、以下のセクションを参照してください。簡単に言えば、Asyncifyは現在利用可能ですが、ビルド時間の延長、バイナリサイズの増加、および実行時のパフォーマンス低下というオーバーヘッドが生じます。JSPIはオーバーヘッドが最小限か、あるいは全くありませんが、まだすべてのブラウザでサポートされているわけではありません。

注: 以前のバージョンの Qt for WebAssembly では、main() 内で application.exec()を呼び出すことは 任意でしたが、Asyncify または JSPI を使用する場合は、これが必須となっています。

Asyncify

リンカーオプションに「-sASYNCIFY -Os」フラグを追加して、asyncifyを有効にします:

CMake:

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

qmake:

QMAKE_LFLAGS += -sASYNCIFY -Os

asyncify を有効にすると、バイナリサイズの増加や CPU 使用率の上昇といったオーバーヘッドが生じます。オーバーヘッドを最小限に抑えるため、最適化を有効にしてビルドしてください。

JSPI (JS Promise Integration)

Asyncifyとは異なり、JSPIを使用するにはQtをソースからビルドする必要があります。有効にするには、Qtのconfigureスクリプトに-feature-wasm-jspiおよび-feature-wasm-exceptionsフラグを指定してください。(JSPIは、Emscriptenがデフォルトでエミュレートする例外とは互換性がありません。)

次に、リンカーオプションに -sJSPI フラグを追加して、アプリケーションで JSPI を有効にします:

CMake:

target_link_options(<your target> PUBLIC -sJSPI)

qmake:

QMAKE_LFLAGS += -sJSPI

デバッグとプロファイリング

Wasmのデバッグは、ブラウザのJavaScriptコンソールで行います。Qt Creator 内で直接Wasmアプリケーションをデバッグすることはできません。

Emscriptenのリンカー引数を使用することで、デバッグを容易にするために詳細出力を増やすことができます:

  • -s LIBRARY_DEBUG=1(ライブラリ呼び出しを出力)
  • -s SYSCALL_DEBUG=1(システムコールを表示)
  • -s FS_LOG=1(ファイルシステム操作を出力)
  • -s SOCKET_DEBUG (ソケットおよびネットワークデータ転送を出力)

CMake:

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

qmake:

QMAKE_LFLAGS_DEBUG += -s LIBRARY_DEBUG=1

最適化

WebAssembly 向け Qt の最適化では、Emscripten ツールチェーンを使用してバイナリを生成しますが、パフォーマンスやバイナリのサイズに影響を与える可能性のあるフラグが多数あります。詳細については、「Emscripten: コードの最適化」を参照してください。

通常の C++ アプリケーションと同様に、リンカおよびコンパイラのフラグを指定できます:

target_compile_options(<your target> PRIVATE -oz -flto)
target_link_options(<your target> PRIVATE -flto)
QMAKE_CXXFLAGS += -oz -flto
QMAKE_LFLAGS += -flto

バイナリファイルのサイズを最小限に抑える

シームレスなユーザー体験を提供するためには、WebAssemblyアプリケーションのダウンロードおよび読み込みにかかる時間を短縮することが重要です。アプリケーションバイナリのサイズを小さくすることは、ダウンロードを高速化するための重要な要素の一つです。バイナリサイズを縮小するには、以下の方法を利用してください:

  • 必ずリリースビルドを配布するようにしてください。デバッグビルドにはデバッグシンボルが含まれており、サイズがはるかに大きくなります。
  • サーバー側で圧縮を有効にしてください。gzip やBrotliといった一般的なアルゴリズムはWasmバイナリに有効であり、サイズを大幅に削減できます。
  • バイナリサイズを小さくできるコンパイラやリンカーのフラグ(例: '-os'、'-oz')を試してみてください。結果はアプリケーションによって異なります。
  • ソースから Qt for WebAssembly をコンパイルする際は、未使用の機能を無効にしてください(以下を参照)。
機能の無効化

WebAssemblyアプリケーションはデフォルトでQtライブラリに静的にリンクされるため、コンパイラはデッドコードを削除できます。しかし、Qtの動的な性質上、コンパイラが常にそのような最適化を実行できるとは限りません。

ソースから Qt for WebAssembly をビルドする場合、機能を無効化することで Qt バイナリのサイズを縮小でき、その結果として .wasm バイナリのサイズも縮小できます。 QtはWebAssemblyプラットフォーム向けにデフォルトで一部の機能を無効にしていますが、アプリケーションで使用しない機能については、別途無効にすることも可能です。詳細については、「無効化された機能」を参照してください。

以下の機能を無効にすることで、バイナリサイズを(通常10~15%程度)削減できます:

設定引数概要
-no-feature-cssparserカスケードスタイルシート(CSS)用のパーサー。
-no-feature-datetimeedit日付と時刻の編集(datetimeparserに依存)。
-no-feature-datetimeparser日付・時刻テキストの解析。
-no-feature-dockwidgetQMainWindow 内にウィジェットをドッキングするか、デスクトップ上でトップレベルウィンドウとして浮動表示します。
-no-feature-gesturesジェスチャー用のフレームワーク。
-no-feature-mimetypeMIMEタイプ処理。
-no-feature-qml-networkネットワークの透過性。
-no-feature-qml-list-modelListModel QML タイプ。
-no-feature-qml-table-modelTableModel QML タイプ。
-no-feature-quick-canvasCanvas アイテム。
-no-feature-quick-pathパス要素。
-no-feature-quick-pathviewPathView アイテム。
-no-feature-quick-treeviewTreeView 項目。
-no-feature-style-stylesheetCSS を通じて設定可能なウィジェットスタイル。
-no-feature-tableviewテーブルビューのデフォルトのモデル/ビュー実装。
-no-feature-texthtmlparserHTML用パーサー。
-no-feature-textmarkdownreaderMarkdown(CommonMark および GitHub)リーダー。
-no-feature-textodfwriterODF書き込み機能。

Wasmの例外

Qtはデフォルトで例外サポートなしでビルドされるため、例外がスローされるとプログラムが異常終了します。WebAssemblyの例外を有効にするには、ソースからビルドし、Qtのconfigureに -feature-wasm-exceptions フラグを指定します。これにより、コンパイルおよびリンク時にコンパイラに -fwasm-exceptions フラグが渡されます。 Qtでは、以前のJavaScriptベースの例外実装に対するEmscriptenのサポートを有効にすることはできません。

内部実装上の理由により、例外が有効になっている場合、QApplication::exec() の呼び出しはサポートされていないことに注意してください。代わりに、「アプリケーションの起動とイベントループ」で説明されているように、早期に終了し exec() を呼び出さない形式で main() を記述してください。

共有ライブラリと動的リンク(テクノロジープレビュー)

Qt for WebAssembly はデフォルトで静的リンクを使用しており、アプリケーションは Qt ライブラリとアプリケーションコードを含む単一の WebAssembly ファイルとしてデプロイされます。動的リンクは、各ライブラリとプラグインが個別に配布される代替のビルドモードです。

たとえば、Qt Quick を使用するアプリケーションでは、次のようなライブラリやプラグインが利用される場合があります。

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

6.12 における動的リンクのサポートは、テクノロジープレビュー段階にあります。この実装はプロトタイプ作成や評価には適していますが、本番環境での使用には適していません。現在の制限事項および制約は以下の通りです:

  • マルチスレッドはサポートされていません。
  • Asyncifyはサポートされていません。

ダイナミックリンクでビルドされた Qt アプリケーションでは、バイナリに加えて「qt_plugins.json」および「qt_qml_imports.json」という 2 つの追加ファイルが必要です。これらのファイルは、アプリケーションの起動時に読み込まれる共有ライブラリのリストを指定します。 これらを生成するためのヘルパーツールとして、wasmdeployqt が利用可能です。このツールの使い方を確認するには、--help フラグを指定して実行すると、ツールを実行するために必要なフラグの概要や使用例を確認できます。

アプリケーションをホストするWebサーバーには、Qtの共有ライブラリが用意されている必要があります。これを実現するには、QtのインストールフォルダのコンテンツをWebサーバーにコピーするか、ファイルシステムのシンボリックリンクを作成します。

クイックスタート

ビルドおよびデプロイの手順は、静的 WASM ビルドや共有デスクトップビルドの場合とは若干異なります。本格的なアプリケーションのビルドに進む前に、まずは簡単なサンプルから試してみることをお勧めします。

  1. ソースからQtをビルドし、Qtのconfigureスクリプトに-shared オプションを指定します。' -prefix'オプションを使用して、インストールディレクトリを設定してください。
  2. 手順1で構築したQtを使用して、アプリケーションをビルドします。
  3. アプリケーションのビルドディレクトリからデプロイメントツールを実行し、プラグインのプリロードリストを作成します。
    • <qtpath>/qtbase/bin/wasmdeployqt --qt-wasm-dir=<Path to the Qt for WebAssembly directory> --qml-root-path=<Root directory for QML files> --qt-host-dir=<Path to the Qt host directory>

共有ライブラリのデプロイメントの詳細

Qtの共有ライブラリのビルドは2段階に分けてデプロイされます。第1段階では、QtおよびアプリケーションのビルドをWebサーバーからダウンロード可能にし、第2段階では、アプリケーションの起動時に必要なQtプラグインとQt Quick のインポートをダウンロードします。

最初のステップでは、WebサーバーからQtのインストールパッケージをダウンロードできるようにします。Webサーバーの設定の詳細によっては、これを実現する方法が異なる場合があります。共通点として、Qtローダーは、アプリケーションをロードするHTMLファイルを基準として、「qt」という名前のディレクトリ内にQtライブラリとプラグインが存在することを想定しています。

デプロイの一環としてすでにアプリケーションをWebサーバーにコピーしている場合は、Qtも一緒にコピーするのが一つの選択肢となります。開発段階ではよくあることですが、アプリケーションをビルドディレクトリから直接配信している場合は、Qtへのシンボリックリンクを作成するとうまく機能します。

2番目のステップに備えて、プラグインやQt Quick のインポートなど、Qtコンポーネントのプリロードリストを作成しておきましょう。プリロードを行うことで、アプリケーションの起動時に必要なすべてのQtコンポーネントが確実に利用可能になります。コンポーネントを必要に応じてダウンロードする遅延読み込みも可能ですが、ここでは扱いません。

プリロードは、Qt JavaScriptローダーによって実装されており、このローダーはWebサーバーからEmscriptenが提供するメモリ内ファイルシステムへファイルをダウンロードします。どのファイルをダウンロードするかは、JSON形式のダウンロードリストを使用して指定します。Qtにはプリロードリストを生成するためのツールが用意されています。詳細は上記の「クイックスタート」セクションを参照してください。

既知の問題

  • ネストされたイベントループは、実験的な Asyncify または JSPI 機能を使用する場合にのみサポートされています。
  • 印刷はサポートされていません。
  • QDnsLookup Webサンドボックスのため、ルックアップおよびQSsl は機能せず、サポートされていません。DNSルックアップとSSL証明書の処理はブラウザが行います。DNSルックアップが必要なアプリケーションでは、DNS over HTTPを使用できます。
  • QTcpSockets は使用できますが、すべての POSIX ソケット関数がプロキシされるわけではありません。Websockifyのような WebSocket サーバープロキシを使用する必要があります。
  • すべての Q*Server クラスは、このプラットフォームではサポートされていません。
  • QWebSocket Emscripten では、メインスレッド上でのみ接続がサポートされています。
  • WebAssembly用のQWebSocketsは、Webページやブラウザで利用可能なAPIがこの機能を公開していないため、pingやpongフレームの送信をサポートしていません。
  • QtWebSockets を使用する場合、QtMqtt を使用するには、サブプロトコルを「mqtt」に設定する必要がある場合があります。QWebSocket を開く際は、QWebSocketHandshakeOptions を使用してください。
  • フォント:Wasmサンドボックスでは、システムフォントへのアクセスが許可されていません。フォントファイルは、アプリケーションと一緒に配布する必要があります(例:Qtリソース内やダウンロード形式)。Qt for WebAssembly自体には、そのようなフォントが1つ組み込まれています。
  • Qt Quick のControls 2コンポーネント(チェックボックスなど)の一部で、初期化されていないグラフィックメモリのアーティファクトが発生する可能性があります。これは、HighDPiディスプレイで時々確認されることがあります。
  • Wasmプラットフォームではその機能が提供されていないため、WindowsおよびmacOS向けのネイティブスタイルはサポートされていません
  • 「wasm-ld: error: initial memory too small」などのリンク時エラーが発生する場合は、初期メモリサイズの調整が必要です。QT_WASM_INITIAL_MEMORY を使用して初期サイズを kb 単位で設定してください。この値は 64KB (65536) の倍数でなければなりません。 デフォルトは 50 MB です。CMakeLists.txt では、set_target_properties(<target> PROPERTIES QT_WASM_INITIAL_MEMORY "150MB") と記述します。
  • CMakeLists.txt 内の add_executable を使用しても、<target>.html は生成されず、qtloader.js もコピーされません。代わりに qt_add_executable を使用してください。

その他のトピック

Qt 設定オプションのリファレンス

ソースから Qt for WebAssembly をビルドする際には、以下の configure オプションが関係します。

configure 引数概要
-feature-threadマルチスレッド対応の Wasm。
-feature-wasm-simd128WebAssemblyのSIMDサポートを有効にします。
-feature-wasm-exceptionsWebAssemblyの例外処理をサポートします。
-device-option QT_EMSCRIPTEN_ASYNCIFY=1asyncify のサポートを有効にします。
-device-option QT_EMSCRIPTEN_ASYNCIFY=2asyncify (JSPI) のサポートを有効にします。

Qt では、バイナリサイズを縮小するため、WebAssembly プラットフォーム向けにはデフォルトで一部の機能が無効になっています。Qt for WebAssembly を設定する際に、機能を明示的に有効にすることができます:

設定引数概要
-feature-topleveldomainドメインがトップレベルドメインであるかどうかを検証する機能を提供します。

一般的なダウンロードサイズ

予想されるフットプリント(ダウンロードサイズ):コンパイラによって生成される Wasm モジュールはサイズが大きくなる場合がありますが、圧縮効率は良好です:

例gzipbrotli
helloglwindow (QtCore +QtGui)2.8M2.1M
wiggly widget (QtCore +QtGui +QtWidgets)4.3M3.2M
SensorTag (QtCore +QtGui +QtWidgets +QtQuick + QtCharts)8.6M6.3M

圧縮は通常、標準の圧縮機能を使用してWebサーバー側で処理されます。サーバーは自動的に圧縮を行うか、あらかじめ圧縮されたバージョンのファイルを読み込みます。一般的に、Wasmファイルに対して特別な処理を行う必要はありません。

詳細については、「バイナリサイズの最小化」を参照してください。

例

Webブラウザで動作するQtアプリケーションの例とデモ:Qt Demos

外部リソース

ライセンス

Qt for WebAssemblyは、The Qt Companyから商用ライセンスの下で提供されています。また、GNU General Public License(バージョン3)の下でも利用可能です。詳細については、「Qtのライセンス」をご覧ください。

「 https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS」および「https://developer.mozilla.org/en-US/docs/Web/HTTP/Cross-Origin_Resource_Policy」も参照してください 。

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