A browser-based companion for The Tower — plan labs, model upgrades, import your save, compare builds, and share loadouts without touching the game.
Get The Tower: Google Play · App Store · TechTree Store
| Area | What you can do |
|---|---|
| Research | Browse every research tree (main, attack, defense, utility, cards, perks, bots, modules, and more) with costs and benefits shown where wiki data allows. |
| Labs | Model upgrade costs and build times, compare configs side by side, save named presets, and use Max All to cap every visible lab at once. |
| Workshop | Simulate attack, defense, utility, and ultimate-weapon upgrades with full coin and power-stone costs. Buy-multiplier rail includes MAX (+ to cap, − to zero). The Enhance tab unlocks once Workshop Enhancements is researched. |
| Bots | Track the five event-shop bots (Flame, Thunder, Golden, Amplify, Bot Bot), medal unlock order, stat upgrades, and Bot+ abilities. BOTS lab levels update cooldown and duration live. |
| Cards | Manage your full 31-card inventory, star levels (Lv.1–7), five preset loadouts (double-click a tab to rename labels to match in-game), equip-slot limits, and Card Mastery scaling. |
| Modules | Configure chassis modules (cannon / armor / core / generator) across epic→ancestral tiers, sub-module effects, assist unlocks, stone efficiency, and five saved module presets (renamable tabs, same as Cards). |
| Relics | Catalog all 268 wiki relics with art, filter by unlock group, and have owned relics feed automatically into workshop stat formulas. |
| Themes | Track owned tower skins, backgrounds, banners, music, and guardians — including coin-bonus rollups per category. |
| Guardians | GUARDIANS tab: active guardian (ties to Themes), four chip slots (Bits unlock costs), six chips (Attack, Ally, Bounty, Fetch, Scout, Summon) with three upgrade tracks each from GOD tables under tables/guardians/. Import from playerInfo.dat or tower CSV; respec and workspace undo. |
| Vault | Plan Power and Harmony tech-tree unlocks on an interactive tree with in-game icons and benefit tooltips. |
| Displayed stats | Workshop cards show in-game-aligned values: damage, DPM, health, defense, and utility rows fold in labs, cards, relics, sub-modules, and Enhance tiers (e.g. Recovery Package+ on Recovery Amount and Max Recovery). Stat values and upgrade costs come from GOD tables under tables/. |
Import your save — On the LAB tab, load a gzip-compressed playerInfo.dat from The Tower (account menu → tower backup). TowerSmith maps your lab levels, workshop stats, bots, ultimates, modules, card stars, relics, guardian chips, guild ID, and owned themes in one shot.
- Android: tap Import playerInfo.dat to copy the save-folder path, then pick the file.
- iOS: import a copy from Files, iCloud, or a backup extract (the game sandbox isn't browsable in-browser).
CSV backup — Export/import a tower_csv_v1 file with one or more named builds. Row types include:
| Row prefix | Purpose |
|---|---|
build,name,… |
Named build label |
lab,<section>-<item>,level |
Lab level overrides |
lab,gameResearchLevel,… |
Full researchLevel[] JSON array from the save |
ws,… |
Workshop snapshot (upgrades, enhance, bots, cards, modules, relics) |
card,… |
Card stars, presets, equip slots |
module,… |
Module loadout presets |
relic,ownedIds / relic,simBonusFraction |
Owned relics and sim bonus |
theme,ownedIds / theme,selection |
Owned cosmetic IDs and active skin picks |
guardian,state |
Guardian chip slots, unlocks, and upgrade tracks |
Swap builds without overwriting your current setup.
Effective Paths sync — On the LAB tab → Tower Backup & Sharing, open Effective Paths sync… to import from or export to community Effective Paths Google Sheets workbooks (see Effective Paths sync below).
Share links encode a full snapshot (labs, workshop, build name, themes) in the ?tower= query string. Copy the URL or generate a QR code — anyone opening the link gets the same build.
Community gallery — Browse and load community builds from the BUILDS tab. Sign in with Google, Discord, or Twitch (footer) to publish your own. Copy share link publishes and copies a short ?build=<uuid> URL in one step. Owners can set category and visibility, regenerate the share link, and see upvote counts. Filter by author or registered guild name.
TowerSmith can import from and export to the community Effective Paths spreadsheet ecosystem — the IDS Master gateway tab plus linked workbooks (Laboratory, Workshop, Ultimate Weapons, Cards, Modules, Bots, Guardians, Themes & Songs, Relics, and future Vault). IDS Collection workbooks (one spreadsheet with category tabs for multiple areas) are detected automatically; category tab pickers prefer the tab that matches the workbook layout.
User flow
- Tools / Settings → paste your IDS Master spreadsheet URL or ID and press Save IDS (stored on your Supabase profile when signed in and synced across devices; device-local when signed out). If you saved the URL while signed in, sign in to TowerSmith first — the sync dialog explains this when the ref is on your account.
- LAB tab → Tower Backup & Sharing → Effective Paths sync… — follow the in-dialog setup steps if this is your first time.
- Sign in with Google (OAuth
drive.filescope; CSRF-protectedstateparameter). Choose your IDS Master in the Google file picker, then select any linked workbooks TowerSmith cannot open yet. TowerSmith reads workbook IDs from the IDS tab for per-category import/export actions. - Import pulls lab levels, workshop stats, relic ownership, themes, cards, bots, guardians, modules, and related data into your workspace.
- Export writes TowerSmith state back to the linked sheets. Exports can stage preview tabs titled
… (TowerSmith preview)so you can review before promoting changes to the live sheet tabs.
Requirements
- A configured Google OAuth Web client with the Google Sheets API and Google Picker API enabled. Set
VITE_GOOGLE_SHEETS_OAUTH_CLIENT_IDandVITE_GOOGLE_PICKER_API_KEYin.env(local) and Netlify build env (production). OAuth uses the non-sensitivedrive.filescope; users grant access per spreadsheet via Picker. See.env.example. - Netlify Functions for server-side Sheets API calls: use
npm run dev:netlifylocally or deploy to Netlify. Plainnpm run devruns the UI only (OAuth may work for listing, but import/export endpoints need Functions). - Your Google account must have edit access to the IDS Master sheet and each linked workbook.
OAuth troubleshooting
- Google Cloud “Use secure flows” — Keep production and dev on separate Web OAuth clients. The production client must use HTTPS only (no
http://127.0.0.1,http://localhost, or LAN redirect URIs). TowerSmith uses the GIS authorization-code flow with a CSRFstateparameter and PKCE. After updating the client or code, allow a few days for Google’s security dashboard to clear the warning. - Cross-Account Protection (RISC) — Google’s security dashboard expects a registered RISC receiver for apps that use Google OAuth. TowerSmith exposes
POST /api/risc/events(Netlify Function). SetGOOGLE_RISC_OAUTH_CLIENT_IDSon Netlify, enable the RISC API in GCP, then runnode scripts/register-google-risc.mjswith a RISC Configuration Admin service-account key. Effective Paths stores Google tokens only in browsersessionStorage, so events are logged for audit; TowerSmith sign-in uses Supabase, not this GCP client. - Use Chrome or Firefox at the site URL — embedded IDE browsers (including Cursor’s built-in preview) can show Google sign-in but cannot return the access token.
- Allow popups for the TowerSmith origin and complete the Google sign-in and file picker steps after choosing your account.
- Local dev: run
npm run dev:netlifyand openhttp://localhost:8888(Functions + COOP headers). Plainnpm run devon port 5173 also sets COOP for OAuth, but import/export still needs Functions. - If sign-in times out, hard-refresh and retry; check that your OAuth client’s Authorized JavaScript origins include your exact origin (e.g.
https://www.towersmith.com,http://localhost:8888).
Maintainer code paths: src/effectivePaths/ (parsers, sheet layouts, staging), netlify/functions/import-effective-paths.ts, export-effective-paths.ts, ids-gateway-effective-paths.ts.
- Appearance — Dark (default), Light, and High contrast themes in Tools / Settings.
- Keyboard shortcuts —
/focuses search on Labs, Relics, and Themes;1–9switches main tabs (Workshop, Labs, Cards, Modules, Bots, Guardians, Themes, Relics, Vault);0opens the community Gallery;Ctrl+Zundoes the last Max All or reset (up to 20 steps);Esccloses the top dialog. Full list under Tools / Settings → Keyboard shortcuts. - Bug Buster — Floating report button attaches an optional tower CSV and player save excerpt to bug reports (email, Discord support ticket, GitHub, or clipboard).
- Deep links — Link directly to a lab card, workshop stat, ultimate weapon, or relic via URL hash or query param.
- PWA — Install to your home screen (Tools / Settings → Install app on Android; Safari Share → Add to Home Screen on iOS). Works with limited offline support.
- Persistence — All settings, snapshots, presets, and owned IDs survive reloads. Signed-in users sync lab presets, card/module preset tab labels, and guardian chips via account workspace backup, and the Effective Paths IDS Master URL on their Supabase profile. Full reset available in Tools / Settings.
- Privacy — Privacy Policy linked from the site footer and static HTML for OAuth verification.
For release history, see CHANGELOG.md.
Requirements: Node.js 20 or newer (CI uses Node 22).
npm install
npm run devOpen the URL Vite prints (default http://localhost:5173/). The dev server also advertises a Network URL for testing on a phone on the same Wi-Fi.
Production build
npm run build # typecheck + bundle → dist/
npm run preview # serve dist/ locallyWith the community gallery
npm run dev:netlifyRequires Supabase env vars for the gallery — copy .env.example to .env and fill in your keys. For Effective Paths sync, also set VITE_GOOGLE_SHEETS_OAUTH_CLIENT_ID and VITE_GOOGLE_PICKER_API_KEY. See Community gallery and Effective Paths sync.
Note: Netlify Dev runs Vite only (see netlify.toml); it does not re-run copy-god-tables-to-public.mjs on every start. After editing GOD tables under tables/, run node scripts/copy-god-tables-to-public.mjs or use npm run dev / npm run build.
| Command | Description |
|---|---|
npm run dev |
Start Vite dev server with HMR (runs GOD table copy first). |
npm run dev:netlify |
Vite + Netlify Functions locally (gallery, guild, Effective Paths). |
npm run build |
Typecheck and bundle to dist/ (prebuild copies GOD tables to public/tables/). |
npm run preview |
Serve the production build locally. |
npm run lint |
Run ESLint. |
npm run test |
Run Vitest unit tests. |
npm run test:e2e |
Playwright end-to-end tests (share-link flow). |
npm run check:i18n |
Fail if locale dictionaries drift from English keys. |
npm run import-lab |
Import lab CSV into tower-labs.json (scripts/import-lab-csv.mjs). |
npm run wiki-stamp |
Bump the wiki/game alignment date shown in Tools / Settings. |
npm run post-changelog-discord |
Post latest CHANGELOG entry to Discord (needs webhook env). |
npm run icons |
Re-rasterize public/app-icon.svg → favicon and PWA PNGs. |
npm run og-banner |
Regenerate the 1200×630 social preview image. |
npm run research-unmapped |
List unmapped researchLevel[] slots (docs/research-level-unmapped.txt). |
npm run decode-wiki-html |
Decode wiki HTML exports for table scraping. |
npm run scrape-wiki-table |
Scrape a wiki table to TSV/JSON. |
| Path | Role |
|---|---|
public/research/ |
Runtime research data: manifest.json and section JSON files. |
public/tables/ |
Runtime GOD table JSON copied from tables/ at build/dev start (workshop, labs, guardians). |
tables/ |
GOD ground truth: labs/, workshop/, guardians/ JSON. Do not edit to satisfy tests without evidence. Labs: src/data/labGodTables.ts + src/labCosts.ts. Workshop: src/data/workshopGodTables.ts + src/workshopCosts.ts. Guardians: src/data/guardianChipGodTables.ts. Refresh: node scripts/sync-lab-god-tables.mjs, node scripts/import-workshop-god-tsv.mjs + node scripts/sync-workshop-god-tables.mjs, node scripts/copy-god-tables-to-public.mjs. |
src/data/ |
Lab costs (tower-labs.json), workshop curves, bot/ultimate/relic/module/guardian tables, and generated data files. Coin formatting rules: src/labCosts.ts (see Lab coin display). |
src/effectivePaths/ |
Effective Paths Google Sheets parsers, sheet layouts, import/export staging, IDS Master workbook discovery. |
src/playerSave/ |
playerInfo.dat NRBF decoder, save-field mappings, and import pipeline. See Player save ↔ TowerSmith mapping and NOTICE.md. |
src/components/ |
All UI — research browser, workshop, bots, modules, cards, relics, themes, guardians, settings, compare dialogs, Effective Paths sync. |
src/i18n/ |
English, Spanish, and German UI strings and research overlays. |
netlify/functions/ |
Netlify Functions: community gallery, guild name registry, Effective Paths Sheets API (see table below). |
scripts/ |
Data maintenance scripts (lab/workshop/guardian GOD import, wiki scrapers, save dump tools, icon/banner regen). |
supabase/schema.sql |
Gallery and guild database schema. |
| Group | Functions | Purpose |
|---|---|---|
| Gallery | submit-tower, list-towers, get-tower, delete-tower, vote-tower, set-tower-visibility, set-tower-category, regenerate-tower-link, admin-me |
Publish, browse, upvote, and manage community builds |
| Account | account-workspace |
Per-user lab preset and guardian chip cloud backup (GET/PUT /api/account/workspace) |
| Guild | register-guild, update-guild, resolve-guild |
Registered guild names for profile and gallery filters |
| Effective Paths | import-effective-paths, export-effective-paths, ids-gateway-effective-paths, list-effective-paths-sheets, workbook-access-effective-paths |
Google Sheets import/export and workbook access checks |
After editing files under public/research/ or src/data/, save and refresh — Vite HMR picks up most changes automatically.
The gallery uses Netlify Functions as the API layer and Supabase (Postgres + Storage) for data. Anyone can browse and load builds; publishing requires signing in.
-
Create a project at supabase.com.
-
Run
supabase/schema.sqlin the SQL editor. Existing projects: run the upgrade blocks at the bottom of that file when noted (e.g.effective_paths_ids_master_refonprofilesfor IDS Master sync). -
Storage — the schema creates a private
tower-payloadsbucket (JSON only, 2 MB per file). Gallery builds and per-account workspace backups are read/written only via Netlify Functions (service role), not public URLs. If you created this bucket earlier as public, run the hardening upgrade insupabase/schema.sql(search forharden tower-payloads). -
Auth — enable Google, Discord, and Twitch providers. In Authentication → URL Configuration:
- Site URL: your production origin (e.g.
https://www.towersmith.com/) - Redirect URLs: add both production and local dev origins (e.g.
http://localhost:5173/**). If sign-in from localhost lands on production, localhost is missing here. - Google/Discord/Twitch redirect URI:
https://<project>.supabase.co/auth/v1/callback.
- Site URL: your production origin (e.g.
-
Copy keys into
.env(local) and your Netlify site env:VITE_SUPABASE_URL,VITE_SUPABASE_ANON_KEY(build + browser)SUPABASE_URL,SUPABASE_SERVICE_ROLE_KEY(Functions only)
Important: All four values must come from the same Supabase project (same
refin the JWT). If the browser signs in to project A but Functions verify with project B, publish returns 401 while guild resolve still works. After changingVITE_*vars, trigger a new production build (not just a functions-only deploy).
| Flag | Effect |
|---|---|
TOWER_GALLERY_SUBMIT_DISABLED=1 |
Reject new submissions. |
VITE_TOWER_GALLERY_DISABLED=1 |
Disable gallery API calls in the frontend build. |
TOWER_GALLERY_ADMIN_USER_IDS |
Comma-separated Supabase user UUIDs allowed to use Gallery admin (see below). |
For allowlisted Supabase users, Tools / Settings includes a Gallery admin panel (requires npm run dev:netlify or a Netlify deploy with Functions).
- List — Loads every row in
public.builds(public and private/unlisted), 20 per page with Load more. The public BUILDS tab only listsvisibility = publicbuilds (signed-in users also see their own private builds in My builds). - Delete — Removes the
buildsrow and storage payload; old?build=links stop working. - Setup — Sign in with the same provider used for publish, add your user UUID from the access-denied hint (or Supabase Authentication → Users) to
TOWER_GALLERY_ADMIN_USER_IDSin Netlify (and.envfor local Functions), then restartdev:netlify.
List API: authenticated GET /api/towers?admin=1 (see list-towers.ts).
Research card cost lines use marginal COST values from src/data/tower-labs.json, formatted in src/labCosts.ts:
| Lab family | Function | Display rules (wiki-aligned) |
|---|---|---|
| Assist Module Substats / Bonus (8 cards) | formatAssistModuleLabCoinDisplay |
Always q, never T. Raw ≥ 1e12 < 1e15 → ÷ 1e12 (e.g. 250.00q). Raw ≥ 1e15 → ÷ 1e15 (e.g. 3.75q). All eight names alias to the Assist Module Substats - Cannon table. |
| Other coin labs (e.g. Ultimate Weapon Durations) | formatLabCoinDisplay |
T below 1e15; q from 1e15 up (e.g. 2.00q, not 2000.00T). |
| Workshop medals / enhance panels | formatCoinAbbrev / formatCoinAbbrevPreferT |
Workshop UI keeps T at trillion scale where the wiki does. |
Legacy snapshot strings in public/research/sections/*.json (e.g. 0.25 q) are normalized on load via normalizeCoinAbbrevDisplay (Assist Module cards pass assistModuleLab: true).
playerInfo.dat is gzip-compressed Unity BinaryFormatter (NRBF). TowerSmith decodes it with src/playerSave/nrbf.ts (adapted from CrispStrobe/nrbf; see NOTICE.md).
| Save field / area | TowerSmith destination |
|---|---|
researchLevel[] |
Lab level overrides via game*ResearchMapping.ts files; index regenerated with node scripts/gen-game-research-index.mjs |
upgradeWorkshopLevel[] |
Workshop upgrade levels |
*BotPresets / legacy bots*Presets |
Bots tab (gameBotPresetMapping.ts, gameBotLegacyPresetMapping.ts) — levels[] = [cooldown, range, weaponStat2, weaponStat4]; import uses purchased levels[], not farming selectedLevels[] |
*BotLevelCooldownSelected + bot researchLevel[…] |
BOTS lab overrides (gameBotLabMapping.ts) — cooldown labs 102–106; burn stack 107; Golden Bot - Duration 108; Thunder Bot - Linger Time 109; Amplify Bot - Duration 100; Bot Bot - Duration 213 |
Main / Modules / Battle Condition researchLevel anchors |
Reroll Daily Mission 148, Enhancement Attack - Coin Discount 154, Enhancement Utility - Coin Discount 227, Dissonant Echo - Attack 239, Dissonant Echo - Defense 240, Dissonant Echo - Utility 238 (gameMainResearchMapping.ts); Common Drop Chance 134, Shatter Shards 152, Unmerge Module 151, Assist Module Substats/Bonus 230–237 (gameModulesResearchMapping.ts); Battle Condition Reduction 199 (gameBattleConditionResearchMapping.ts) |
Module infoIndex / effects |
Chassis and assist modules (gameModuleIndex.ts; node scripts/gen-game-module-index.mjs) |
guardianChipSlot, guardianChipUnlocked, guardianChipLevel |
Guardians tab (gameGuardianChipMapping.ts) |
| Relic unlock arrays | Owned relic IDs |
| Theme / banner / music unlock flags | Themes owned IDs and selection |
lastGuildID, profile fields |
Profile and gallery guild filter |
Pipeline entry points: decodePlayerInfo.ts → mapPlayerDataToTower.ts → importPlayerInfo.ts.
The docs/ folder is gitignored. Scripts write reference dumps there during save-format or research-mapping work:
| Output | Generator | Use |
|---|---|---|
docs/player-save-field-dump.json / .txt |
node scripts/regenerate-player-save-dump.mjs |
Field inventory from a local playerInfo.dat |
docs/research-level-unmapped.txt |
npm run research-unmapped |
Slots in researchLevel[] not yet mapped to a lab |
docs/game-workshop-index-map.csv |
node scripts/export-game-research-id-map.mjs |
Workshop array index ↔ stat name |
Point scripts at your local save path (e.g. h:/The Tower/SaveGames/playerInfo.dat) when regenerating dumps. After mapping changes, run Vitest under src/playerSave/ and update the relevant game*Mapping.ts file — never guess slot IDs without save evidence.
UI is available in English, Spanish, and German. Research section and card names have locale overlays generated by scripts in scripts/. Run npm run check:i18n to verify all locale files stay in sync with the English key set.
- Run
npm run lint,npm run check:i18n, andnpm run testbefore pushing. CI (GitHub Actions) runs the same checks plus Playwright on every push/PR. - GOD tables — Committed JSON under
tables/is authoritative. Fix consumer code or tests when values disagree; do not edit tables to make tests pass without new evidence. - Ship checklist — For user-visible releases: bump
VERSION/package.json, add aCHANGELOG.mdentry, addwhats_new_*keys in en/de/es (src/whatsNew.ts), runnpm run check:i18n. - Discord release posts — pushing a new top version in
CHANGELOG.mdtomainposts release notes to Discord (.github/workflows/discord-changelog.yml). Add repo secretDISCORD_CHANGELOG_WEBHOOK_URL(channel webhook from Server Settings → Integrations → Webhooks). To backfill the latest entry: Actions → Discord changelog → Run workflow (force on), or locallyDISCORD_CHANGELOG_WEBHOOK_URL=... npm run post-changelog-discord -- --force. - Bump
dataVersioninpublic/research/manifest.jsonwhen research data changes — this busts the PWA cache. Users can also force a refresh via Tools / Settings → Refresh research data. - After editing
public/app-icon.svg, runnpm run icons. After changing the banner layout, runnpm run og-banner. - On Windows with OneDrive, Vite's cache is redirected to the system temp directory to avoid EPERM errors. Keep the project outside synced folders if issues persist.
Canonical version lives in VERSION, mirrored in package.json. Human-readable history is in CHANGELOG.md.
Licensed under CC BY-NC-SA 4.0.
Contributors are listed in AUTHORS.








