I’ve recently started working on a game project called We Were Earth.
The project is an isometric RPG prototype built with Phaser 3 and TypeScript. It is currently focused on proving out the core systems that will eventually support a larger story-driven game: movement, interaction, dialogue, inventory, save/load, scene transitions, and a reusable structure for future rooms and chapters.
The game is still early, but it has already become one of the more interesting technical projects I have worked on because it sits somewhere between software architecture, game design, storytelling, and user experience.
With business applications, the goal is usually to make workflows reliable, efficient, and clear. With games, the technical foundation still matters, but the feel of the interaction matters just as much. Movement, timing, dialogue pacing, menu behaviour, and feedback all affect whether the world feels alive.
What the Prototype Does So Far
The current version of We Were Earth starts with a boot flow that loads into a main menu. From there, the player can start a new game or continue from a saved state.
The game currently includes:
- A boot scene and main menu
- A short intro text sequence
- A playable isometric bedroom scene
- Click-to-move player movement
- Automatic pathfinding across the room
- Interactive furniture tiles
- Dialogue that advances line by line
- Dialogue choices with meaningful outcomes
- Confirmation text after player decisions
- Inventory support
- A pause menu with save, resume, and exit options
- A coordinate overlay for debugging
- Save/load support using browser localStorage
The first playable scene is a 14×14 isometric quarters room. The player can click around the room, move between tiles, interact with highlighted furniture, make choices, pick up items, and leave the room through an exit door after a confirmation prompt.
It is a small slice of gameplay, but it touches many of the systems that will be needed as the game grows.

