Repository navigation
Development And Contributing
This page is for developers and contributors who want to run Local VTT locally, understand the project structure, test changes, or prepare releases.
For user-facing setup, see Getting Started.
Local VTT is an Electron desktop app with a React renderer and Canvas 2D scene rendering.
The app is local-first and stores campaign data in portable local folders.
The runtime is split into:
- Electron main process code.
- A secure preload bridge.
- Shared TypeScript models and validation.
- React GM View.
- React Player View.
- Canvas-based scene rendering.
Development requires:
- Node.js.
- npm.
- Git.
The release workflow currently uses Node 24 in GitHub Actions.
From the project root, install dependencies:
npm installAfter install, Local VTT runs scripts/ensure-electron.mjs through postinstall to verify or repair the Electron binary. This should create node_modules/electron/path.txt when npm lifecycle scripts are enabled.
If Electron did not install correctly, run:
npm run electron:installIf path.txt is still missing, check whether npm lifecycle scripts are disabled:
npm config get ignore-scriptsnpm install may report deprecation warnings from transitive Electron packaging dependencies, such as rimraf, inflight, glob, or boolean. To see why one is installed, run:
npm explain rimraf
npm explain inflight
npm explain glob
npm explain booleanStart the app in development mode:
npm run devThis builds the Electron files, starts the Vite dev server, waits for the local dev URL, and launches Electron.
Linux developers can test Electron's Ozone platform and Vulkan flags when diagnosing Wayland, X11, compositor, or GPU driver behavior:
LOCALVTT_OZONE_PLATFORM=wayland npm run dev
LOCALVTT_OZONE_PLATFORM=x11 npm run dev
LOCALVTT_OZONE_PLATFORM=auto npm run dev
LOCALVTT_ENABLE_VULKAN=1 npm run dev
LOCALVTT_DISABLE_VULKAN=1 npm run devThese options are opt-in because the best combination depends on the compositor, GPU driver, and Electron/Chromium version. LOCALVTT_DISABLE_VULKAN=1 takes precedence when both Vulkan variables are set.
Build renderer and Electron files:
npm run buildThis runs TypeScript checks, builds Electron files, and builds the Vite renderer output.
Run ESLint:
npm run lintRun the Vitest suite:
npm testRun the full local verification pass:
npm run checkThis runs TypeScript typechecking, ESLint, and Vitest tests.
After a production build, run the Electron smoke test:
npm run smoke:electronThe smoke test builds the app, launches the production Electron entrypoint, verifies the GM preload bridge, opens Player View through IPC, sends a Player View idle state, and checks Electron display enumeration.
To create a Windows package:
npm run package:winSuccessful Windows packaging creates output in the release/ folder, including the installer, blockmap, update metadata, and unpacked app.
To create a macOS package:
npm run package:macmacOS signing and notarization are not currently configured.
To create Linux packages:
npm run package:linuxLinux packaging produces AppImage, Debian package, and RPM artifacts.
Important project areas include:
electron/
main.ts
preload.ts
persistenceCodecs.ts
assetProtocol.ts
src/
shared/
renderer/
src/renderer/
views/
components/
canvas/
hooks/
lib/
styles/
docs/
architecture.md
canvas-performance-budget.md
codebase-audit.md
layer-ownership-rules.md
release-process.md
tests/
electron/
renderer/
shared/
The Electron main process handles:
- Application lifecycle.
- Secure window creation.
- Campaign folder IO.
- Asset import and copy operations.
- Metadata backups.
- Player View window control.
- Native filesystem interactions.
Key files include:
electron/main.tselectron/assets.tselectron/assetProtocol.tselectron/persistenceCodecs.tselectron/metadataErrors.ts
The preload bridge exposes a typed API to the renderer through Electron's contextBridge.
The renderer does not receive unrestricted filesystem access.
Key file:
electron/preload.ts
Shared TypeScript code defines campaign and scene models, validation, defaults, schema normalization, and Player View-safe scene projection.
Key area:
src/shared/
Campaign and scene data include schema versions so future migrations have an explicit upgrade path.
Only bump schema versions when the saved campaign or scene JSON shape changes.
The renderer contains the React GM View, React Player View, canvas rendering code, hooks, feature libraries, and styles.
Key areas include:
src/renderer/views/src/renderer/components/src/renderer/canvas/src/renderer/hooks/src/renderer/lib/src/renderer/styles/
Local VTT uses Canvas 2D for most map and scene rendering.
Canvas rendering covers:
- Static maps.
- Video maps.
- Pan and zoom.
- Grids.
- Fog of war.
- Tokens.
- Drawings.
- Templates.
- Weather.
- Animated effects.
- Ruler measurement.
- Scene overlays.
Three.js is used where 3D rendering is needed, such as dice.
Layer responsibilities are intentionally narrow so tools, rendering, visibility, and persistence stay predictable.
For detailed layer rules, see:
docs/layer-ownership-rules.md
General guidance:
- Put visibility and reveal logic in Fog of War.
- Put environmental visuals in Effects.
- Put annotations and templates in Drawings.
- Put movable map markers in Tokens.
- Keep GM-only prep content private.
- Do not overload reserved future layers.
Player View receives a filtered scene payload.
Player-safe projection should prevent GM-only data from leaking into the Player View.
When adding features, make sure Player View visibility rules are handled centrally and tested.
Use focused tests for small changes and broader tests for cross-cutting behavior.
Important test areas include:
- Campaign actions.
- Persistence normalization.
- Player View sync.
- Scene editing.
- Token behavior.
- Fog and visibility.
- Map calibration.
- Measurement.
- Dice.
- Turn order.
- Effects.
- Electron metadata and asset behavior.
Tests live in:
tests/shared/tests/renderer/tests/electron/
Vitest discovery is intentionally limited to tests/**/*.test.ts so ignored build output such as dist-electron/ is not counted as source test coverage.
Application icons live in the build/ folder:
build/
icon.svg
icon.ico
icon.icns
The editable icon source is build/icon.svg.
To regenerate icon files after changing the source artwork:
npm run generate:iconWindows packaging uses build/icon.ico.
macOS packaging uses build/icon.icns.
Release builds are created by GitHub Actions when a version tag matching v*.*.* is pushed.
A typical release flow:
- Create or update a release branch.
- Bump the app version.
- Update
CHANGELOG.md. - Run local verification.
- Commit release metadata.
- Push the release branch.
- Create and push the version tag.
- Confirm the GitHub Actions release build succeeds.
- Check the GitHub Release assets.
For detailed steps, see:
docs/release-process.md
The app version is stored in:
package.jsonpackage-lock.json
Use npm to update both:
npm version 0.1.9 --no-git-tag-versionOnly bump campaign or scene schema versions when saved data shape changes.
Schema constants live in:
src/shared/localvtt.ts
Published GitHub release assets may be immutable.
If a release has already been published for a version tag, create a new patch version and tag rather than trying to replace the existing release assets.
For example, if v0.1.8 is already published, use v0.1.9 for a corrected build.
The release workflow validates expected Windows, macOS, and Linux artifacts before publishing. If validation fails before a GitHub Release is created, fix the release branch, update or recreate the unpublished tag on the corrected commit, and rerun the tag workflow. If the GitHub Release already exists, do not retry the same tag.
Before publishing a Windows release, test installing the candidate over the previous release and confirm existing campaign folders, Desktop shortcuts, and Start Menu shortcuts still work.
Also attempt installing the previous release over the candidate release. Downgrades are not a supported recovery path unless a release explicitly says otherwise, so record whether the installer blocks, warns, or allows the downgrade and confirm campaign folders are not corrupted.
For the detailed checklist, see docs/release-process.md.
Before opening a pull request, run:
npm run check
npm run buildFor changes affecting Electron, packaging, Player View, or release behavior, also run:
npm run smoke:electronFor packaging changes, test the relevant package command:
npm run package:winor:
npm run package:macWhen contributing:
- Keep changes focused.
- Follow existing project structure.
- Add or update tests for behavior changes.
- Avoid moving unrelated files.
- Keep Player View visibility rules in mind.
- Preserve local-first campaign portability.
- Do not introduce unrestricted renderer filesystem access.
- Update docs when workflows or user-facing behavior changes.
Project docs include:
docs/architecture.mddocs/canvas-performance-budget.mddocs/codebase-audit.mddocs/layer-ownership-rules.mddocs/release-process.md
These are useful references for architectural decisions, release flow, and feature placement.