# Stack Research **Domain:** Browser-based narrative idle game (cozy + watercolor 2D + 7-Season prestige + AI-assisted content pipeline) **Researched:** 2026-05-08 **Confidence:** HIGH (engine + numeric + audio + bundler) / MEDIUM (animation + content pipeline) / LOW (cloud save backend — depends on launch scope) --- ## TL;DR — The Opinionated Stack **Engine:** Phaser 4 (v4.1.0+) with TypeScript, mounted inside a React shell that owns all narrative/menu UI. Vite for tooling. Howler.js for music crossfading on top of Phaser's audio. break_eternity.js for the prestige economy. Zustand for the game-state store that bridges Phaser scenes and React UI. Ink (via inkjs) for branching narrative content. lz-string + IndexedDB for saves. PocketBase as the eventual cloud-save backend. Spine (spine-phaser runtime) for the few characters that need rigged animation; everything else is hand-painted PNG sequences and PixiJS-style sprite work that Phaser handles natively. **Why this combination:** The dimensions of your game pull in opposite directions. The watercolor-and-cello atmosphere wants a renderer with strong asset fidelity. The 7-Season scope with thousands of memory fragments wants a framework that doesn't make you reinvent scene management, tweens, audio, and asset loading. The narrative-text density wants real DOM (React) for accessibility, font rendering, and i18n later. The AI-assisted content pipeline wants plain-text authoring formats (Ink, JSON, MD+frontmatter) the team can iterate on without engine round-trips. Phaser-4-as-engine + React-as-UI-shell hits all four; nothing else does. --- ## Recommended Stack ### Core Technologies | Technology | Version | Purpose | Why Recommended | |------------|---------|---------|-----------------| | **Phaser** | 4.1.0+ ("Salusa", April 2026) | 2D game framework: scenes, asset loader, camera, tweens, input, particle system, audio manager | Phaser 4 ships a rebuilt-from-scratch WebGL renderer (cited "100x faster" on internal tests vs Phaser 3), unified filter system, ESM modules, and official integrations for Vue/React/Next/Svelte/Solid. It is an actual *framework* (not just a renderer like PixiJS), which removes ~6 months of "build the engine before the game" work. Crucially: Phaser 4's official `create-phaser-game` CLI scaffolds a React + Vite + TypeScript template out of the box. (HIGH confidence) | | **TypeScript** | 5.6+ | Static typing for game state, save schema, narrative content schema | A 7-Season game with a complex authored prestige economy and persistent-save migration is a TypeScript-mandatory project. Save schema versioning (mandatory for an idle game) requires types that compiler-check migrations. (HIGH) | | **React** | 19.x | UI shell: title screen, journal, settings, narrative dialog overlays, fragment viewer, premium Keeper's Journal | Phaser's canvas can render text, but for **paragraphs of authored prose, the cozy-game audience expects browser-native typography, copy-paste, accessibility, screen-reader support, and i18n**. Render the world (garden, plants, weather, watercolor canvas) in Phaser; render the narrative chrome in React absolutely-positioned over (or around) the canvas. This is now the dominant pattern and Phaser 4 has a first-class React template. (HIGH) | | **Vite** | 6.x (Vite 8 with Rolldown landing 2026) | Dev server, HMR, prod bundler | Vite cold-start (~1.2s) and HMR (~10–20ms) vs Webpack (cold ~7s, HMR 500ms–3s) is a solo-team productivity multiplier. Native TypeScript via esbuild, no loader config. Phaser 4's official template ships with Vite. Vite 8 will replace esbuild+Rollup with Rolldown (single Rust compiler, dev = prod), but Vite 6 is fine to start on today. (HIGH) | | **Zustand** | 5.x | Game state store; bridge between Phaser scenes and React UI | Idle games have a single canonical state tree (resources, plants, fragments collected, season progress, prestige currencies). Zustand's single-store API + 5-star TS support + ~12ms single-update latency + tiny bundle (≈3KB) is the right shape. Redux is over-engineered for a single-player game; Jotai's atomic model fights you when ~80% of state mutates together each tick. (HIGH) | | **break_eternity.js** | 2.1.3 (Dec 2025) | Big-number library for prestige economies (numbers up to 10^^1e308) | Drop-in successor to break_infinity.js / decimal.js. Same interface, comparable perf (within 2x), supports very small numbers (e.g. 1e-400 tickspeeds) which break_infinity does *not*. Includes built-in `index.d.ts` — TypeScript-native. The standard choice for serious modern idle games. (HIGH) | | **inkjs** | latest (active fork: `inkle/inkjs` and `y-lohse/inkjs`) | Runtime for Ink narrative scripts (branching dialog, variables, knot/stitch flow) | Ink is the proven narrative scripting language behind 80 Days, Sorcery!, Heaven's Vault, A Highland Song. Inkjs is the official JavaScript port — zero deps, browser + Node, TypeScript imports under `/types`. Authors write `.ink` in the free Inky editor; you compile to JSON and stream into the game. **This is the single highest-leverage decision in your content pipeline** because it lets your writer iterate in a tool designed for them, not in source code. (HIGH) | | **Howler.js** | 2.2.4 (Sept 2024, actively maintained, 25.3k stars) | Cross-browser audio with Web Audio API + HTML5 fallback; dedicated music + ambience layer | Phaser 4 has native Web Audio with `fadeIn`/`fadeOut`, but Howler is the industry standard for **layered, crossfading, looping music with reliable mobile-Safari behavior**. Use Phaser's audio for SFX (interleaved with game events, scene-bound) and Howler for the persistent music+ambience bed that crossfades across Seasons independent of scene transitions. Splitting these is the cleanest architecture for a 7-Season tonal-shift soundtrack. (HIGH) | ### Supporting Libraries | Library | Version | Purpose | When to Use | |---------|---------|---------|-------------| | **lz-string** | 1.5.x | Compress save JSON before storage (50–70% reduction; UTF-16 safe for localStorage) | Always. Idle game saves grow with collected fragments; hitting the 5MB localStorage cap is a real concern by Season 5+. Use `compressToUTF16` for localStorage and `compressToBase64` for cloud-save payloads. (HIGH) | | **idb** (or **idb-keyval**) | 8.x | Promise-based IndexedDB wrapper | Use as primary save target instead of localStorage. IndexedDB is async (non-blocking — important when saves get large), has ~50% of disk available, and survives browser cleanup pressure better. Keep localStorage as fallback for tiny "last-played-timestamp" check. (HIGH) | | **spine-phaser** | 4.2+ (matches Spine editor 4.2 "physics revolution") | Skeletal animation runtime for characters that need rigged motion (Lura, the Nameless Man, possibly the Archivist) | Use **only** for the 3 named characters and any 2-3 hero plants that benefit from skeletal motion. Sprite sheets use 40–70% less GPU memory than skeletal animation, so for the 100+ background plants stick to PNG sequences or static art with shader sway. Esoteric upgraded spine-phaser to support Phaser 4 directly. (MEDIUM — depends on whether you commit to skeletal at all) | | **@pixi/ui** | n/a (only if going PixiJS) | UI components inside the Pixi canvas | **NOT recommended for this project** — see "What NOT to Use." Listed only because it's the canvas-UI option if you reverse the engine choice. | | **gray-matter** | 4.x | Parse YAML frontmatter from `.md` files | For the **fragment library** specifically: each memory fragment is a Markdown file with frontmatter (`id`, `season`, `unlock_condition`, `tags`, `length_seconds`, `voice_tone`). gray-matter + a build-time aggregation step turns a directory of fragments into a single typed JSON manifest the game loads. This is the pipeline shape that scales to thousands of authored fragments without a CMS. (MEDIUM — pattern is well-proven, exact tool is interchangeable) | | **zod** | 3.23+ | Runtime schema validation for save data and content | Validate every save on load (catches corrupt saves before they crash the game) and validate every fragment manifest entry at build time (catches typos in fragment frontmatter before they ship). Critical for an idle game where saves persist for months/years across game updates. (HIGH) | | **mitt** | 3.x | Tiny event emitter (~200 bytes) | Cross-cutting events that don't fit Zustand's reactive model: "season-changed", "memory-storm-triggered", "fragment-unlocked". Used to decouple Phaser scenes, React UI, and Howler music from each other. (MEDIUM) | | **dayjs** | 1.11+ | Time math for offline progression | Idle games need solid timestamp-diff calculations including DST/TZ edge cases. Lighter than Luxon/date-fns; sufficient API for "how long was the player gone?" with no surprises. (MEDIUM) | | **vite-plugin-checker** | 0.8+ | Run TypeScript + ESLint in a separate process during dev | Keeps Vite's ESM-on-demand speed while still surfacing type errors live. (HIGH) | ### Development Tools | Tool | Purpose | Notes | |------|---------|-------| | **`npm create @phaserjs/game@latest`** | Scaffold Phaser 4 + React + Vite + TypeScript template | Use the React template, not the vanilla one. This is the official path and will save days of integration work. | | **Inky** (free, by Inkle) | Narrative authoring environment for `.ink` files | The writer-facing tool. Ships with a live preview that simulates branches. Compile to JSON via the Ink CLI as a build step. | | **Spine** (Esoteric, $69 Essential / $339 Pro one-time) | 2D skeletal animation editor | Only purchase if you commit to rigged characters. Free trial covers prototyping. The Pro license unlocks meshes/skinning, which you likely want for watercolor textures that need to deform. | | **Aseprite** ($20) or **Photoshop** | Watercolor texture authoring + frame-sequence cleanup of AI-assisted output | Aseprite is the indie 2D standard; Photoshop is better if your watercolor pipeline starts in real watercolor + scan or in AI image generation. Given the AI-assisted-then-hand-refined constraint, Photoshop is the more honest fit. | | **TexturePacker** ($40) or **free-tex-packer** | Pack PNG sequences into atlases Phaser/Spine can stream | Atlas packing is mandatory for a watercolor game — individual high-res PNGs blow draw-call budget fast. | | **Audacity** (free) or **Reaper** ($60 indie) | Audio editing for cello recordings + ambience | Reaper's noncommercial license covers the entire dev cycle of a solo project. | | **Vitest** | Test runner for game-state logic, save migrations, prestige math | The non-rendering parts of an idle game (economy, save migrations, fragment unlock conditions) **must** be unit-tested. Vitest is the Vite-native choice. | --- ## Installation ```bash # Scaffold (run once) npm create @phaserjs/game@latest my-garden # → choose: React + Vite + TypeScript cd my-garden # Core game runtime npm install phaser@^4.1.0 npm install break_eternity.js@^2.1.3 npm install inkjs@latest npm install howler@^2.2.4 @types/howler npm install zustand@^5.0.0 npm install zod@^3.23.0 npm install mitt@^3.0.0 npm install dayjs@^1.11.0 # Save persistence npm install idb@^8.0.0 npm install lz-string@^1.5.0 @types/lz-string # Content pipeline npm install gray-matter@^4.0.0 # Optional: skeletal animation npm install @esotericsoftware/spine-phaser@^4.2.0 # Dev dependencies npm install -D typescript@^5.6.0 npm install -D vite@^6.0.0 npm install -D vite-plugin-checker npm install -D vitest @vitest/ui npm install -D @types/node ``` --- ## Alternatives Considered | Recommended | Alternative | When to Use Alternative | |-------------|-------------|-------------------------| | **Phaser 4** | **PixiJS 8** | If you reach Season 5 and discover you need 5,000+ animated plant sprites on screen at 60fps with custom shaders. PixiJS is ~3x smaller (450KB vs 1.2MB), faster at pure rendering, and has a richer filter pipeline for watercolor-style post-processing. The cost: you'd be writing scene management, asset loading, audio routing, and tween systems yourself. Verdict: not worth it for The Last Garden's actual rendering load. | | **Phaser 4** | **Excalibur.js** | If the team had a strong C# / Java background and wanted the most "game-engine-y" TypeScript-first framework. Excalibur is genuinely good but **still pre-1.0** (0.x), has a much smaller asset/plugin ecosystem, and lacks the official integration templates. Risk-adjusted: the 7-Season scope is too long to bet on a pre-1.0 engine. | | **Phaser 4** | **Godot 4 HTML5 export** | If the team strongly prefers a node-based scene editor and is willing to accept ~20–40MB minimum download size and a ~10–15% performance gap vs native. GDScript is fine for game logic but is an additional language for AI tooling to support and a recruiting constraint. The Last Garden's content pipeline is text-heavy, not scene-tree-heavy, so the editor advantage is muted. | | **break_eternity.js** | **break_infinity.js** | If you can prove the prestige economy will never need numbers above 1e1e308 *and* you don't need very small numbers (<1e-308). break_eternity is a drop-in upgrade with comparable perf, so there's almost no reason to choose the older library now. | | **Ink (inkjs)** | **Yarn Spinner** | If the writer prefers Yarn Spinner's syntax. Yarn's strength is Unity; its JS/web story is weaker than Ink's, and Godot/Unreal support was still alpha as of early 2026. Ink is more battle-tested in browser-native deployment. | | **Ink (inkjs)** | **Twine + custom JSON export** | If the narrative is primarily hyperlink-style hypertext rather than variable-driven branching dialog. The Last Garden's fragments are short prose pieces with conditional unlocks — closer to Ink's strengths than Twine's. | | **React UI shell** | **Phaser-native UI (`@phaserjs/dom-ui` or in-canvas Text)** | If you decide accessibility, screen-reader support, and copy-paste of fragments are not priorities. They almost certainly are for a narrative-cozy audience. | | **Zustand** | **Jotai** | If late in development you discover you have many independent atomic state slices that don't update together. Idle games rarely fit that shape — game state ticks as one unit. | | **PocketBase** | **Supabase + Cloudflare Workers** | If you expect >10K concurrent players or want managed Postgres. PocketBase is single-server SQLite (great until ~10K MAU on a $5 VPS); Supabase scales further but adds operational complexity. Defer this decision until you have real player data — local-only saves are fine for v1 launch. | | **Howler.js** | **Tone.js** | If the music system needs procedural composition or DSP effects beyond crossfading and looping. The Last Garden's audio is *recorded cello + recorded ambience*, not procedurally generated, so Tone.js is overkill. | | **lz-string** | **pako (DEFLATE/gzip)** | If saves grow past ~500KB compressed. Pako compresses 60–75% (vs lz-string's 50–70%) but is larger and slower for small strings. For a fragment-collecting game with text-heavy saves, this could matter eventually — easy swap. | | **Spine** | **DragonBones** (free) | If budget is the constraint. DragonBones is genuinely capable and free, but the runtime situation in 2026 is messier — Spine has official Phaser 4 + PixiJS runtimes maintained by the engine authors themselves. For a multi-year project, the maintenance story matters more than the license fee. | | **Spine** | **Frame-by-frame PNG sequences only** | **This is the most likely actual answer for v1.** Watercolor + AI-assisted production lends itself to *image-sequence-of-frames* much better than to skeletal rigging — you can't easily "rig" a watercolor wash. Use Spine only if specific characters demand it; default to image sequences. | | **Vite** | **Webpack** | If the team has heavy investment in a Webpack-based build pipeline. None applies here — greenfield project. | --- ## What NOT to Use | Avoid | Why | Use Instead | |-------|-----|-------------| | **Unity WebGL** | (1) 25–60MB+ download is fatal for the *A Dark Room* / *Paperclips* lineage, where one of the genre's defining traits is "starts in 3 seconds in any browser tab." (2) Unity WebGL has memory and startup-time issues that compound on lower-end devices and mobile browsers. (3) Cosmic-overkill for a 2D narrative idle game. (4) Locks you out of the cozy-narrative-idle audience that plays at work. | Phaser 4 (browser-native, ~1.2MB framework, instant load) | | **Unreal Engine HTML5** | Doesn't really exist as a supported target anymore; what does exist produces multi-hundred-megabyte builds. Same audience-destruction argument as Unity WebGL but worse. | Phaser 4 | | **Phaser 3** | Phaser 4 (April 2026) is the active line. Phaser 3 still works, but the official tooling, courses, migration guides, and Spine runtimes have all moved to Phaser 4. Starting greenfield on Phaser 3 in 2026 is choosing the deprecated branch. | Phaser 4 v4.1.0+ | | **PixiJS as the *only* framework** | PixiJS is a *renderer*, not a *framework*. You'd build your own scene system, asset loader, input router, audio manager, tween engine, and physics layer. That's 6 months of yak-shaving before Season 1's loop is playable. | Phaser 4 (uses PixiJS-style WebGL rendering under its renderer abstraction anyway) | | **react-pixi-fiber / @pixi/react** | Reasonable for single-screen interactive graphics, but the React-reconciler layer over the Pixi scene graph adds complexity and per-tick overhead that an idle game's tick loop will hit constantly. The Phaser+React-shell pattern keeps React out of the hot path. | React for UI shell; Phaser owns the canvas | | **decimal.js** (alone) | Arbitrary precision, not arbitrary magnitude — slower than break_infinity/eternity for the same idle-game use case, and runs out of room above ~1e9e15 anyway. | break_eternity.js | | **Plain `Number` for prestige currencies** | JavaScript Number maxes at 1.79e308 then becomes Infinity. A 7-Season prestige game with exponential growth *will* exceed this within a few prestige cycles. Discovering this in Season 4 means refactoring every formula. | break_eternity.js from day one | | **localStorage as primary save** | Synchronous (blocks main thread on large saves), 5MB hard cap, and gets cleared by browsers under storage pressure. Fine as a fallback or for a "last save timestamp" key. | IndexedDB (via `idb`) for primary saves; localStorage fallback for the bookkeeping key | | **JSON.stringify with no compression** | Save bloat is real for fragment-heavy games. A 2MB uncompressed save compresses to ~600KB with lz-string and stays well clear of any cap. | lz-string (`compressToUTF16` for IDB/localStorage, `compressToBase64` for cloud) | | **Yarn Spinner for narrative** | Best support is Unity; web/JS support exists but lags Ink. Godot/Unreal integrations were still alpha as of early 2026. | Ink + inkjs | | **Twine** for the fragment system | Twine is *story-shaped* (hypertext nodes); your fragments are *content-shaped* (typed records with metadata). Mismatched data model. | Markdown-with-frontmatter files compiled to JSON manifests (gray-matter) | | **Vanilla JavaScript** (the *Paperclips* approach) | *Paperclips* was 1 person, 1 screen, no animation, ~2,000 lines. The Last Garden is 7 Seasons of authored content, watercolor visuals, named characters, prestige currencies, save migrations across multi-year update cycles. The Paperclips approach was right for Paperclips and wrong for this. | TypeScript + Phaser 4 + Vite | | **Redux Toolkit** | Boilerplate-heavy for a single-player game with no time-travel debugging requirements. ~50% larger memory footprint than Zustand for the same workload. | Zustand | | **GDScript-only stack (Godot)** | Adds a second language to a solo/small team that needs AI tooling support across the whole codebase. Web export bundles are 20–40MB+ minimum. | TypeScript everywhere via Phaser 4 | | **Tone.js** | Procedural-audio framework. Your audio is recorded, not synthesized. | Howler.js | | **Custom Canvas + TypeScript from scratch** | You'll build Phaser. Worse, slower, less tested. The "I'll just write Canvas code" instinct dies in Season 2 when you need particle effects for the Memory Storms. | Phaser 4 | --- ## Stack Patterns by Variant **If solo developer (likely):** - Stick to the recommended stack exactly. - Don't add Spine in v1 — frame-sequence PNGs only. Add Spine in a v1.x update if a specific character benefits. - Defer cloud saves entirely. Local-only is fine for launch; the *A Dark Room* / *Paperclips* lineage shipped without cloud saves. **If 2–3 person team with a dedicated artist:** - Add Spine from the start for the 3 named characters. - Add a minimal PocketBase deploy (single $5 VPS, ~15MB binary) for opt-in cloud saves at launch. PocketBase ships with auth + REST + admin UI, so it's roughly a weekend of integration work. **If you decide to skip React for the UI shell:** - Don't. The narrative-cozy audience will use screen readers, will copy-paste fragments to share, will run at 200% browser zoom. Canvas-native text fights all of these. - If you absolutely must, use Phaser 4's built-in Text + a custom modal system, accept that journals/fragment views will feel less native, and budget for accessibility complaints. **If the watercolor look needs more shader work than Phaser's filter system supports:** - Phaser 4's unified filter system covers most needs (post-process bloom, color grading by Season, paper-texture overlays). - If you hit a wall, you can run a PixiJS render layer alongside Phaser for one specific effect — Phaser 4's renderer is PixiJS-compatible at the WebGL level. **If audience testing reveals players want voiced dialogue post-launch:** - Howler.js handles this trivially (it's just more audio sprites). The architecture survives the addition. --- ## Version Compatibility | Package A | Compatible With | Notes | |-----------|-----------------|-------| | `phaser@^4.1.0` | `vite@^6` or `^7` | Phaser 4's official template uses Vite. ESM-first; Phaser 4 fixed several ESM build issues in v4.1.0. | | `phaser@^4.1.0` | `@esotericsoftware/spine-phaser@^4.2.x` | Esoteric upgraded spine-phaser specifically for Phaser 4 in collaboration with Phaser's author. **Do not** use spine-phaser v3 with Phaser 4. | | `react@19` | `phaser@^4.1.0` | React 19 owns the DOM; Phaser owns the canvas. They coexist via a `` mounted by `useEffect`. The official template handles this. | | `inkjs@latest` | TypeScript via `inkjs/types/Story` etc. | The TypeScript classes are under the `/types` submodule, not the root export. | | `break_eternity.js@^2.1.3` | TypeScript native | Ships `index.d.ts`. No `@types/...` package needed. | | `howler@^2.2.4` | All evergreen browsers + iOS Safari | Howler abstracts the iOS-Safari "must touch screen first" gotcha that bites raw Web Audio code. | | `idb@^8` + `lz-string@^1.5` | Compose freely | Compress the JSON string with lz-string, then store the compressed string as a single value in IndexedDB. Don't try to store the parsed object directly — keep the indirection so save migrations can run on the string. | | `zustand@^5` | React 18+ | Zustand v5 dropped some React 17 compat. Fine here since we're on React 19. | --- ## Content Pipeline (Specific to Memory Fragments) This is the load-bearing piece of your production model and deserves explicit treatment. **The author writes:** - `.ink` files for branching dialog scenes (the three named characters' arcs, place-memory vignettes, the final binary choice). - `.md` files with YAML frontmatter for individual memory fragments. Example: ```markdown --- id: f_0247_theLatch season: 3 unlock_when: { plant: "winter-rose", harvested_at_least: 3 } tone: melancholy characters: [Lura] length_seconds: 8 --- The latch on the gate she always meant to fix. Not broken — never broken. Just stiff in the cold, the way her father's hands got, the year before. ``` **Build step does:** 1. Walk `content/fragments/**/*.md`, parse with gray-matter, validate each entry against a Zod schema, fail the build on schema violations. 2. Aggregate into `dist/fragments.json` (compressed with lz-string before bundling). 3. Compile every `content/dialog/*.ink` to JSON via the Ink CLI. 4. Generate a TypeScript `.d.ts` file enumerating fragment IDs so unlock conditions are type-checked at compile time. **The game runtime:** - Lazy-loads fragments by Season (ship Season 1 fragments in the initial bundle, fetch Seasons 2–7 on Season transition). - Streams Ink stories via `inkjs`, with `Story.variablesState` tied to the Zustand store so narrative branching reacts to game state. - Uses Howler to crossfade the Season's musical bed when an Ink story triggers a `# music: storm_theme` tag. **Why this shape:** It separates *authoring* (writer-friendly tools) from *engineering* (typed, validated, lazily loaded). It scales to thousands of fragments without a CMS. It survives AI-assisted production because Markdown+frontmatter is exactly what LLMs generate well. And it lets the writer iterate on Season 4 in Inky without rebuilding the game. (MEDIUM confidence on the exact tool choices; HIGH confidence that *some* version of this shape is correct. Specific tools are easy swaps; the architecture is the recommendation.) --- ## Sources - [Phaser 4 release notes (v4.1.0 "Salusa", Apr 2026)](https://phaser.io/news) — verified Phaser 4 is the active stable line; HIGH - [Phaser CLI / `@phaserjs/create-game`](https://github.com/phaserjs/phaser) — verified React+Vite+TS template; HIGH - [PixiJS v8.18.1 release (Apr 2026)](https://github.com/pixijs/pixijs/releases) — verified version; HIGH - [break_eternity.js v2.1.3 (Dec 2025)](https://github.com/Patashu/break_eternity.js) — verified TS-native, drop-in for break_infinity.js/decimal.js; HIGH - [Howler.js v2.2.4 (Sept 2024)](https://github.com/goldfire/howler.js/releases) — verified active maintenance; HIGH - [inkjs (inkle/y-lohse forks)](https://github.com/inkle/inkjs) — verified TS support and browser compat; HIGH - [Spine-Phaser runtime upgraded for Phaser 4](https://en.esotericsoftware.com/spine-pixi) — verified runtime compatibility; HIGH - [Vite vs Webpack 2026 benchmarks](https://dev.to/pockit_tools/vite-vs-webpack-in-2026-a-complete-migration-guide-and-deep-performance-analysis-5ej5) — HMR/cold-start metrics; MEDIUM - [State Management 2026: Zustand vs Jotai vs Redux Toolkit vs Signals](https://dev.to/jsgurujobs/state-management-in-2026-zustand-vs-jotai-vs-redux-toolkit-vs-signals-2gge) — perf and bundle data; MEDIUM - [PocketBase / Supabase comparison 2026](https://uibakery.io/blog/supabase-alternatives) — backend trade-offs; MEDIUM - [Universal Paperclips — Wikipedia + memalign analysis](https://en.wikipedia.org/wiki/Universal_Paperclips) — verified the lineage was vanilla JS, but argument *against* repeating that approach for this scope; HIGH on the historical claim - [Math of Idle Games (Kongregate)](https://blog.kongregate.com/the-math-of-idle-games-part-iii/) — offline progression / delta-time math foundation; HIGH (industry-standard reference) - [Cuphead animation pipeline (GDC, Made With Unity)](https://unity.com/made-with-unity/cuphead) — referenced for "watercolor backgrounds + frame-sequence character art" pattern, *not* the engine choice; MEDIUM - [lz-string for client-side compression](https://pieroxy.net/blog/pages/lz-string/index.html) — verified UTF-16 safety + compression ratios; HIGH - [Godot 4 HTML5 export size analysis](https://docs.godotengine.org/en/latest/tutorials/export/exporting_for_web.html) — verified ~20–40MB minimum; HIGH - [Unity WebGL load-time/file-size guidance](https://docs.unity3d.com/6000.3/Documentation/Manual/webgl-performance.html) — verified the size/load problem; HIGH --- *Stack research for: browser-based narrative idle game (The Last Garden)* *Researched: 2026-05-08*