BeamNG.Earth
A 1:1 scale streamed Earth for BeamNG.drive: real elevation and satellite imagery, floating origin, LOD streaming from a companion server
Character sheet and game runner for fallowRPG
GamesTypeScript · PythonGitHub @gbelowOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
Loading the full README. The saved excerpt is shown below.
Deterministic Frontend Architecture for Complex Interactive Systems
This is a web application built to explore how complex, highly interdependent rulesets can be modeled in a way that keeps the frontend predictable, testable, and resilient under heavy interaction.
While themed as a tabletop RPG assistance tool, the project’s primary focus is frontend architecture: extracting a deterministic domain layer that dramatically reduces UI complexity and state divergence in React applications.Read the full README ↗Live Demo: https://fallow-app.vercel.app/
This is a web application built to explore how complex, highly interdependent rulesets can be modeled in a way that keeps the frontend predictable, testable, and resilient under heavy interaction.
While themed as a tabletop RPG assistance tool, the project’s primary focus is frontend architecture: extracting a deterministic domain layer that dramatically reduces UI complexity and state divergence in React applications.
This project is intentionally over-scoped in domain complexity to stress-test frontend architecture decisions.
It explores:
The goal is not to ship a complete game.
The goal is to demonstrate how a complex system can remain coherent, explainable, and maintainable as interaction density increases — a common failure point in large frontend codebases. This project prioritizes clarity under complexity, not framework cleverness.
This project is best evaluated by examining how rule changes propagate through the system. Most architectural decisions are visible in the domain layer and its relationship to the UI, rather than in feature completeness or visual polish.
At a high level, the system is structured as:
Domain (types, rules, selectors, updaters)
↓
React hooks & thin state layers
↓
UI components
React is treated as an integration layer; authoritative game logic is not implemented inside components or hooks.
All authoritative logic lives in a deterministic domain layer that can be:
The most challenging and intentional part of this project was designing a domain layer that is UI-agnostic, yet frontend-driven.
This inversion dramatically reduces:
app/
├── components/ # Thin UI & interaction panels
├── */.json # JSON templates (domain inputs: characters, equipment, etc.)
├── domain/ # Deterministic rules, factories, selectors
├── hooks/ # UI-facing access to domain/state
├── stores/ # Localized Zustand slices (non-authoritative)
├── actions.ts # Server actions (persistence boundaries)
└── page.tsx # Application entry point
⚠️ Note on Zustand Zustand is used sparingly for localized coordination. It is not used as a global derived-state store. All authoritative game logic remains in the domain layer.
The domain layer is authoritative, so most changes start there and flow outward. Data moves in one direction:
JSON templates (app/**/*.json) → Zod schemas (app/domain/types.ts) → factories (app/domain/factories.ts) → rules + lenses/projections + commands (app/domain/**) → stores/hooks → components
When implementing a feature or fix:
Heuristics:
app/domain/character/rules/** (or scene/rules/**); a stat the sheet edits also gets a lens registered in lenses/index.ts; a shape the UI renders is a view getter in character/lenses/** or a projection in scene/projections/** / item/projections/**.app/domain/**/commands/** returning a new aggregate.app/actions.ts) or redis.ts; treat persistence as I/O, not rule evaluation.Non-negotiable constraints:
All character data flows through the Character / CampaignCharacter aggregate.
Domain functions stay synchronous, pure (no I/O, time, or randomness unless explicitly modeled), and stable under recomputation.
Don't add a setter that bypasses the derived-stat invariant floor(0.5 * STR * DM + base) — edit the stored base term, not the derived output.
No component performs arithmetic on a domain number. Components render values and call handlers; if a component needs a + b, the domain owes it a getter. This is the rule that keeps the thesis honest — every rule leak this project has had (an armor tier table in JSX, an STA-regen formula in a header, a STR-mod damage formula duplicated between a panel and a command) began as one small expression inside a component that nobody thought was a "rule".
When the domain doesn't already expose the shape you need, that's the signal to add a view getter, not to compute it in the component. A view getter is a pure (c: Character) => RenderableShape — the same pattern as getStrikeTerms, extended past scalars to rows and tables (getDamageTiers, getWeaponAttackRows). The tell that you're about to leak a rule is a component reading a raw characteristic (STR, STA, TGH) — it's reading it in order to do arithmetic with it.
ESLint rules enforce the boundary mechanically (eslint.config.mjs), so this is a build failure rather than a code-review habit:
app/domain/** may not import from app/stores/**, React, or Zustand — the domain defines its own types and the store imports them, never the reverse.rules/, lenses/ and projections/ may not import commands/ or the bot — the read side never runs a button to find out an answer.app/domain/{character,item}/** may not form an import cycle — size and scale, then items, then the character's state, then AP prices; a file that imports upward mixes two of those and is split.app/components/** may not import from **/domain/*/rules/**, **/domain/*/lenses/**, **/domain/*/projections/** or **/domain/*/commands/** — go through a hook in app/hooks/. Type-only imports are allowed via import type. Inert modules (domain/types, domain/tables, domain/factories, , ) stay importable. is exempt: it's a stress-test harness whose whole purpose is to drive the lens registry and the store directly.Game mechanics exist to anchor abstractions, not to showcase feature breadth.
They were chosen specifically because they create non-linear, cascading effects, which are useful for testing deterministic modeling.
Examples include:
These mechanics ensure that small changes can have wide-reaching effects — exactly the kind of scenario that breaks naïve frontend state management.
git clone https://github.com/gbelow/udgs-runtime.git
cd udgs-runtime
pnpm install
Create .env:
UPSTASH_REDIS_URL=...
UPSTASH_REDIS_TOKEN=...
Run locally:
pnpm dev
Open:
http://localhost:3000
This project prioritizes long-term coherence and change tolerance over short-term feature velocity. The central architectural decision was to model the game as a deterministic domain that fully owns rules, invariants, and data interpretation, with the UI acting purely as a consumer of domain outputs. This allows rule changes to be implemented by modifying the domain layer alone, without requiring coordinated updates across components, stores, or persistence logic.
State in the application is treated as derived by definition: the domain describes how the system behaves, not merely the shape of stored data. As a result, changing data or rules implies updating the domain logic rather than patching downstream consumers. This consolidation was intentional—it trades flexibility at the edges for clarity at the core, and prevents the UI from silently diverging from the rules it is meant to represent.
Global state libraries were deliberately avoided for derived logic. Redux was rejected due to its boilerplate cost and focus on action-based mutation rather than memoized computation. Zustand is used selectively as a coordination layer, primarily to manage memoization boundaries and prevent unnecessary re-renders under heavy interaction. It does not own rules or derived values; those remain in the domain.
For persistence, Redis was chosen over a relational database because the system operates on self-contained aggregates rather than relational queries. Characters and related entities are loaded and saved as whole objects, without joins. This simplifies iteration but introduces the responsibility of handling data evolution explicitly. The system assumes that data shape changes are intentional and versioned, with the domain acting as the authoritative interpreter of stored data rather than relying on implicit schema guarantees.
JSON files are used for domain inputs (characters, equipment, rules tables) both for transparency and for ease of bulk editing. This choice made experimentation and reshaping faster during development and reinforced the separation between data definition and rule evaluation. While not optimal for all production scenarios, it was valuable for exploring how rule changes propagate through the system.
This architecture required more upfront design and refactoring than a feature-first approach. However, the resulting system is significantly more scalable: boundaries are explicit, responsibilities are clear, and future expansion is expected to be faster and safer precisely because the difficult decisions have already been centralized.
These tradeoffs were intentional.
Private project Not licensed for commercial or public distribution.
Made by Guilherme Below Full-Stack Engineer
domain/utilsdomain/play/diceBreakMe.tsx