Another Life: A Life Simulator Across Ten Worlds

A bilingual life simulator with branching stories, reproducible progression, and persistent memories across playthroughs.

Another Life showing a desktop decision scene with a character's current story and available actions
The current scene and its choices take priority, with character details and earlier history available when needed.

Build a life through choices

A character might grow up in a modern city, join a magical academy, or survive for centuries as a vampire. I designed and built Another Life to explore how a few simple controls could support very different lives, with choices that continue to matter after their immediate outcome.

Each life begins in a randomly assigned world. Players choose three talents and distribute twenty points across intelligence, charisma, physique, wealth, and luck. The simulation advances through the years, presenting events and pausing for decisions. Ten worlds supply distinct settings, from ancient tribes and medieval kingdoms to space travel and disaster recovery.

The application uses Next.js, React, and TypeScript, with the simulation running in the browser. Its compiled registry contains over 12,000 scene and outcome nodes, assembled from typed story definitions and structured variations. The central challenge was making those pieces form coherent experiences.

Open Another Life

Another Life's English talent draft showing the randomly assigned world and three selected talents
Each life begins with a random world and a draft of three talents.

Let earlier decisions shape later events

A business venture can lead to a partnership, a theft, an investigation, and an attempt to recover the loss. Helping an alien visitor can lead to a later escape from an abandoned airport. These connections depend on the people, possessions, and actions recorded along the way.

Events declare requirements such as world, age, attributes, inventory, identity, relationships, and previous outcomes. The engine filters out ineligible events before applying scheduling and weighted selection. Wealth might make a venture possible; a tool might improve the odds of a rescue. Required consequences and active story continuations receive dedicated treatment.

Repeated play also changes discovery. Previously encountered ordinary entrances receive lower weights, and frequently visited worlds become less likely. Necessary follow-ups retain their normal treatment, so encouraging new experiences does not break a story already underway.

The interface keeps the current scene and actions visible. Attributes, possessions, and earlier history expand on request, allowing a growing simulation to remain comfortable to read.


Give each action the right amount of time

A yearly simulator needs more than one clock. Training can take years, but someone trapped in a burning building cannot wait until the protagonist’s next birthday for a rescue decision.

I separated annual developments, immediate outcomes, and linked continuations. A pursuit or duel can involve several decisions at the same age. Preparation and rebuilding advance through later years. Each year has one primary history entry, with ordered scenes attached for events within it.

This distinction also shapes restoration. A save preserves the exact pending choice and the preceding actions in that scene. Family progression uses actual birth or adoption years, while later callbacks refer to the age and action that established them. The timeline forms part of the persisted story state.


Compile stories into rules the engine can check

Long adventures are authored as graphs of scenes, actions, outcomes, and successor nodes. Compilation produces stable event identifiers and the conditions needed to enter each branch. Registration rejects duplicate identifiers and invalid graph connections.

A difficult class of errors appears where stories reuse context. A later scene might mention a partner who never joined or a tool that was never collected. New long adventures therefore declare causal contracts: which facts a scene needs, establishes, or removes. Changes to possessions, relationships, attributes, and other state include Chinese and English evidence from the displayed prose.

Validation follows each declared branch and checks whether required facts exist before use. It also verifies that an effect’s explanation appears in the final displayed text. These contracts make specific inconsistencies inspectable; editorial review and actual play paths assess whether the complete story makes sense.


Preserve the history behind the state

The engine uses a seeded random generator and saves its current state with the life record. Equivalent simulation state, choices, and content version reproduce the same progression. Refreshing preserves an already resolved outcome and resumes the sequence.

Probability explanations preserve their own history. When an event is drawn, the engine records the modifiers that actually matched. Later improvements to a character’s attributes do not rewrite why an earlier opportunity appeared.

Recorded events and outcomes establish relationship, organization, and achievement eligibility. This provenance connects later opportunities to the experiences that earned them.

When a life ends, authored rules generate a retrospective from its recorded decisions and consequences. The summary preserves the actual ending and is generated locally, including offline.


Bring the hidden story into the interface

Recurring clues across the worlds lead to the Harvester, an antagonist who steals memories. Relics, techniques, and echoes of consenting companions can survive completed lives and support a later confrontation. Each memory retains the life, world, age, and actions that established it. A character’s inheritance is frozen at birth, preserving a clear boundary between preparation in earlier lives and discoveries made now.

The confrontation brings those histories back into play. Companions can recall an actual rescue, choose to stay, or withdraw. Their presence and losses affect subsequent tactics. The player can destroy the threat, seal it, or seize its core; taking that power permanently consumes the companion echoes still present.

I extended the conflict into the interface itself. The Harvester addresses the player and crosses out Attack. Continuing to press, holding the control, or calling on an eligible companion can restore it. Pointer, touch, and keyboard input have bounded recovery paths, with reduced-motion support. Restoring the control returns the decision to the player, who explicitly confirms the attack.

That interference is saved with the scene. Refreshing or changing languages preserves its state, while recovering control leaves the character’s age and random sequence intact.


Keep play available across languages and connections

Chinese and English presentation share canonical event identifiers and choice data. Switching language renders the same history without altering its decisions, ages, or random state. Bundled translations remain available offline, while source fingerprints and contextual checks help detect missing or stale copy.

Local saves contain the active life, discoveries, achievements, memories, and completed histories. JSON import validates structure and recorded transitions before restoration. Export provides a separate backup and a way to move progress between browser origins.

Optional cloud backup stores complete snapshots in private Vercel Blob storage. A random recovery code identifies the save, while the Blob token remains on the server. Delayed, serialized writes preserve upload order, and local saving continues through network failures. Restoration selects a complete snapshot using its saved timestamp.

The PWA caches application resources after an online visit, with private save requests excluded. The same game can then continue in an installed window or an offline browser.

Another Life's Chinese interface showing a current story and choices on a phone-sized screen
Chinese presentation uses the same saved life and underlying choices.

Validate stories as well as code

Content checks cover references, registered items and identities, age and probability ranges, branch links, continuation limits, causal evidence, and translation coverage. Regression tests exercise actual choices, annual and immediate timing, world transitions, family progression, save restoration, and persistent memories.

Validation runs before the normal test and production-build commands. GitHub Actions repeats content checks, tests, TypeScript checking, and the production build. This makes the content registry part of the application’s verification workflow.

The project brought narrative design into problems I enjoy solving in software: a rescue depends on a friendship, the friendship depends on an earlier choice, and all three must survive saving and switching languages. Designing those connections gave Another Life both its variety and its engineering depth.

Open Another Life