Personal build

Life In Character

A Habitica-inspired RPG habit tracker, built as a personal engineering study in gamification, sprite layering, and Redux-at-scale.

Role
Solo — design, engineering
Timeframe
2025 – 2026
Stack
React NativeExpo SDK 54TypeScriptRedux ToolkitRedux PersistAsyncStorageReact Navigation (Material Top Tabs)
Life In Character avatar and stats screen
Context

Why this exists

Life In Character is a personal engineering build — not a product. It's a Habitica-inspired RPG habit tracker I built to push on three things: a feature-based Redux architecture large enough to feel real, a sprite-layered avatar system with hundreds of assets, and the gamification mechanics (XP, gold, health, daily reset) that make Habitica work.

The sprite assets are from the open-source Habitica project, licensed CC-BY-NC-SA 3.0. That license is the reason this app can't be commercialized — and it's also the reason the case study exists in the first place. It freed me to build the thing honestly as a study, without product pressure.

Key decisions

Interesting calls

  • 01

    Feature-based Redux slices, not a single store

    Six separate slices — habits, dailies, todos, player, character, inventory — each with their own selectors and reducers. Easier to reason about, easier to test in isolation, and surfaced a real insight: player state (XP, gold, health) needs to react to events from three other slices, so thunks became the clear boundary.

  • 02

    Redux Persist + AsyncStorage, no backend

    Everything lives on-device. No account system, no cloud sync. The build rule was 'if it doesn't need a server, it doesn't get one' — which kept the project scope honest and let me focus on the mechanics rather than the plumbing.

  • 03

    Three task archetypes from Habitica's model

    Habits (flexible positive/negative), Dailies (scheduled with streaks), Todos (one-shot). Tried collapsing them during prototyping — regretted it. The three archetypes exist because they model genuinely different behaviors; merging them made the UI worse, not simpler.

  • 04

    Sprite map with 460+ keys, layered avatar renderer

    The avatar is a stack of sprite layers — skin, hair, shirt, armor, helmet, weapon, pet — each driven by equipment state in the inventory slice. CharacterAvatar.tsx composes the layers in Z-order so equipping an item updates the render automatically.

  • 05

    Daily reset with missed-task penalties

    Dailies lose health when missed, which is the single most motivating mechanic in Habitica. Implementing it meant a scheduled check on app open: diff last-reset timestamp against today, apply penalties, reset streaks. Small feature, huge behavior impact.

Flow

Five screens, five decisions

  1. Character customization screen
    01

    Character

    The avatar is the product. Equipment from the inventory slice renders as Z-ordered sprite layers in real time — buy a helmet, see it immediately.

  2. Habits screen
    02

    Habits

    Flexible behaviors with +/- buttons and unlimited interactions per day. Hits the player slice on every tap — XP and gold for positive, health damage for negative.

  3. Dailies screen
    03

    Dailies

    Scheduled recurring tasks with streak tracking. The real mechanic is what happens when you don't check them: health loss on next app open.

  4. Todo list screen
    04

    To-Dos

    One-shot tasks with optional due dates. Rewards are bigger than habits or dailies because the cost of completion is higher.

  5. Rewards shop screen
    05

    Rewards Shop

    56 purchasable items across four categories — armor, head, weapons, pets. Closes the loop: effort becomes gold becomes avatar becomes motivation to keep going.

Architecture

How the pieces fit

Layer 1Presentation
HabitsScreen
+/- interactions
DailiesScreen
streaks + reset
TodosScreen
one-shot tasks
CharacterScreen
avatar + stats
RewardsScreen
shop + equip
Layer 2Store (Redux Toolkit)
habits/
slice + selectors
dailies/
slice + selectors
todos/
slice + selectors
player/
xp, gold, health, level
character/
customization
inventory/
items + equipped
Layer 3Rendering
CharacterAvatar
Z-ordered sprite layers
spriteMap.ts
460+ sprite keys
StatsBar
player state display
Layer 4Persistence
Redux Persist
slice rehydration
AsyncStorage
device-only

There is no backend. State lives in six slices, persists to AsyncStorage via Redux Persist, and rehydrates on app open. The sprite renderer reads from inventory slice and composes layers automatically — equipping an item is a single dispatch.

What I learned

Honest outcome

The biggest lesson was about scope. Habitica has been shipping for over a decade; my version is a system study, not a rival. Keeping that straight meant resisting feature creep — I don't need parties, challenges, or sync, because the point was to understand how the mechanics hold together, not to compete.

The sprite-layering system was the unexpected win. Building it forced a clean separation between "what's equipped" (inventory slice) and "what's rendered" (presentation layer), and that separation turned out to be the cleanest architecture decision in the project.

Attribution: Sprite and pixel-art assets are from the Habitica project (HabitRPG), licensed under CC-BY-NC-SA 3.0. This project is non-commercial and shares the same license. Source: LeoZai613/Life-In-Character.