Wordle word clouds, reimplemented in C++ / WebAssembly / WebGL2.
Live demo —
this exact cloud, interactive: 🔄 re-layouts, with undo/redo. A release build
deployed to GitHub Pages by CI on every push to
main. (GitHub READMEs can't run WebAssembly, so the image above links to the
real thing.)
./dev # configure (first time) + build + start dev serverthen open / reload http://localhost:8787/. That's the whole loop: edit,
./dev, reload. In VS Code, the default build task (⇧⌘B) runs the same thing,
and the "Run words (Chrome)" launch config builds and opens Chrome with the
debugger attached.
./dev release builds and serves an optimized build instead.
- CMake ≥ 3.25 + Ninja, driven by
CMakePresets.json(wasm-debug,wasm-release). - vcpkg in manifest mode (
vcpkg.json), tripletwasm32-emscripten, chainloading the Emscripten toolchain. Add dependencies tovcpkg.json; the next configure installs them. - Emscripten via emsdk, expected at
~/emsdk(override with$EMSDK). vcpkg is expected at~/vcpkg(override with$VCPKG_ROOT).
FreeType (font loading, glyph outlines), HarfBuzz (shaping/kerning), and
Clipper2 (polygon booleans), all built for wasm32-emscripten by vcpkg. Two
local workarounds, both of which should be revisited on toolchain updates:
triplets/overlays the community triplet so the chainloaded toolchain istriplets/emscripten-chainload.cmake(Emscripten's toolchain plus extra compile flags — the stock triplet's direct chainload silently dropsVCPKG_C(XX)_FLAGS). Currently it definesHB_NO_PRAGMA_GCC_DIAGNOSTIC_ERROR, because emsdk's clang 23 fires-Wunused-templateinside HarfBuzz's headers, which HarfBuzz's own pragmas promote to errors.ports/harfbuzzoverlays the stock port to pass-Dutilities=disabled; the hb-gpu-* utilities fail to link on wasm (mixed-pthreadobjects) and we only need the library.
The default font (Typodermic's Sexsmith, CC0 public domain) is preloaded from
assets/fonts/ into the Emscripten virtual FS; the rest of the original
Wordle's font collection (see assets/fonts/README.md) is served statically and
fetched on demand via ?font=<basename>. All text geometry is shaped and
extracted in integer font units (HarfBuzz scale pinned to upem,
FT_LOAD_NO_SCALE) and transformed to screen space exactly once, at draw time.
src/— C++ sources.web/— static harness (HTML/CSS/JS); copied into the build'sdist/by the build, so it goes through the same./dev+ reload loop.tools/serve.py— dev server (no-store caching, COOP/COEP for future wasm-threads support).build/<preset>/dist/— everything the browser loads.
Two tiers of golden-image tests:
- Geometry approvals
(
cmake --preset host-test && ctest --preset host-test): the GL-free world model builds natively and serializes the demo scene to SVG; ApprovalTests compares againsttests/goldens/geometry/*.approved.svg. The approved files are viewable images — open them in a browser. On a mismatch, inspect the.receivedfile and rename it over the.approvedone to bless. Runs in the presubmit whensrc/ortests/changed. - SwANGLE e2e (
tools/e2e-golden.sh): renders the real wasm release build in headless Chrome on SwiftShader-backed ANGLE — CPU rasterization, byte-deterministic for a fixed browser version — at a frozen scene time (?t=<seconds>URL parameter), and byte-compares the screenshot againsttests/goldens/e2e/. The browser is a pinned Chrome for Testing build (version intools/chrome-version.txt, auto-downloaded to~/.cache/words/chromebytools/get-chrome.sh), so system browser updates can't shift pixels.--blessapproves the current rendering; bump the pin and re-bless deliberately.
tools/bench.sh # all benchmarks
tools/bench.sh --benchmark_filter=BM_BuildWord # google-benchmark args pass throughGoogle Benchmark compiled to wasm and run under node — the same V8 that runs the
app in Chrome, so these are wasm runtime numbers, not native ones. Built only in
the wasm-release preset (vcpkg feature bench); sources in bench/.
./presubmit.sh (run automatically by jj push-main; skip with
SKIP_PRESUBMIT=1) enforces include hygiene via clang-tidy's
misc-include-cleaner: unused #includes fail the push. Fix a report
automatically with:
tools/include-cleaner.sh --fixIn-editor, .clangd enables the same analysis (strict unused/missing include
diagnostics) with quick-fixes.
Debug builds emit DWARF and a source map. With Chrome's C/C++ DevTools Support extension you can set breakpoints in C++ from DevTools.
