A local-first infinite whiteboard that lives on your Windows desktop.
Notes, sketches, and diagrams behind your desktop icons — without replacing Explorer or switching to a separate editor.
Status: early preview (0.1.0). The whiteboard, persistence, window modes, and browser fallback are tested. The behind-the-icons desktop canvas is implemented and manually exercised, but remains experimental — see Known limitations. No signed installer or binary release is published yet; build from source.
Underlay embeds the MIT-licensed Excalidraw engine in a Tauri 2 desktop shell, adds local persistence and recovery, and can reparent its own WebView2 window behind the desktop icons. The same engine and the same board are used in every presentation mode; there is no second canvas and no wallpaper screenshot.
- Infinite canvas with pan/zoom, freehand pen, a highlighter action, shapes, lines, and arrows bound to elements.
- Sticky notes and direct text editing, selection, move, resize, rotate, group, duplicate, and delete.
- Undo/redo, clipboard text/image paste, Excalidraw scene import/export, Underlay board import/export, and PNG export.
- Multiple boards with rename, duplicate, delete-to-trash, and a board library.
- Local autosave with atomic writes, previous-version backups, rotating snapshots, interrupted-save recovery, and corrupt-file fallback.
- Dark bluish-black default canvas with light/dark UI themes, five board backgrounds, and five muted sticky palettes.
- Windows presentation modes: experimental desktop canvas behind icons, a normal window, and a legacy always-on-bottom mode.
- Desktop integration: tray menu, single instance, opt-in launch at login,
global shortcuts, and a
--safe-modefallback. - Browser preview over localStorage for frontend development.
Choose Canvas under Settings → “When Underlay starts”, or press Ctrl+Alt+W.
The Tauri window and its WebView2 child are reparented under the desktop
WorkerW, below SHELLDLL_DefView and therefore below the icons.
- Click notes and empty canvas to select, drag, draw, and type. Desktop icons, the taskbar, Start, and ordinary apps stay usable above the canvas.
- Right-click empty canvas for the Underlay menu.
Alt+right-click opens the real Windows desktop menu. Right-clicking an element shows Excalidraw's element menu. Ctrl+Alt+Xpauses canvas input so every click reaches the shell or an app.Ctrl+Alt+Breturns to the normal window.- If shell icon geometry cannot be read, input passes through in the icon area rather than guessing positions.
--safe-mode(orUNDERLAY_SAFE_MODE) disables the desktop canvas.
The desktop canvas currently targets the primary monitor. The legacy always-on-bottom window mode remains available as a fallback.
- Windows 10/11. Development and verification used Windows 11 build 26300; Windows 10 is untested.
- Microsoft Edge WebView2 Runtime (normally present on Windows).
- Node.js 22+ and npm.
- Rust stable with the MSVC toolchain and Visual Studio C++ build tools.
- Git Bash for
scripts/env.shand the test harnesses.
git clone https://github.com/spaxe-dev/underlay.git
cd underlay
source scripts/env.sh # Git Bash: keeps caches and build output inside the repo
npm ci
npm run desktop:dev # Vite + the native Tauri windowFor a browser-only preview (localStorage instead of disk storage):
source scripts/env.sh
npm run devscripts/env.sh is optional. It redirects TEMP, the npm cache, CARGO_HOME,
CARGO_TARGET_DIR, the WebView2 profile, and development data to gitignored
folders inside the repository. Without it, npm and Cargo use their normal
locations.
| Shortcut | Action |
|---|---|
Ctrl+S |
Save now and show confirmation |
Ctrl+, |
Open settings |
Ctrl+Alt+B |
Show/hide the canvas, or return from the desktop canvas (global) |
Ctrl+Alt+W |
Toggle the desktop canvas behind icons (global) |
Ctrl+Alt+X |
Pause/resume canvas input, or toggle legacy click-through (global) |
Ctrl+Alt+D |
Toggle legacy always-on-bottom desktop mode (global) |
The highlighter button in the top bar (also in the app menu) selects a wide, translucent yellow freehand stroke.
flowchart TB
subgraph Web["React + TypeScript"]
UI["Underlay UI and board library"]
Engine["Excalidraw 0.18.1 engine"]
end
subgraph Native["Tauri 2 / Rust"]
Store["Board store: JSON, backups, trash, settings"]
Surface["Desktop surface host: WorkerW + WebView2"]
Input["Scoped input bridge and focus proxy"]
end
UI --> Engine
UI --> Store
UI --> Surface
Surface --> Input
- The frontend owns the single Excalidraw engine and the application shell.
- The Rust store validates paths and names, writes boards atomically, keeps previous-version backups and snapshots, and falls back to the last good file.
- The desktop surface saves the window's parent/style/rectangle, reparents the
existing Tauri window under
WorkerW, routes only desktop-level mouse input, reads icon geometry through the shell, and restores everything on detach. - The normal window and legacy always-on-bottom mode bypass the desktop surface entirely.
There is no Explorer injection, no global keyboard hook, and no permanent low-level mouse hook: mouse routing is scoped to the desktop surface and removed when it detaches.
More detail: docs/ARCHITECTURE.md and docs/DESKTOP_SURFACE_POC.md.
Underlay is local-only. It has no telemetry, accounts, or runtime network services; Excalidraw fonts and locales are served from local files.
Packaged builds resolve the data folder in this order:
UNDERLAY_DATA_DIR, if set.D:\Underlay, ifD:exists. An existing pre-renameD:\Vanvasfolder is reused in place.%LOCALAPPDATA%\Underlay, or an existing%LOCALAPPDATA%\Vanvas.
Development uses <repo>/userdata when scripts/env.sh is sourced. Boards are
versioned JSON named <id>.underlay.json under boards/, with backups/,
trash/, logs/, exports/, and settings beside them. Deletes go to trash/;
nothing is silently erased.
Underlay was previously prototyped as Vanvas. Existing Vanvas boards
(.vanvas.json), previous-version backups, browser localStorage keys, and
VANVAS_* environment variables are still read, and legacy boards are saved in
place.
source scripts/env.sh
npm run typecheck
npm run lint
npm test # 35 Vitest tests
npm run test:rust # 15 Rust store tests
npm run build # production frontend
npm run desktop:build # release executable + unsigned NSIS installerEnd-to-end harnesses:
# Browser: requires `npm run dev` in another terminal.
npm run e2e
# Desktop: requires an unlocked interactive Windows session.
npm run desktop:e2e
npm run desktop:surfaceThe current tree passes typecheck, lint, 35 unit tests, 15 Rust tests, 47 browser checks, and the production frontend build. The desktop harnesses drive the real WebView2 through CDP and verify Win32 window styles; their last complete run predates the Underlay rename. Release smoke and the opt-in Explorer-restart phase are not yet run against the renamed build.
Build outputs live under build/ (gitignored). The NSIS installer is unsigned
and has not been installed on a clean machine.
- Board format: versioned JSON (
schemaVersion: 1). - Import accepts
.underlay, legacy.vanvas,.json, and.excalidrawfiles. - Existing settings, backups, trash, and browser preview data are read across the rename.
UNDERLAY_SAFE_MODEand the legacyVANVAS_SAFE_MODEboth disable the desktop canvas.
- Windows only, with the desktop canvas on the primary monitor. Mixed-DPI, monitor removal, sleep/lock, IME, touch, and pen behavior are unverified.
- Explorer restart and wallpaper conflicts are implemented or documented but
not verified live. On builds with no
WorkerWhost, Underlay sends the undocumented0x052Cmessage toProgmanonce per attach; that path has not been exercised by a real Explorer restart. - No published binary or signed installer. Build from source; the installer is unsigned and untested on a clean machine.
- Excalidraw font notices must be collected before distributing built binaries. The fonts are copied from npm at build time and are not stored here.
- Packaged CSP is still Tauri's default and needs hardening.
- Rotation, clipboard paste, and image insertion come from Excalidraw but are not individually automated in this repository's tests.
- No auto-updater.
Issues and pull requests are welcome. Keep the app local-first (no telemetry or required network services), run the checks above before opening a PR, and avoid vendoring GPL-licensed code. See THIRD_PARTY_NOTICES.md for dependency obligations.
MIT © 2026 Aadit (spaxe-dev)
Underlay builds on MIT/Apache-2.0 dependencies; see THIRD_PARTY_NOTICES.md and docs/DEPENDENCY_AUDIT.md. Excalidraw is MIT-licensed and remains the copyright of its contributors.