Virtastic Write - We compiled QGIS Desktop to WebAssembly (Read-Only)

We compiled QGIS Desktop to WebAssembly

How we took the full QGIS desktop GIS, Qt Widgets and GDAL and all, into the browser, what got better, and what it teaches about modernizing desktop apps.

QGIS is not a small app. It is a full professional geographic information system: millions of lines of C++, a deep Qt Widgets UI, and a stack of heavyweight native dependencies. It is exactly the kind of software the industry says you “have to rewrite” to get onto the web.

We didn’t rewrite it. We recompiled it. QGIS Desktop 3.40 LTR runs 1:1 in the browser. The real qgis_core, qgis_gui and qgis_app, the same project files, the same tools, with nothing to install. This post walks through how it was done, what actually improved, and what it means if you have a desktop app of your own.

How it was done

The engine is Emscripten, the same C++ to WebAssembly toolchain behind Photoshop, AutoCAD and Figma on the web. Our build pins Emscripten 3.1.70 with Qt 6.9.0’s multithreaded WebAssembly platform, the toolchain we proved on the sibling FreeCAD port.

The honest truth about a port like this is that the application is the easy part. The mountain is underneath it: GDAL, PROJ, GEOS, Qt itself, and dozens of transitive libraries, every one of which has to be cross-compiled to WebAssembly as a static library before QGIS will even link. This is where our maintained dependency library earns its keep. Most of those libraries were already built, verified and current, so the port started most of the way up the hill instead of at the bottom.

Three engineering decisions worth understanding:

  1. Patches stay tiny and upstream-friendly. Every divergence from stock QGIS lives behind #ifdef __EMSCRIPTEN__ guards, kept as a clean patch series against the exact upstream release tag. Desktop behavior is untouched, and when upstream ships a new version, we rebase a small, reviewable set of patches instead of maintaining a fork. That is what makes the port maintainable, not just possible.
  2. Real threads in the browser. QGIS expects worker threads for rendering and processing. WASM threads run on SharedArrayBuffer, which browsers only enable behind cross-origin isolation, so the app is served with the COOP/COEP headers that unlock it. This is standard for every serious port we do.
  3. The GUI is the GUI. Qt for WebAssembly renders the same widget tree to a canvas. We did not rebuild panels in React or “webify” the interface. A QGIS user sees QGIS.

What actually got better

A 1:1 port sounds like “the same, but in a tab.” Operationally it is a different product:

  • Deployment became a URL. No installers, no per-OS packaging, no version skew across the team. Everyone runs the same build the moment it is published; a fix ships once and reaches every user on next load.
  • It runs on anything with a browser. Managed corporate desktops where users can’t install software, Chromebooks, tablets, a colleague’s laptop. The barrier to trying and using the tool drops to clicking a link.
  • No VDI or streaming middle layer. The app executes client-side on the user’s own machine. There is no session server rendering frames, no per-seat streaming license, no latency between click and response.
  • A platform for the web layer. Once the app lives in the browser, cloud project storage, SSO, usage monitoring and real-time collaboration become additive features on top of the port, not a second rewrite. That is the modernization pay-off: the port is the foundation, the web capabilities are the compounding return.

How this maps to your app

The recipe generalizes, and that is the point of publishing these write-ups:

  1. Inventory the native dependencies; match them against what is already ported.
  2. Cross-compile the app with minimal, guarded patches. Never fork.
  3. Map the desktop’s assumptions (threads, filesystem, GPU, audio) onto browser primitives.
  4. Ship behind an isolated, monitored web stack; then layer on storage, auth and collaboration.

The QGIS port is in preview. It is not released yet and there is no public build to open today, but it is now listed on the demos page as a preview. The playable build lands at that same link the day it is ready to share.

If you want to see the approach working right now, our OpenMW port is released and you can play it in a browser tab this second. Same pipeline, same dependency library, same hosting stack.

If your product, or the ancient internal tool your team depends on, is C++, Qt, or a native stack like it, this same path applies. Read the source on GitHub, or book a meeting and we’ll scope what your app looks like in a browser tab.

Keywords: WebAssembly, QGIS, Qt, modernization

Have a desktop app that belongs in the browser? We recompile legacy desktop software to WebAssembly, then maintain and host it. Book a call.

What next: Open the apps · More posts · Contact us

Page 1   Sec 1   1/1 At 1"   Ln 1   Col 1 RECTRKEXTOVRWPH