A local-first AI pet that lives on your desktop.

Mochi moves on her own, remembers what matters, and chats briefly through an optional local LLM. Fully offline. No accounts. No cloud. No telemetry.

MIT Tauri 2 Svelte 5 Rust 292 tests
happy

This is the real simulation — every 3 s tick runs the same pure decay, mood and movement rules the desktop app runs. How it works ↓

  • Alive without the LLM.A deterministic local simulation drives mood, needs, movement and sleep — every 3 seconds, all day, no network calls.
  • The LLM only speaks at salience moments.Short greetings and reflections, behind a salience gate and hard cooldowns. No spam, no surprise bills.
  • Memory you can read and delete.SQLite + FTS5, ranked by importance × recency × confidence. Export to Markdown anytime.
  • Sandbox by default.She reads only what you drop into her inbox, only after explicit per-file consent — never your filesystem at large.

Clone it and meet her

No installers yet — the honest path is clone-and-run. She’s happy in silent mode; the chat is optional.

quickstart
$ git clone https://github.com/cskwork/pet-mochi.git
$ cd pet-mochi
$ npm install
$ npm run tauri:dev # launches the desktop pet

Requirements: Node ≥ 20, Rust ≥ 1.77, Windows / macOS / Linux. Optional: Ollama with a small model for chat replies — ollama pull gemma4:e2b, then Settings → provider ollama.

Under the hood

Cute surface, rigorous underneath. The pet is a simulation first — the LLM is seasoning, not the meal.

292
tests passing
113
PRD requirements tracked
3 s
simulation tick, fully offline
0
network calls by default
Every layer, and what it actually does
LayerTechRole
Desktop shellTauri 2Transparent, always-on-top, draggable overlay.
UISvelte 5 + TypeScriptPNG sprite + CSS animation, no extra renderer.
Simulation engineTypeScript pure functionsMood, decay, movement — testable, no LLM required.
Memory engineRust + rusqlite + FTS5Durable memories, prefix search, weighted ranking.
LLM adapterRust + reqwestPluggable LlmProvider trait; Ollama is the first implementation.
SandboxRustInbox watcher, path-jail safe IO, declarative skills.

Six moods you can read at a glance

Hidden stats decay every tick; the first matching rule wins. A color tint plus a distinguishing glyph keeps similar tones apart — watch them drift live in the device above.

Tired
energy < 20

She finds a quiet corner and sleeps it off.

Hungry
hunger > 75

The 🍡 glyph appears; the snack tray starts looking interesting.

Bored
boredom > 70

Wanders off to explore — the “…” glyph gives it away.

Lonely
affection < 20

A ♡ asks for a pat. She never guilt-trips, only hopes.

Curious
curiosity > 70

A “?” floats up when something novel is nearby.

Happy
otherwise

The default. Idle quirks, wiggles, and the occasional celebrate.

Every pose is hand-drawn

28 sprites, one character, each with its own CSS motion in the app — plus per-mood color tints layered on top. These are the real files, straight from public/sprites/.

Optional: give her a voice

Mochi is silent by default and fine with it. With a local Ollama model she adds short, mood-aware, in-character replies — and the leash stays short.

talking to mochi
$ ollama pull gemma4:e2b
# then: Settings → provider: ollama
# endpoint: http://localhost:11434
  • Salience-gated, cooldown-capped.An event must clear salience ≥ 70 and wait out a 90 s autonomous cooldown. No care action alone reaches the gate — Play is 40, Feed 35, Rest 25, Pat 20.
  • Memory before words.Relevant memories are retrieved and re-ranked by 0.4·importance + 0.3·recency + 0.3·confidence, so old-but-important beats fresh trivia.
  • Failure is graceful.Ollama offline, DB unavailable, garbled extraction — the pet keeps animating. Failures log warnings, never bubble up.

Her whole world is one folder

A path-jailed sandbox (canonicalized, verified, no .., no absolute escapes, no symlinks that resolve outside). Want her somewhere else? Point MOCHI_HOME at it.

pet-mochi/
├─ inbox/    drop .txt / .md / .json here
├─ notes/    she writes summaries
├─ dreams/   daily reflections
├─ exports/  memory exports
└─ mochi.db   # SQLite memory + state
  • Reads only from inbox/, and only after explicit per-file consent.
  • Writes only to notes/, dreams/, exports/.
  • No shell, no eval, no remote skill loading — ever.
  • Every path is canonicalized and jailed before any read or write.

Coming next — clearly not shipped yet

“The pet must feel alive even when the LLM is off.”
— Project North Star, PRD §1