A local-first AI digital pet that lives on your desktop.
Mochi moves on her own, remembers what matters, and chats briefly through an optional local LLM. Works fully offline. No accounts. No cloud. No telemetry.
idle |
walk |
jump |
sit |
look |
sleep |
eat |
yawn |
roll |
blush |
hide |
celebrate |
14 hand-drawn poses + per-mood color tint + 4 distinguishing glyphs
(๐ก hungry ยท โฆ bored ยท โก lonely ยท ? curious)
Most AI companions are chatbots wearing a mascot. Most virtual pets are cute shells with no memory. Mochi is a deliberate middle path:
"The pet must feel alive even when the LLM is off." โ Project North Star
git clone https://github.com/cskwork/pet-mochi.git
cd pet-mochi
npm install
npm run tauri:dev # launches the desktop pet
Requirements
That's it. Mochi happily runs in silent mode โ no setup needed for the core experience.
.txt / .md / .json into the
inbox; Mochi asks before reading it.stretch ยท peek ยท tilt_head ยท
shake ยท nuzzle ยท wiggle ยท dizzy ยท surprise.Further out: Voice ยท Custom skins ยท Live2D / VRM ยท Git/test-runner watcher ยท
Local embedding memory search ยท Multiple pets. See PRD.md ยง24.
Pet Mochi is happy in silent mode. To enable chat, install Ollama and pull a small lightweight model:
ollama pull gemma4:e2b
Open Settings โ set provider to ollama, point endpoint at
http://localhost:11434 and choose a model (defaults to gemma4:e2b).
Mochi retrieves relevant memories, sends a compressed prompt, and stores
the interaction. A best-effort memory extraction job runs in the
background after each LLM-backed reply.
| Layer | Tech | Notes |
|---|---|---|
| Desktop shell | Tauri 2 | Transparent, always-on-top, draggable overlay. |
| UI | Svelte 5 + TypeScript | PNG sprite + CSS animation, no extra renderer. |
| Simulation engine | TypeScript pure functions | Mood/decay/movement; testable, no LLM required. |
| Memory engine | Rust + rusqlite + FTS5 | Durable memories, prefix search, weighted ranking. |
| LLM adapter | Rust + reqwest | Pluggable LlmProvider trait. Ollama is the first impl. |
| Sandbox | Rust | Inbox watcher (notify), path-jail safe IO, declarative skills. |
src/ Svelte + TypeScript frontend
โโ App.svelte Routes pet (default) and settings (#/settings)
โโ lib/
โ โโ sim/ Pure simulation engine (testable)
โ โโ events/bus.ts In-process event bus + salience-driven LLM gating
โ โโ bridge/ Tauri invoke wrapper + typed API
โ โโ components/ Pet, MochiSprite, ChatBubble, PetActions, Settings
src-tauri/ Rust backend
โโ src/
โ โโ db.rs SQLite schema, FTS5 search, CRUD
โ โโ llm/ Provider trait, Ollama, prompts, cooldowns
โ โโ sandbox.rs Pet home, safe file IO, skill manifests
โ โโ watcher.rs notify-based inbox watcher
โ โโ commands.rs Every #[tauri::command]
โ โโ state.rs Shared AppState (Arc<Db>, LLM, cooldowns)
โ โโ lib.rs Tauri builder, plugin wiring, setup
runTick(state, ctx, elapsed) pure function decides
the pet's behavior every 3s. The LLM is consulted only when an event's
salience clears 70 and a 90-second autonomous cooldown has passed.0.4 ร importance + 0.3 ร recency + 0.3 ร confidence so old-but-important
memories beat fresh trivia.eval, no shell, no remote skill loading. File paths are
canonicalized and verified to live under the sandbox before any read/write.Mochi's home folder lives at:
| OS | Path |
|---|---|
| Windows | %LOCALAPPDATA%\pet-mochi\ |
| macOS | ~/Library/Application Support/pet-mochi/ |
| Linux | ~/.local/share/pet-mochi/ |
Override with the MOCHI_HOME env var.
pet-mochi/
โโ inbox/ โ drop .txt / .md / .json files here
โโ notes/ โ Mochi writes summaries
โโ dreams/ โ daily reflections
โโ exports/ โ memory exports (Markdown / JSON)
โโ mochi.db โ SQLite memory + state
Mochi never reads files outside inbox/, never writes outside notes/,
dreams/, or exports/, never executes shell commands, and refuses paths
containing .., absolute escapes, or symlinks that resolve outside the
sandbox.
npm test # 99 frontend simulation tests (vitest)
cd src-tauri && cargo test --lib # 41 backend tests (db, sandbox, llm, prompts)
npm run check # svelte-check
npm run build # vite frontend bundle
npm run tauri:build # full desktop installer (icons already generated)
See DECISIONS.md for the rationale behind recent UX/a11y
improvements, and BACKLOG.md for deferred items.
Every numbered requirement (REQ-001 โฆ REQ-099) from PRD.md is
either implemented, in active development (REQ-015, REQ-070โฆ076, REQ-094โฆ099),
or explicitly out-of-scope for the MVP. Highlights:
LlmProvider) โ Ollama default, swappable (REQ-040โฆ044)dreams/ (REQ-070โฆ073)inbox/, notes/, dreams/, exports/ (REQ-080โฆ084)Out of scope for MVP (per PRD ยง4.2): voice, 3D/Live2D, browser automation, shell execution, cloud sync, marketplace plugins.
Issues and PRs welcome. Before opening a PR:
npm test and cd src-tauri && cargo test --lib โ both must pass.npm run check โ must be 0 errors / 0 warnings.npm run tauri:dev and describe what you
saw in the PR description.MIT โ do whatever you want, no warranty.
If Pet Mochi made your day a little nicer, โญ the repo. That's all.