An always-on-top desktop overlay that shows StarCraft: Brood War / Remastered build orders from a bundled release-time catalog. Native bundles for Windows, macOS, and Linux. Covers all three races (Terran, Protoss, Zerg) and all nine matchups.
Run StarCraft in Windowed (Fullscreen) mode. True/exclusive fullscreen hides every other window — including this overlay. In Options > Graphics, set the display mode to Windowed (Fullscreen) (sometimes labelled "Borderless Windowed"). The overlay then sits on top of the game correctly.
- All 9 matchups (TvT/TvP/TvZ, PvT/PvP/PvZ, ZvT/ZvP/ZvZ) with race-themed colors.
- Build orders shown as a clean paginated list — no per-step "click next".
- Bundled Liquipedia-derived catalog generated by maintainers at release time.
- Manual builds with personal notes and favorites.
- Search, favorites, compact mode, opacity slider, click-through overlay mode, global hotkeys.
- Built on Tauri 2 + Rust: native installers under ~10 MB. WebView2 / WKWebView / WebKitGTK provide the UI runtime.
- Custom app icon, auto-opening DevTools in dev mode, F12 / Ctrl+Shift+I toggle anywhere.
Just want to use the overlay? You don't need any toolchain.
Open the latest release and pick the right file for your OS:
| OS | Recommended | Alternative |
|---|---|---|
| Windows | BW-Build-Overlay_<version>_x64-setup.exe (NSIS) |
BW-Build-Overlay_<version>_x64_en-US.msi |
| macOS | BW-Build-Overlay_<version>_universal.dmg (Intel + ARM) |
BW-Build-Overlay.app.tar.gz |
| Linux | BW-Build-Overlay_<version>_amd64.AppImage |
bw-build-overlay_<version>_amd64.deb (needs libwebkit2gtk-4.1-0) |
Launch the app. The overlay window stays on top of StarCraft and ships with a bundled build catalog. Open Manager to edit builds, choose favorites, or add your own manual builds.
Set StarCraft to Windowed (Fullscreen). In SC:R, open Options > Graphics and set Display Mode to Windowed (Fullscreen) (a.k.a. Borderless Windowed). True/exclusive fullscreen will hide the overlay; Windowed (Fullscreen) looks identical and lets the overlay stay on top.
- Windows. WebView2 is pre-installed on Windows 10 21H2+ and Windows 11. The installer bundles the WebView2 bootstrapper for older systems. SmartScreen may warn the first time you run the EXE because it isn't code-signed — click "More info" → "Run anyway".
- macOS. The
.appis not code-signed or notarized. Gatekeeper will refuse the first launch. Right-click the app → Open → Open in the dialog, or runxattr -d com.apple.quarantine /Applications/"BW Build Overlay.app"once. - Linux. The AppImage is self-contained —
chmod +xand run. The.debrequireslibwebkit2gtk-4.1-0(sudo apt install libwebkit2gtk-4.1-0).
| Hotkey | Action |
|---|---|
| Ctrl+Alt+1 / 2 / 3 | Player race: Terran / Protoss / Zerg |
| Ctrl+Alt+Q / W / E / R | Opponent: Terran / Zerg / Protoss / Random |
| Ctrl+Alt+B | Next build in current matchup |
| Ctrl+Alt+Shift+B | Previous build |
| Ctrl+Alt+PgDn / PgUp | Next / previous page of long builds |
| Ctrl+Alt+0 | Jump to page 1 |
| Ctrl+Alt+F | Toggle favorite on current build |
| Ctrl+Alt+C | Toggle compact mode |
| Ctrl+Alt+L | Toggle overlay mouse click-through |
| Ctrl+Alt+K | Toggle overlay reposition mode |
| Ctrl+Alt+H | Hide / show overlay |
/ (in overlay) |
Focus the build search |
| F12 / Ctrl+Shift+I | Toggle DevTools (debug log) |
Click Manager to edit the catalog and app behavior. Tabs:
- Edit - manually create or edit a build (id, race, opponent, difficulty, tags, steps, notes). Saving marks edited builds as
customEditedso future bundled catalog refreshes won't overwrite your changes. - Settings - compact mode, window opacity, game-safe click-through, overlay snap-to-corner controls, app update checks/install, page size, default race, and the bundled catalog's last sync date.
If something looks broken (overlay frozen, click does nothing, data not loading, etc.), open DevTools to see what's happening:
- Press
F12orCtrl+Shift+Ianywhere in the app — DevTools toggles in any build. - Set the env var
BW_DEVTOOLS=1before launching to auto-open DevTools on startup. Useful when filing bug reports:# Windows $env:BW_DEVTOOLS = "1" & "$env:ProgramFiles\BW Build Overlay\BW Build Overlay.exe"
# macOS / Linux BW_DEVTOOLS=1 "BW Build Overlay"
- The Console tab shows logs (
[bw-overlay] renderer bootedconfirms the renderer started). - If the renderer fails to boot, a red banner appears at the top of the overlay window with the error.
When filing a bug, please attach the DevTools console output as a screenshot or copy-paste.
Your builds, settings, and backups live in your OS-specific user-data folder. Tauri uses the bundle identifier (com.local.bwbuildoverlay) for the directory name:
| OS | Path |
|---|---|
| Windows | %APPDATA%\com.local.bwbuildoverlay\ |
| macOS | ~/Library/Application Support/com.local.bwbuildoverlay/ |
| Linux | ~/.local/share/com.local.bwbuildoverlay/ (or $XDG_DATA_HOME/...) |
Each folder contains:
builds.json— your build library.settings.json— overlay opacity, rate limit, default race, etc.builds-backup-*.json— timestamped backups produced by Manage > Backup builds.json.
App updates replace the application binaries but keep this user-data directory intact. Your manual builds, customEdited entries, favorites, and personal notes remain in place across updates.
Use Manage > Open data folder to open the directory in your file manager.
- Node 24 LTS (the repo includes a
.nvmrc;nvm usewill pick the right version on systems with nvm or nvm-windows). - npm 11+ (ships with Node 24).
- Rust 1.85+ via
rustup— therust-toolchain.tomlat the repo root pinsstableand addsclippy+rustfmtautomatically on firstcargoinvocation. - OS-specific Tauri build deps:
- Windows: Microsoft Visual C++ Build Tools (Workload "C++ build tools" — installed by
rustup-initon Windows when prompted). WebView2 is pre-installed on Win10 21H2+ and Win11. - macOS: Xcode Command Line Tools (
xcode-select --install). - Linux:
libwebkit2gtk-4.1-dev,libgtk-3-dev,libsoup-3.0-dev,libjavascriptcoregtk-4.1-dev,libayatana-appindicator3-dev,librsvg2-dev. See the Tauri prereqs page for the apt/dnf/pacman one-liner.
- Windows: Microsoft Visual C++ Build Tools (Workload "C++ build tools" — installed by
Verify:
node --version # v24.x
npm --version # 11.x or newer
rustc --version # 1.85 or newer
cargo --versiongit clone https://github.com/Etra-0/starcraft-build-overlay.git
cd starcraft-build-overlay
npm install
npm run devnpm run dev (which is just npm run tauri:dev):
esbuild --watchrebuilds the renderer bundle intodist-frontend/on everysrc/renderer/change.npx tauri devbuilds and launches the Rust backend withBW_DEVTOOLS=1so DevTools auto-opens.
Renderer changes hot-reload on save. Rust changes trigger a recompile + relaunch (Tauri handles this automatically).
| Script | What it does |
|---|---|
npm run dev / npm run tauri:dev |
Watch builds + Tauri with DevTools auto-open. |
npm run dev:renderer |
Just the esbuild watch for the renderer bundle (writes to dist-frontend/). |
npm run build:renderer |
One-shot renderer build → dist-frontend/renderer.js + static assets. |
npm run tauri:build |
Production build of the renderer + Tauri bundle for the host OS. |
npm run dist |
Alias for npm run tauri:build (NSIS/MSI on Windows, DMG/.app on macOS, AppImage/.deb on Linux). |
npm run dist:win / :mac / :linux |
Build only the bundles for that OS family (still has to run on the corresponding host — Tauri does not cross-compile bundles). |
npm run typecheck |
TypeScript type-only check for the renderer (tsconfig.renderer.json). |
npm run lint / npm run lint:fix |
ESLint (flat config; eslint.config.mjs selects **/*.ts). |
npm run format / npm run format:check |
Prettier write / check. |
npm test |
cargo test --manifest-path src-tauri/Cargo.toml - runs Rust unit and fixture tests (storage, parser, importer, utils). |
npm run scrape |
Maintainer-only catalog refresh. Rebuilds data/builds.json and data/scrape-coverage.json from Liquipedia with rate limiting. |
npm run clean |
Delete dist-frontend/ and src-tauri/target/. |
npm run size |
Print the size (MB) of the latest bundle artifacts under src-tauri/target/release/bundle/. |
npm run release:patch | minor | major |
Bump version (package.json, Cargo.toml, tauri.conf.json), rotate CHANGELOG.md, refresh Cargo.lock, commit, and tag (see Releasing). |
src/renderer/*.ts ──esbuild──> dist-frontend/renderer.js (single ESM bundle)
index.html, styles.css ─────cp─────> dist-frontend/ (static assets)
assets/icon.png ─────cp─────> dist-frontend/
src-tauri/src/*.rs ──cargo build──> src-tauri/target/release/bw-build-overlay
src-tauri/icons/* ──tauri bundle──> src-tauri/target/release/bundle/
├── nsis/ (Windows .exe installer)
├── msi/ (Windows .msi)
├── dmg/ (macOS .dmg)
├── macos/ (macOS .app)
├── appimage/ (Linux .AppImage)
└── deb/ (Linux .deb)
The frontendDist field in src-tauri/tauri.conf.json points at ../dist-frontend. Tauri reads HTML/JS/CSS from there at runtime in dev, and embeds them into the resulting bundle in release. There's no Chromium download — the OS-bundled WebView (WebView2 on Windows, WKWebView on macOS, WebKitGTK on Linux) renders the UI.
The renderer is bundled (rather than just transpiled) because the WebView runs the renderer in a sandboxed browser context with no Node integration — require() / import.meta.url to filesystem paths aren't available. esbuild produces one self-contained ESM file.
The dist-frontend/ and src-tauri/target/ folders are git-ignored and regenerated on every build:
npm run clean # deletes dist-frontend/ and src-tauri/target/
npm install # if you blew away node_modules too
npm run tauri:buildThe bundle artifacts under src-tauri/target/release/bundle/<format>/ are your shippable outputs.
Versioning follows Semantic Versioning: MAJOR.MINOR.PATCH.
The single source of truth is the version field in package.json. The release script propagates it to:
src-tauri/Cargo.toml[package].version(andCargo.lock).src-tauri/tauri.conf.json.version.- The GitHub Release tag (
vX.Y.Z). - Bundle filenames (
BW-Build-Overlay_X.Y.Z_x64-setup.exe,BW-Build-Overlay_X.Y.Z_universal.dmg, etc.). - The default User-Agent the maintainer scrape helper sends (sourced from
CARGO_PKG_VERSIONat compile time).
- Edit
## [Unreleased]in CHANGELOG.md with the user-visible changes for this version (Added / Changed / Fixed / Removed). - Run one of:
This script bumps
npm run release:patch # 1.0.0 -> 1.0.1 (bug fixes) npm run release:minor # 1.0.0 -> 1.1.0 (new features, backwards compatible) npm run release:major # 1.0.0 -> 2.0.0 (breaking changes / data migrations)
package.json+Cargo.toml+tauri.conf.json, refreshesCargo.lock, rotates the changelog (Unreleasedbecomes[X.Y.Z] - YYYY-MM-DDand a freshUnreleasedblock is added on top), commits, and tagsvX.Y.Z. Use--dry-runto see what would happen without changing anything. - Push:
git push origin main --follow-tags
- Ensure updater signing secrets are configured in GitHub Actions:
TAURI_SIGNING_PRIVATE_KEYTAURI_SIGNING_PRIVATE_KEY_PASSWORD(optional when key has no password)BW_UPDATER_PUBKEY(public key content used by runtime updater checks)
- The Release workflow is a
windows-latest×macos-latest×ubuntu-latestmatrix that runstauri-apps/tauri-action@v0. Each runner produces its native bundles (NSIS/MSI on Windows, universal DMG on macOS, AppImage/.deb on Linux) and uploads them to a draft GitHub Release namedBW Build Overlay <tag>. Edit the body and publish when ready.
The installed app does not contact Liquipedia. Maintainers refresh the bundled catalog before release:
npm run scrapeThat command updates data/builds.json and data/scrape-coverage.json. Releases now skip scraping by default; run npm run release:patch|minor|major -- --scrape when you intentionally want a fresh Liquipedia snapshot in that release. On app launch, a newer bundled catalog is merged into the user's local builds.json while preserving favorites, personal notes, recently-used state, and builds marked customEdited.
CI runs in two stages on every PR and push to main (ci.yml):
- A
qualityjob (Ubuntu) runsformat:check,lint,typecheck, pluscargo fmt --check,cargo clippy -- -D warnings, andcargo test. - A
packagematrix (windows-latest+macos-latest+ubuntu-latest) builds the renderer and runscargo build --testsso platform-specific Rust regressions surface on PRs without paying for the full bundle build.
Dependency upgrades come in via Dependabot on a weekly cadence.
src/
shared/
types.ts OverlayAPI IPC contract + Build/Settings/Race types
utils.ts pure helpers (deriveMatchup, slugify, splitStep, ...)
renderer/
index.ts renderer entry (bundled by esbuild into one ESM file)
api.ts @tauri-apps/api invoke/listen wrapper implementing OverlayAPI
state.ts in-memory state + localStorage
dom.ts typed element refs (Dom interface, byId<T> helper)
toast.ts non-blocking notifications
overlay.ts overlay rendering + paging
manager.ts Manager modal, build list
edit-tab.ts Edit tab form
settings-tab.ts Settings tab UI
hotkeys.ts hotkey-event -> handler dispatch (typed HotkeyAction union)
src-tauri/
Cargo.toml Rust crate manifest, deps + release profile
tauri.conf.json window, identifier, bundle targets, CSP, frontendDist
capabilities/ Tauri 2 permission manifests
icons/ generated platform icons (don't hand-edit)
src/
main.rs binary entry point (calls run() from lib.rs)
lib.rs Builder, plugin registration, app state, hotkey/window setup
commands.rs #[tauri::command] handlers — one per OverlayAPI method
storage.rs builds.json + settings.json I/O, schema migration
window.rs opacity, always-on-top keeper, devtools toggle, global shortcut registration
types.rs serde structs mirroring src/shared/types.ts
utils.rs Rust port of src/shared/utils.ts (deriveMatchup / slugify / splitStep)
liquipedia/
api.rs rate-limited reqwest MediaWiki client
parser.rs {{build}} template parser, infobox, counters
import.rs release-time page import and merge logic
updates.rs revision check helpers used by maintainer tooling/tests
dist-frontend/ esbuild + static-asset output [git-ignored]
src-tauri/target/ cargo build output [git-ignored]
data/builds.json seed library (all 9 matchups), embedded as a Tauri resource
assets/icon.png icon source (icons/ are generated from this)
scripts/ build-renderer.mjs, dev.mjs, release.mjs, report-size.mjs
index.html entrypoint that loads ./renderer.js
styles.css
tsconfig.renderer.json typecheck config for the renderer (ESM, DOM)
rust-toolchain.toml pins stable + clippy + rustfmt
The release-time scraper parses Liquipedia's {{build|...}} template directly (it looks for *8 - Pylon style bullet lines inside the template). Supports:
- Multi-variant pages (each section's
{{build}}becomes a separate build, linked viavariantOf). {{Infobox strategy}}for race / matchups / creator / popularizer.==Counter To==and==Countered By==lists (shown at the bottom of the build card).- Difficulty tags from
Category:* Beginner/Intermediate/Advanced Strategy. - Per-build
revisionIdtracking in the checked-in catalog for auditability.
The scraper respects Liquipedia's rate-limiting (default 2300 ms between requests). Use BW_LIQUIPEDIA_USER_AGENT or npm run scrape -- --user-agent=... with a contact-able User-Agent when refreshing the checked-in catalog.
The icon source is assets/icon.png (square, ≥1024×1024). Tauri's CLI generates all platform-specific outputs from it:
npx tauri icon assets/icon.pngThis regenerates everything under src-tauri/icons/ — Windows .ico, macOS .icns, multiple PNG sizes, and Android/iOS variants. Tauri picks the right one for each bundle target automatically.
Each build:
Schema migrations run automatically in src-tauri/src/storage.rs (SCHEMA_VERSION constant; currently 4).
| Symptom | Likely cause | Fix |
|---|---|---|
| Overlay disappears the moment StarCraft starts | StarCraft running in true/exclusive fullscreen | Options > Graphics > Display Mode = Windowed (Fullscreen). Always-on-top windows can't sit above an exclusive-fullscreen Direct3D surface. |
| Overlay drops behind the Windows taskbar after clicking it | Known Tauri 2 always-on-top regression | The app re-applies always-on-top on focus loss; if it persists, hit Ctrl+Alt+H twice to toggle, or relaunch. |
npm run tauri:dev fails with "linker link.exe not found" |
MSVC C++ Build Tools missing on Windows | Re-run rustup-init and accept the MSVC toolchain prompt, or install Visual Studio's "Desktop development with C++" workload manually. |
npm run tauri:dev fails with "webkit2gtk not found" on Linux |
Missing Tauri prereqs | sudo apt install libwebkit2gtk-4.1-dev libgtk-3-dev libsoup-3.0-dev libjavascriptcoregtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev (or the dnf/pacman equivalent). |
| macOS launch is blocked: "BW Build Overlay" can't be opened | Unsigned .app quarantined by Gatekeeper |
Right-click → Open → Open, or xattr -d com.apple.quarantine /Applications/"BW Build Overlay.app". |
| Overlay opens but clicks do nothing | Renderer bundle stale (rebuilt Rust but not renderer) | npm run build:renderer and relaunch. |
| Built bundle launches and closes instantly | Crash on startup | Set BW_DEVTOOLS=1 and re-launch — DevTools console will show the error. |
| GitHub release didn't get bundles for one of the OSes | One matrix runner failed | Open the failed Release workflow run and read the build log for that runner. |
MIT — see LICENSE.
- File bugs / feature requests in the GitHub issue tracker.
- Include OS, version, reproduction steps, expected behavior, and screenshots when relevant.
- For renderer issues, please attach the DevTools console (F12) output.
{ "id": "pvt-1-gate-core", "race": "Protoss", // player race: Terran | Protoss | Zerg "opponent": "Terran", // Terran | Zerg | Protoss | Random "matchup": "PvT", // derived "name": "1 Gate Core", "variantOf": null, // parent build id when this came from a multi-variant page "tags": ["standard", "starter"], "difficulty": "beginner", // beginner | intermediate | advanced | null "sourceName": "Liquipedia", "sourceUrl": "https://liquipedia.net/starcraft/1_Gate_Core_(vs._Terran)", "sourcePageTitle": "1 Gate Core (vs. Terran)", "notes": "...", // catalog description "userNotes": "", // your own notes; survives bundled catalog refreshes "customEdited": false, // true => bundled catalog refreshes skip this build "favorite": false, "recentlyUsedAt": null, "revisionId": 12345, // Liquipedia revision id captured by maintainer scraper "lastImportedAt": "...", "lastCheckedAt": "...", "counters": ["..."], "counteredBy": ["..."], "steps": ["8 - Pylon", "10 - Gateway", "..."] }