Table of Contents
This will be the first in a series of posts outlining Lilaya’s architecture.
Intro
As someone who has struggled with ADHD and depression, I’ve often found myself searching for tools to help increase my self-awareness and foster personal growth. Traditional journaling never stuck, and most productivity apps felt disconnected from the emotional aspects of self-improvement. I knew I needed something more engaging and insightful.
What struck me was how easily mobile games captured my attention. While couldn’t bring myself to sit down and write a few notes, I was more than willing to exert energy to complete my daily quests. This led me to a crucial question: How can we leverage game elements to incentivize personal development in a meaningful way?
This realization was the spark that led to the creation of Lilaya. On one hand, I wanted to build a tool that could help people like me – those who struggle with focus, motivation, and consistency – to increase their self-awareness so that they can create change in a lasting way. On the other hand, I wanted it to be a game with the same emotional depth as our favorite RPGs. I wanted a game where the mechanics weren’t just superficial add-ons, but integral parts of the user’s journey to self-improvement.
Drawing inspiration from the Bhagavad Gita and yogic traditions, I’m working to address these challenges by creating a system where user inputs (thoughts, emotions, actions) directly shape the game world. This requires careful consideration of data models, event-driven architecture, and state management.
In this series of blog posts, I’ll be diving into the technical and design decisions behind Lilaya. We’ll explore everything from the core gameplay loop to the intricacies of offline-first development and secure data management.
By sharing this process, I hope to spark discussions about innovative approaches to app development, particularly in the realm of personal development tools. Whether you’re a fellow developer, a UX designer, or simply interested in the intersection of technology and personal growth, I invite you to join me on this journey of creating Lilaya.
Core Concept and Gameplay
The fundamental concept of Lilaya is deeply rooted in the wisdom of the Bhagavad Gita, particularly chapters 3.40-3.43. These verses provide profound insights into the nature of desire and its role in human suffering, which form the basis of our core gameplay loop.
The central challenge in designing Lilaya is creating a system where real-life personal development seamlessly translates into meaningful gameplay. Here’s how we’re approaching this:
Conceptual Foundation
The Gita teaches that desire, often arising from our senses, can cloud our judgment and lead us astray from righteous action. It says that desire is the root of suffering, tricking us into believing that what feels good is the right thing to do. The solution, as Krishna advises Arjuna, is to restrain these senses and overcome desire through discernment and self-control.
We must be aware of our thoughts, actions, and feelings so that we may change them.
See here for more info The Nature of Desire
Core Loop
This wisdom translates into our core gameplay loop:
- Identify Desire: Users journal their thoughts and feelings, bringing awareness to their desires and impulses.
- Reflect: Users engage in reflection, analyzing the root of their desires and their potential consequences.
- Choose Action: Based on this reflection, users choose an adaptive behavior or thought pattern to overcome the desire.
- Practice: Users implement this choice, both in-game and in real life, strengthening their ability to act wisely.
Implementation
Translating this philosophical concept into a game mechanic presented an exciting challenge. Here’s how we’ve implemented it:
- Journaling (Identify Desire):
- Users input their thoughts, feelings, and actions through a simple journaling interface.
- Desire Mobs (Manifestation):
- User identifies the initial desire that sparked an entry
- Identified desires manifest as “desire mobs” in the game world
- These mobs represent the user’s internal struggles, giving. tangible form to abstract concepts.
- Reflection (Battle System):
- Users “battle” these mobs through a reflection process.
- They’re prompted to analyze the desire, its origins, and potential consequences.
- Users can choose from various coping techniques to reflect (skills)
- These strategies are based on cognitive-behavioral techniques and mindfulness practices.
- Action and Feedback (Growth):
- Users choose a skill re-action, an adaptive coping mechanism that improves their life
- The game provides feedback, showing the effects of their choices on their in-game character and world.
Key Technical Requirements
Offline-First Functionality
Lilaya is designed to be a constant companion, always available like a trusty notebook or a classic GameBoy. The core gameplay loop - journaling, reflection, and personal growth - should never be interrupted by a lack of internet connection. Just as palm pilots and paper notebooks have always functioned without the need for connectivity, Lilaya aims to provide a seamless, frustration-free experience regardless of network status.
Developing Lilaya presents several unique technical challenges due to its nature as an offline-first personal development game.
Here are the key technical requirements we’re addressing:
- Local Data Storage: We’re using IndexedDB for client-side storage, allowing for complex querying and efficient storage of large datasets.
- Offline Computation: Game logic must run locally on the device
- State Management: The app needs to maintain a consistent game state without relying on server communication. We’re using zustand for a small footprint
- UI/UX Considerations:
- The user interface must be designed to work flawlessly offline, with no “loading” states that depend on network requests.
- We need to implement optimistic UI updates to provide immediate feedback to user actions, even when those actions would typically require server validation in an online-first app.
b) Conflict Resolution: When syncing data across devices, we need robust conflict resolution. We’re implementing a custom merge algorithm that considers the nature of personal development data.
c) Progressive Enhancement: The app is designed to work entirely offline, with online features (like cross-device sync) added as progressive enhancements.
By prioritizing offline-first functionality, we ensure that Lilaya remains a reliable tool for personal development, available whenever and wherever the user needs it - just like flipping open a notebook or powering on a GameBoy.
Cross-Device Synchronization
While Lilaya is primarily designed as an offline-first application, we’re living in the 21st century. Users expect to access their data anywhere like all their other apps.
Implementing cross-device synchronization presents several challenges, especially considering our commitment to privacy and offline functionality.
- UX Considerations:
- We want servers to backup the user data, as a SLA in case devices become unusable.
- syncing across devices should be easy. As simple as typing in sync codes between two devices
- Efficient Data Transfer:
- We need to minimize the amount of data transferred to reduce bandwidth usage and sync time.
- Implementation: We’ll use a differential sync system that only transfers changes since the last sync, rather than the entire dataset.
- Conflict Resolution:
- When changes are made on multiple devices while offline, conflicts may arise during synchronization.
- We’ll implement a robust merge strategy that can handle conflicts in journal entries, game state, and user preferences.
- The conflict resolution algorithm will need to consider the nature of the data (e.g., preserving the integrity of journal entries) while resolving differences.
- Encryption:
- All data must be encrypted before leaving the device to maintain our privacy commitments.
- We’ll use end-to-end encryption to ensure that even if data is intercepted during transfer, it remains unreadable.
- Offline Changes Queue:
- Changes made while offline need to be queued and synced when a connection becomes available.
- We’ll implement a reliable queuing system that can handle network interruptions and resume syncing where it left off.
- Sync Triggers:
- Syncing should occur automatically when a connection is available, but also allow manual syncing.
- We’ll need to implement background sync capabilities while being mindful of battery usage.
- Version Control:
- To handle potential conflicts and allow for feature updates, we’ll need a versioning system for our data structures.
- This will help ensure compatibility across different versions of the app that might be installed on different devices.
- Current Development Challenges
As we progress with Lilaya’s development, we’re tackling several interesting challenges:
a) Performance Optimization: Ensuring smooth performance with potentially large datasets on mobile devices.
b) Scalability of Game Mechanics: Designing a system that can easily incorporate new game elements and mechanics as the app evolves.
c) Balancing Gameplay and Personal Development: Continuously refining the connection between user inputs and game elements to ensure meaningful representation.
d) Cross-Platform Development: Utilizing React Native for a shared codebase while optimizing for platform-specific performance.
Privacy and Data Security
Personal journals often contain intimate thoughts and feelings. As developers, we have a responsibility to protect this sensitive information. The simplest and easiest way to ensure privacy is to simply encrypt end-to-end, working the data only on a secure client.
Since the app works offline, we don’t need a server or other replication nodes to read the data. All the processing is done client-side. For cross-device syncing, we only need to sync blobs.
Our approach to privacy and data security in Lilaya is founded on the principle that the user’s data belongs solely to them.
- Local-First Data Storage:
- By default, all user data is stored locally on the device.
- This approach ensures that sensitive information never leaves the user’s device without their explicit consent.
- End-to-End Encryption:
- In cases where users opt for cross-device synchronization, we implement end-to-end encryption.
- All data is encrypted on the device before any network transmission occurs.
- We’re using the Web Crypto API for client-side encryption, ensuring that even if data is intercepted during transmission, it remains unreadable.
- Zero-Knowledge Architecture:
- Our server architecture is designed with a zero-knowledge principle.
- Even when data syncing is enabled, the server never has access to unencrypted user data.
- This means that all data processing, including the generation of game elements from journal entries, happens entirely on the client-side.
User Control:Users have the option to keep all their data entirely local, never syncing to any server.Clear, understandable privacy settings allow users to control exactly what data is stored and how it’s used.
- Minimal Data Collection:
- We only collect the bare minimum of data necessary for the app to function.
- User data such as journaling entries is never read by us. The game only ever uses this data offline.
- Any analytics or crash reporting is strictly opt-in and anonymized.
By prioritizing privacy and security, we aim to create a trusted space where users feel safe to express their thoughts and feelings, knowing their personal data is protected. This approach not only respects user privacy but also aligns with growing global privacy regulations.
Choosing the Tech Stack
For an offline-first application, a PWA fits the bill. With a PWA, we can have it installed on any device with a modern browser and work offline.
Offline First PWA with Remix
For our PWA, we will choose vite-pwa, a framework specifically for this purpose. It has extra support out-of-the-box to support common offline patterns.
Zustand for State Management
We’ll use zustand for state management. It’s lightweight, simple, and performative
RxDB for Offline Database and Replication
We’ll use RxDB as our client-side database.
RxDB is an excellent choice for Lilaya’s data management needs:
- Offline-First: RxDB is designed with offline-first applications in mind, perfect for our use case.
- IndexedDB Interface: It uses IndexedDB under the hood, providing a more developer-friendly API while leveraging the browser’s powerful storage capabilities.
- Real-Time Queries: RxDB’s reactive architecture aligns well with React’s paradigm, allowing for efficient updates to the UI as data changes.
- Cross-Device Replication: The built-in replication features will greatly simplify our implementation of data syncing across devices.
Service Workers for Offline Capabilities
We’ll implement custom service workers to handle caching strategies and ensure smooth offline functionality.
This tech stack provides us with a robust foundation for building Lilaya:
- The combination of Next.js, React, and TypeScript gives us a powerful and type-safe frontend framework.
- Tailwind CSS and shadcn components allow for rapid and consistent UI development.
- Zustand provides efficient state management.
- RxDB handles our offline data storage and synchronization needs.
- Service workers will ensure the app functions seamlessly offline.
Architecture Overview
- Local-first data management
- Sync mechanism for cross-device usage
- PWA features for installability
Lilaya’s architecture is designed to support an offline-first, cross-device personal development game. Here’s a breakdown of the main components and how they interact:
User Interface Layer
- React Components: Built using React and TypeScript
- UI Framework: Tailwind CSS for styling, with shadcn components for consistent UI elements
- Rendering: Client-side rendering for dynamic content, with potential for static generation of non-user-specific content
State Management Layer
- Zustand: Manages global application state
- Local Component State: React’s useState and useReducer for component-specific state
Data Layer
- RxDB:
- Serves as the primary data store using IndexedDB
- Handles local data persistence
- Manages real-time queries for reactive UI updates
- Facilitates data replication across devices
Offline Capabilities
- Service Workers:
- Cache static assets and API responses
- Enable offline functionality
- Handle background sync when coming back online
Sync and Replication
- RxDB Replication: Manages data synchronization across devices
- Custom Sync Logic: Handles conflict resolution and ensures data integrity
Game Logic
- Core Game Loop: Implemented in TypeScript, potentially as a separate module
- State Updates: Trigger Zustand actions to update global state
API Layer (if needed)
- API Routes: For any server-side operations when online
- Offline Fallbacks: Local processing when offline
Data Flow
- User interacts with React components
- Interactions trigger Zustand actions or local state updates
- State changes cause React to re-render affected components
- Data mutations are persisted to RxDB
- RxDB triggers real-time updates to subscribed components
- When online, RxDB handles replication to sync data across devices
Offline-First Strategy
- All core functionality works without an internet connection
- Service workers cache necessary assets and data
- RxDB stores all user data locally
- Sync operations are queued when offline and executed when online
Cross-Device Synchronization
- RxDB manages data replication across devices
- Custom conflict resolution strategies ensure data consistency
- Synchronization happens in the background without disrupting user experience
Security Considerations
- All sensitive data is encrypted before storage or transmission
- Authentication and authorization handled client-side when offline
- Secure protocols for data transmission when online