
I Built a Tamagotchi That Keeps Living When the App Is Closed
How I turned a nostalgic desktop toy into a deterministic real-time simulation with Tauri, React, TypeScript, PixiJS, persistent care history, branching evolution, and consequences that continue offline.
The original Tamagotchi was a tiny plastic object with an oversized emotional footprint. Three buttons, a few pixels and a clock were enough to make a creature feel present even when it was not on the screen.
I wanted to rebuild that feeling as a Windows desktop companion—not as a dashboard, not as a browser game inside a rectangle, and not as a collection of progress bars wearing a cute skin. I wanted a small object that lives on the desktop, asks for attention, develops according to how it is treated and keeps changing when the application is closed.
That last requirement changed the entire project.
The result is a Windows-first Tamagotchi built with Tauri v2, React, TypeScript, Vite, PixiJS, Zustand and SCSS. It has a frameless draggable shell, tray behavior, pixel-art animation, persistent saves, attention calls, sickness, poop, weight, discipline, sleep schedules, branching evolution and terminal death.
This post explains both sides of it: the toy the player experiences and the simulation that makes the toy feel alive.
The product constraint: it must feel like an object
The visual brief was simple: opening the application should feel like placing a small digital toy on the desktop.
That ruled out the usual application shell. There is no navigation sidebar, settings dashboard or large title bar. The window is transparent, frameless, fixed in size and draggable from the body. The pet lives inside an LCD-like screen. A, B and C controls work as physical buttons and as keyboard shortcuts. Closing the window hides it to the system tray instead of ending its life.
The interaction model stays deliberately narrow:
| Control | Meaning |
|---|---|
| A | Move through actions or choose left in the guessing game |
| B | Confirm, advance a status page or choose right in the game |
| C | Cancel, return or begin again after death |
| Tray | Show, hide, reset or quit the companion |
| Right click | Change shell color or keep the toy always on top |
This constraint matters beyond aesthetics. A virtual pet works because it is legible at a glance. The interface should disappear quickly enough for the player to think about the creature rather than the software around it.
The rule that makes it alive: closing is not pausing
The application does not need to run continuously for the pet to live continuously.
Every save records the last simulated timestamp. When the app opens again, regains focus or wakes after the computer has slept, it calculates the elapsed interval and advances the domain state to the present.
save at t₀
↓
application closes or Windows sleeps
↓
real time passes
↓
resume at t₁
↓
simulate(t₀ → t₁), then persist the new state
That interval can contain hunger decay, happiness loss, sleep, energy recovery, poop, sickness damage, weight damage, attention mistakes, evolution or death. Reopening the window is not a reset; it is a reunion with whatever happened while you were away.
There are three related clocks, and keeping them distinct was important:
| Clock | What it controls |
|---|---|
| Real elapsed time | Offline catch-up and ordinary simulation |
| Unpaused age | Life stage, evolution windows and expected lifespan |
| Pet clock | Scheduled sleep and wake times; setting it starts the internal day at 08:00 |
The explicit pause command freezes age, evolution, hearts, poop and stat simulation. Closing the app does not. That difference is the foundation of the game.
A lifecycle measured in real care
The pet begins as an egg and can live for nearly a month. Evolution is not chosen from a menu. It is an accumulated consequence of care.
| Unpaused age | Stage | Possible form |
|---|---|---|
| 0–5 minutes | Egg | Egg |
| 5 minutes–24 hours | Baby | Sprout |
| 24–72 hours | Child | Bud or Pebble |
| 3–7 days | Teen | Spark, Leaflet or Mote |
| 7–21 days | Adult | Guardian, Bloom, Drifter or Rascal |
| 21+ days | Elder | Sage, unless the adult became Starborn |
| Expected lifespan | Death | Determined by lifetime care |
The baby always becomes Sprout. After that, the care-quality score selects each branch:
| Care score | Child | Teen | Adult |
|---|---|---|---|
| 85–100 | Bud | Spark | Guardian |
| 65–84 | Bud | Leaflet | Bloom |
| 55–64 | Pebble | Leaflet | Drifter |
| 40–54 | Pebble | Mote | Drifter |
| 0–39 | Pebble | Mote | Rascal |
Once an ordinary adult form is selected, day-to-day score changes do not replace it. The adult is a record of the care that led there, not a live rank badge.
The secret path: Rascal to Starborn
Poor early care is not necessarily the end of the story. A Rascal has one difficult redemption path between days 9 and 12.
To become Starborn, every condition below must be true at the same time:
| Identity and history | Current condition |
|---|---|
| Adult form is Rascal | Hunger is 4/4 hearts |
| Age is between 9 and 12 days | Happiness is 4/4 hearts |
| Discipline is 100% | No poop is present |
| No lifetime lights mistakes | Pet is not sick |
| Lifetime poop neglect is 15 minutes or less | Cleanliness, health and happiness are all at least 95% |
I like this branch because it makes evolution more than a simple “good score gives rare character” ladder. Starborn requires recovery, attention and a clean history in the areas that matter most. A neglected adult can transform, but only through an exacting stretch of care.
The visible needs and the systems underneath them
The player sees familiar needs: hearts, weight, discipline, illness, poop, mood and sleep. Under the surface, percentage-based stats provide enough resolution for gradual simulation.
| System | Natural behavior | When it becomes dangerous |
|---|---|---|
| Hunger | Hearts decay over time | Below 28% begins damaging health; zero requests attention |
| Happiness | Hearts decay over time | Below 24% damages health; below 35% produces a sad mood |
| Cleanliness | Falls while awake and faster around poop | Below 28% damages health; below 22% produces anger |
| Health | Recovers slowly under good care | 52, 38 and 20 trigger escalating sickness; zero is death |
| Energy | Falls awake and recovers asleep | Below 20% damages health; below 18% forces a sleeping mood |
| Discipline | Rises after correct responses to false calls | Changes care score, evolution and lifespan |
| Weight | Increases with food and only falls through games | Every pound above the stage target drains health continuously |
Each visible hunger or happiness heart represents 25%. During the baby’s demanding first hour, hunger loses a heart every 15 minutes and happiness every 20 minutes. After that, the cadence relaxes to 90 and 120 minutes respectively.
That first hour is intentionally intense. It establishes the relationship quickly, then gives the player room to settle into a longer routine.
Every helpful action has context
The buttons are not independent “increase stat” commands. Most actions solve one problem while creating another.
| Action | Immediate benefit | Tradeoff |
|---|---|---|
| Meal | +1 hunger heart | +1 lb and −5 cleanliness |
| Candy | +1 happiness heart | +2 lb and −4 cleanliness; dangerous at full happiness |
| Game: 0–2 correct | −1 lb | −14 energy and −8 cleanliness |
| Game: 3–4 correct | −1 lb and +1 happiness heart | −14 energy and −8 cleanliness |
| Game: 5 correct | −1 lb and +2 happiness hearts | −14 energy and −8 cleanliness |
| Toilet with poop | Clears every pile, +36 cleanliness and +5 health | No direct cost |
| Toilet without poop | +12 cleanliness | No direct cost |
| Rest | +26 energy | No meaningful lasting happiness penalty at present |
| Medicine while sick | Reduces sickness by one level and restores 14 health | −4 energy |
| Correct scold | +25 discipline | Only works for a genuine discipline call |
| Incorrect scold | Nothing | −1 happiness heart |
| Lights | Enables protected scheduled sleep when off | Lights off outside bedtime do not create protection |
The game becomes the safest way to restore happiness because it also removes weight. Candy is faster, but repeated convenience accumulates a cost. That makes the care loop a set of small judgments instead of a rotation through whichever meter is lowest.
The quiet killer is weight
Weight is the most dangerous hidden mechanic in the current simulation.
Each stage has a target:
| Stage | Healthy weight |
|---|---|
| Egg | 5 lb |
| Baby | 10 lb |
| Child | 20 lb |
| Teen | 30 lb |
| Adult | 40 lb |
| Elder | 35 lb |
There is no passive weight loss. A meal adds one pound, candy adds two, and every completed game removes one—even if the player loses.
Being under the target is harmless. Being over it is not:
hourly weight damage = excess pounds × 0.9 health
Weight damage continues at full strength while the pet sleeps and while the application is closed.
| Example baby | Excess weight | Health lost per hour | Health lost over 8h 40m |
|---|---|---|---|
| 10 lb | 0 lb | 0 | 0 |
| 14 lb | 4 lb | 3.6 | 31.2 |
| 20 lb | 10 lb | 9 | 78 |
| 23 lb | 13 lb | 11.7 | 101.4 |
A baby can go to bed with full hearts and still be dead by morning because it was overfed. That sounds harsh in isolation, but it gives food meaning. Feeding is not universally loving; appropriate care is.
Candy can become abuse
Candy is safe when happiness is below four hearts. Giving it to a pet that is already fully happy immediately removes eight health and creates at least level-one sickness.
Repeated candy does not automatically raise sickness severity by itself, but it continues adding two pounds per use. In practice, the combined health hit and weight gain make compulsive candy one of the fastest ways to destabilize a healthy pet.
The rule is deliberately contextual:
| State before candy | Result |
|---|---|
| Happiness below 4/4 | Normal happiness, weight and cleanliness effects |
| Happiness already 4/4 | Normal weight gain plus −8 health and sickness |
The safer response to missing happiness is usually a game. It requires more interaction and energy, but it improves happiness without turning affection into overfeeding.
Sickness does not disappear by waiting
Sickness severity uses the highest applicable trigger:
| Trigger | Severity |
|---|---|
| Health at or below 52 | 1 |
| Health at or below 38 | 2 |
| Health at or below 20 | 3 |
| 3 poop piles | At least 1 |
| 4 or more poop piles | At least 2 |
| Candy at full happiness | At least 1 |
Once active, sickness does not resolve automatically. Each medicine dose reduces severity by one level, so the player must continue until the indicator disappears.
| Severity | Awake health loss | Protected-sleep loss | Doses from full severity |
|---|---|---|---|
| 1 | 2.7/hour | 1.35/hour | 1 |
| 2 | 5.4/hour | 2.7/hour | 2 |
| 3 | 8.1/hour | 4.05/hour | 3 |
Meanwhile, good care restores only 1.5 health per hour, and only when hunger and cleanliness are at least 62, energy is at least 42 and happiness is at least 42. Recovery is possible, but prevention is much more efficient.
Poop is a timer, not decoration
A new pile appears every 180 effective awake minutes, up to a maximum of nine. Each new pile removes one happiness heart.
The effects compound:
- Every pile drains 2.1 health per hour.
- Cleanliness already loses 6% per awake hour, plus another 2.7% per hour for each pile.
- Three piles cause level-one sickness; four cause level two.
- Accumulated neglect time lowers both care quality and expected lifespan.
The toilet removes every pile at once. The real challenge is not the button press; it is noticing and responding within the attention window.
Sleep is protection, but not invulnerability
Sleep follows the pet’s internal clock and changes by life stage:
| Stage | Sleep | Wake | Duration |
|---|---|---|---|
| Baby | 20:00 | 08:00 | 12 h |
| Child | 20:00 | 09:00 | 13 h |
| Teen | 21:00 | 09:00 | 12 h |
| Adult | 22:00 | 08:00 | 10 h |
| Elder | 20:00 | 10:00 | 14 h |
Scheduled sleep only becomes protected sleep when the lights are off.
| During scheduled sleep with lights off | Effect |
|---|---|
| Hunger and happiness hearts | Paused |
| Poop generation | Paused |
| Ordinary care and cleanliness pressure | Reduced to 12% |
| Poop health damage | Reduced to 12% |
| Sickness damage | Reduced to 50% |
| Weight damage | Not reduced |
| Energy | Recovers at 13.2/hour |
| Age | Continues normally |
This creates an overnight ritual: clean the pet, check its weight, cure sickness and turn the lights off at the correct time. Simply toggling the lights during the day does not manufacture sleep protection.
Leaving the lights on for at least 30 minutes while the pet is scheduled to sleep begins damaging health and happiness. Longer neglect also records lights mistakes, which later affect evolution and lifespan.
Attention calls teach the player’s rhythm
The pet calls for six kinds of attention:
| Call | Correct response |
|---|---|
| Hunger | Give one meal |
| Happiness | Play a game or give candy |
| Poop | Use the toilet |
| Sickness | Give medicine until cured |
| Lights | Turn the lights off |
| Discipline | Scold only when no real need exists |
The response window is approximately 15 minutes. Missing a genuine need adds a care mistake. Missing a discipline call adds a discipline miss.
Discipline calls are the interesting exception: the pet asks for attention even though nothing is wrong. Scolding is correct only then. Scolding a pet with a real unmet need removes a happiness heart.
As discipline rises, these calls become less frequent:
| Discipline | Approximate interval |
|---|---|
| 0–24% | 3 hours |
| 25–49% | 4 hours |
| 50–74% | 5 hours |
| 75–99% | 6 hours |
| 100% | No further discipline calls |
The system rewards learning the pet’s state rather than reacting blindly to a notification.
Mood compresses the simulation into one expression
Mood is derived in priority order. That priority prevents a pet from looking cheerful merely because one stat is high while a more serious condition is active.
| Priority | Mood | Condition |
|---|---|---|
| 1 | Dead | Health is zero |
| 2 | Sick | Sickness is active, health is below 35, or cleanliness and energy are critically low |
| 3 | Sleeping | It is bedtime or energy is below 18 |
| 4 | Angry | Hunger or cleanliness is below 22 |
| 5 | Sad | Happiness is below 35 |
| 6 | Happy | Happiness, hunger, cleanliness and health are all comfortably high |
| 7 | Neutral | No higher-priority state applies |
PixiJS turns that derived state into animation: bouncing when happy, slowing during sleep, reacting to meals and medicine, displaying attention, evolving and finally becoming still at death.
That separation is useful. The renderer does not decide whether the pet is sick. It receives a deterministic state and decides how sickness should look.
Care quality is a history, not a snapshot
The evolution score begins at 100. It subtracts recorded failures, adds a discipline bonus and clamps the result between zero and 100.
100
− 8 × care mistakes
− 6 × discipline misses
− 10 × sickness events
− 5 × lights mistakes
− 4 × each 90 minutes of poop neglect
− 2 × each tracked maximum pound above 30 lb
− 3 × each candy beyond the number of games played
+ 1 × each 5 discipline points
= care-quality score, clamped to 0–100
| Score | Rating |
|---|---|
| 85–100 | Excellent |
| 65–84 | Good |
| 40–64 | Rough |
| 0–39 | Poor |
This is one of my favorite parts of the model. A full hunger meter right now does not erase a week of neglect. Equally, one imperfect afternoon does not destroy a well-cared-for life. The form reflects a pattern.
Lifespan is the final accumulated outcome
The base lifespan is 18 days, with a hard minimum of eight and maximum of 28.
Care quality can move the expectation by roughly five days in either direction. Every 25 discipline points adds another day. Care mistakes, sickness events, lights mistakes and long poop neglect remove days.
A perfectly maintained pet with a 100 care score and 100 discipline currently reaches about 27 days.
Death is terminal. The save records whether it happened through old age, sickness, overweight or neglect. The UI becomes a small memorial with a care report rather than instantly wiping the evidence away. Starting again is a deliberate action.
That consequence is essential. A virtual pet without loss becomes a maintenance widget. A virtual pet that remembers how it lived can become a story.
The architecture behind the toy
The implementation keeps the desktop shell, interface, rendering and simulation separate:
| Layer | Responsibility |
|---|---|
game/** | Pure lifecycle rules, stat decay, actions, attention, evolution, care quality and mortality |
game/save/** | Save validation, migrations, corruption recovery and persistence contracts |
renderer/** | PixiJS canvas, pixel sprites, reactions and animation timing |
ui/** | React shell, LCD menus, status pages, setup, guessing game and A/B/C controls |
state/** | Loading, ticking, actions and persistence coordination |
desktop/** | Dragging, wake/resume behavior, sounds and notifications |
src-tauri/** | Native app-data saves, atomic writes, tray behavior and Windows packaging |
The domain layer does not import React, PixiJS or Tauri. An action is fundamentally a pure transition:
type PetTransition = (
pet: PetState,
timestamp: number,
) => PetState;
Before applying an action, the simulation advances the pet to the supplied timestamp. The action then produces a new state. This prevents the UI from becoming the authority on game time.
It also makes testing practical. A test can create a pet at a known timestamp, move time forward by eight hours and inspect the exact result without waiting eight hours or mounting a component.
Persistence has to assume failure
The native build stores a versioned JSON save in the Tauri application-data directory. Browser development uses localStorage behind the same repository boundary.
Loading is not a blind JSON.parse. The save layer validates schema version, timestamps, ranges, lifecycle stage and mood. Older saves can be migrated. Corrupt native saves are backed up before the application creates a fresh pet. Writes are atomic so an interrupted write is less likely to destroy the only life on disk.
This may look like a lot of ceremony for a tiny toy, but persistence is part of the emotional contract. If the app claims that care matters, it cannot casually lose or scramble that history.
The safest care card
For anyone trying to keep a pet alive, the rulebook condenses to this:
| Situation | Best response |
|---|---|
| Hunger loses one heart | Give one meal; do not keep feeding at full |
| Happiness loses one heart | Prefer a game |
| Weight exceeds the stage target | Play one game per excess pound |
| Energy is low | Rest |
| Cleanliness drops | Use the toilet/bath |
| Any poop appears | Clean it within 15 minutes when possible |
| Sickness appears | Repeat medicine until the indicator is gone |
| Attention sounds with no real need | Scold once |
| Scheduled bedtime arrives | Verify that the lights are off |
| Leaving overnight | Check weight, health, sickness, poop and internal time |
| Leaving for a long absence | Pause explicitly |
The hardest lesson is simple: full hearts are not the same as good care. Weight, cleanliness, sleep, sickness, discipline and history all matter.
What building it taught me
The technical challenge was not drawing a pixel creature. It was deciding what the creature means over time.
Several design lessons emerged:
- Time is domain data. “Now” should enter the simulation explicitly, not leak in through components and scattered calls to the system clock.
- Consequences need memory. Evolution becomes meaningful when it responds to a care history rather than the current screen.
- Tradeoffs create play. Meals, candy, games, rest and medicine are more interesting when none is a universally correct button.
- Offline behavior must be designed, not improvised. Sleep protection, catch-up, pausing and wake events need one coherent model.
- The interface should serve the fantasy. A narrow, tactile desktop object creates more attachment than a more “efficient” dashboard would.
- Small software still deserves serious boundaries. Pure rules, validated persistence and deterministic tests made the simulation easier to extend without turning it into hidden UI state.
The final effect comes from many ordinary rules interacting: a meal changes hunger, weight and cleanliness; weight changes health; health changes sickness and mood; illness changes attention; missed attention changes care quality; care quality changes evolution and lifespan.
No individual rule creates a living creature. The continuity between them does.
And that is the part I wanted to preserve from the original toy: when you close the window, you do not stop thinking about what might be happening inside it.
Try it on Windows or Linux
You can raise your own pet with the current preview builds for Windows and Linux.
Windows
Download Tamagotchi 0.1.0 for Windows x64 (2.09 MiB)
| Build detail | Value |
|---|---|
| Version | 0.1.0 |
| Platform | Windows 10/11 x64 |
| Installer | NSIS .exe |
| Size | 2,193,167 bytes |
Preview-build note: This installer is not code-signed yet, so Windows SmartScreen may display an “unrecognized app” warning. Download it only from this page and verify the checksum below if you want to confirm the file is unchanged.
SHA-256:
68EA625C860DBE94B1A551521C09D9D0C3EAC9A6FC4AC2561DDD234B11AE962D
Linux
Download Tamagotchi 0.1.1 for Linux x64 (4.06 MiB)
| Build detail | Value |
|---|---|
| Version | 0.1.1 |
| Platform | Debian-based Linux x64 |
| Installer | Debian .deb package |
| Size | 4,253,984 bytes |
SHA-256:
ACF3FCEAEA4E4EE8C156C17B8027082952BD62AD693922C6E21F3DA7EFEC3AFA 