Why Phaser?
I chose Phaser 3 because it gives me a strong 2D game framework while still keeping the project close to the JavaScript and TypeScript ecosystem.
That makes it a good fit for the way I like to build. I can structure the game using familiar TypeScript patterns, organise the project into modules, and still have access to game-specific features like scenes, input handling, tweens, rendering, and UI containers.
Phaser gives enough structure to build a real game, but it does not force everything into a heavy engine workflow. That suits this project well because I want control over the architecture while still being able to move quickly.
Building an Isometric World
One of the core technical pieces in the prototype is the isometric grid system.
The game logic uses grid coordinates, while the renderer converts those into screen coordinates. This allows the game to reason about movement, collisions, interactions, and pathfinding in a clean tile-based way, while still presenting the room as an isometric space.
The current tile dimensions use a 2:1 isometric ratio, with tiles rendered as diamonds.
The grid system handles:
- Converting grid coordinates to screen coordinates
- Converting screen clicks back into grid positions
- Building diamond-shaped tile polygons
- Detecting which tile the player clicked
- Drawing walls, interaction tiles, and the player marker
- Moving the player smoothly along a path
This was one of the most satisfying parts of the prototype because it turns simple tile data into something that feels spatial and interactive.
Pixel Art
I’ve been using Claude’s MCP integration tooling with Pixellab.ai to dynamically use text commands to generate pixel art for me. Once the pixel art has been generated, I then use Aseprite to tidy the artwork up (Aseprite also has a Pixellab.ai module embedded into it for use). The experience has been fun so far!
Click-to-Move Pathfinding
The prototype uses click-to-move navigation.
When the player clicks a tile, the game checks whether the tile is valid and then uses pathfinding to move the character there. The pathfinding uses a 4-directional A* algorithm with a Manhattan distance heuristic.
The game treats wall tiles and interaction furniture as blocked. That means the player walks around furniture rather than standing on top of it. When the player clicks an interactive object, the game finds the nearest adjacent walkable tile, moves the player there, and then triggers the relevant dialogue.
This creates a more natural interaction flow:
- The player clicks an object.
- The game finds a valid approach tile.
- The player walks to that tile.
- The dialogue or interaction begins.
Even in a simple room, this makes the world feel much more intentional.
Dialogue and Choices
The dialogue system is one of the main foundations of the game.
Dialogue entries are stored in JSON and played line by line through a dialogue manager. The system supports shared text snippets, choice branching, flags, named choices, inventory actions, and confirmation text after decisions.
This means dialogue is not just static text. It can change based on the game state.
For example, an interaction can:
- Display a sequence of dialogue lines
- Set a flag in the save state
- Offer the player a list of choices
- Add or remove inventory items
- Save a named decision
- Show confirmation text after the choice
- Use flag-gated variants later
This structure is important because We Were Earth is intended to be a narrative-driven game. Player decisions, collected items, and prior interactions should be able to affect what the player sees later.
By putting dialogue data in JSON, I can separate writing content from scene code. That makes it easier to add new interactions without constantly changing the underlying game logic.
Inventory and Player State
The prototype also includes a basic inventory system.
The player can open the inventory screen with the I key and review collected items. Inventory actions are tied into the dialogue choice system, so a choice can add or remove an item from the player’s inventory.
The inventory system supports stacking, so adding an item that already exists increases the quantity rather than creating duplicates.
This is still early, but it provides the foundation for future mechanics such as:
- Collectable story items
- Tools
- Access items
- Crew-related objects
- Mission equipment
- Puzzle items
The save state currently tracks the player’s location, position, flags, inventory, named choices, and dialogue progress.
Save and Load
Save/load is already built into the prototype using browser localStorage.
The player can save from the pause menu, and the game can restore the saved state when Continue is selected from the main menu.
The saved game state includes:
- Current location
- Player position
- Flags
- Inventory
- Named choices
- Dialogue progress
- Save schema version
I also added save migration support so that as the game state changes over time, older saves can be upgraded into the newer format.
That might seem excessive for an early prototype, but it is a useful habit. Games evolve quickly, and save data can become painful to manage if versioning is not considered early.
Scene Architecture
The project is structured so that scenes orchestrate behaviour, but do not contain all of the low-level logic.
The codebase is split into clear areas:
stylefor theme and visual constantstypesfor shared TypeScript typesdialoguefor dialogue data and dialogue runtime logicmodulesfor engine-independent systems like save, inventory, and isometric mathscenesfor Phaser scene orchestrationuiElementsfor reusable UI widgetsutilitiesfor helpers such as pathfinding and grid utilities
This separation keeps the project easier to grow.
For example, the isometric coordinate math is separate from Phaser scene code. Inventory helpers operate on plain TypeScript data. Dialogue resolution is handled separately from the UI that displays it. Scene input is wired through its own input system and cleaned up safely when scenes shut down.
That kind of structure matters because game prototypes can become messy very quickly. It is easy for everything to end up inside one giant scene file. I wanted to avoid that early.
Lazy Scene Loading
The project also uses lazy dynamic imports for scenes.
The game starts with a boot scene, then loads the main menu. From there, likely next scenes can be prefetched shortly after the menu loads.
This keeps the initial startup path clean and gives the project a structure that can scale as more rooms, chapters, and cutscenes are added.
The current flow looks roughly like this:
Boot Scene
→ Main Menu
→ Intro Text
→ Quarters Room
Over time, this can expand into a larger scene graph without needing every scene to be loaded immediately at startup.
UI Systems
The prototype includes several reusable UI elements:
- Dialog box
- Pause menu
- Dialogue choice menu
- Save toast
- Loading overlay
- Coordinate overlay
Each UI widget is responsible for creating and managing its own Phaser container. They also clean up their own listeners when the scene shuts down.
The dialogue choice menu measures text before laying out the panel, so the menu can automatically size itself based on the available choices. The loading overlay is drawn at the highest depth so it always appears above everything else and blocks input during transitions.
These are small details, but they make the project feel more like a proper game framework rather than a one-off scene.
Debugging Tools
I added a coordinate overlay that can be toggled with the C key.
This shows axis labels and the player’s current grid position, which is useful when building rooms, placing interactions, testing movement, and debugging layout issues.
For a tile-based game, this kind of simple debugging tool saves a lot of time. It makes it easier to understand what the game thinks is happening, not just what appears on screen.
Current Controls
The prototype currently supports:
| Input | Action |
|---|---|
| Left click | Move to tile or interact with furniture |
| SPACE | Advance dialogue |
| ENTER | Open or close the pause menu |
| ESC | Close the pause menu |
| ↑ / ↓ | Navigate pause menu |
| I | Open inventory |
| C | Toggle coordinate overlay |
The controls are intentionally simple at this stage. The focus is on proving out the interaction model before adding more complexity.
What Comes Next
The next step is to keep expanding the world and the systems around it.
The project structure already supports adding new rooms, new dialogue files, new interaction tiles, and new scenes. From here, I can continue building out the first chapter of the game and start layering in more story, more character interaction, more environmental detail, and more meaningful choices.
Future areas I would like to explore include:
- Additional rooms and scene transitions
- More complete character art
- Environmental storytelling
- Crew systems
- Skill systems
- More complex inventory usage
- Branching dialogue
- Mission decisions
- Sound and music
- A more complete save/load interface
The long-term goal is for We Were Earth to become a story-driven isometric RPG with a strong sense of mystery, exploration, and consequence.
Final Thoughts
We Were Earth started as a way to experiment with Phaser.js, but it has quickly become a much deeper technical and creative project.
The prototype already includes movement, pathfinding, dialogue, inventory, save/load, scene loading, and reusable UI systems. More importantly, it has a structure that should allow the game to grow without becoming unmanageable.
It has also been a great reminder that game development is not just about rendering things on screen. It is about designing systems that work together to create a feeling.
For now, the game is still small: a room, a character, a few interactions, and the beginning of a story.
But the foundation is there.