A transparent Live2D desktop pet (Tauri 2 + pixi-live2d-display) with voice lip sync, expressions, mouse tracking, and a local HTTP API. Currently ships the Sparkle (η«θ±) model. Point your hermes agent at it and every voice recording it generates is played on the pet β lip-synced, with a speech bubble.
Releases Β· API reference Β· Hermes skill Β· Claude Code skill
Useful for:
- Health checks
- Cron reminders
- Looking cute
This is the main use case. Your hermes agent runs somewhere (e.g. an EC2 box, chatting over
the Discord gateway); the pet runs on your Mac. Whenever the agent generates a TTS recording
with its text_to_speech
tool, a small skill uploads that audio to the pet, which plays it with lip sync + expression + a speech bubble. Normal chat replies are not spoken β only recordings the agent was already producing get routed.
hermes agent (remote) your Mac
ββββββββββββββββββββ ββββββββ
text_to_speech βββΊ hermes-live2d-voice skill βββΊ POST /speak βββΊ Sparkle pet
(speak.sh, base64) (over Tailscale) lip sync + bubble
on both the Mac and the agent host (same tailnet) β this is how the remote agent reaches the pet without exposing it publicly.TailscaleA hermes agent wired to a TTS engineβ any engine works (ElevenLabs, etc.), as long as the agent produces audio files via itstext_to_speech
tool. That's the audio the pet plays.Theβ bundled in this repo athermes-live2d-voice
skill(skills/hermes-live2d-voice/
SKILL.md
+speak.sh
); you install it on the agent host in step 3.
Requirements: macOS on Apple Silicon (the app uses macOS-private window APIs and builds only
for aarch64-apple-darwin
).
Grab a build from Releases, or build from source:
pnpm install
pnpm tauri build # release .app / .dmg under src-tauri/target/release/bundle/
pnpm tauri dev
Install the Live2D model. The model assets are not included in this repo (they're gitignored) and are not redistributed here β download them and drop them in yourself:
- Download the free
Spark (η«θ±) model frombooth.pm/en/items/8265367and unzip it. - Place the unzipped model folder so its entry file is named exactly
(rename the
Sparkle.model3.json
.model3.json
if the download uses a different name), in one of:β runtime override, no rebuild needed. Loaded on the next launch and takes priority. Result:~/.atri/model/
~/.atri/model/Sparkle.model3.json
(plus its.moc3
, textures, physics, expressions, etc. alongside).β bundled into the app at build time (use this when building from source; required, since the repo ships no model). Result:public/model/
public/model/Sparkle.model3.json
.
To use a different Live2D character entirely, put its files in ~/.atri/model/
with the entry
file renamed to Sparkle.model3.json
β the filename is what the app looks for. Note its expressions/keyforms will differ (see the skill's model-swap note).
Launch the app. On first run it creates ~/.atri/config.json
and starts a local HTTP API on
http://127.0.0.1:3210
. Confirm it's up:
curl http://127.0.0.1:3210/status
The API binds to loopback only (127.0.0.1
) and has no auth, so it must not be exposed directly. Put it on your tailnet instead. On the Mac:
tailscale serve --bg 3210
This publishes the pet at https://<mac-name>.<tailnet>.ts.net
, reachable only by your own devices.
Copy the bundled skill into your hermes agent's skills directory:
cp -r skills/hermes-live2d-voice /path/to/hermes/skills/
It's a standard SKILL.md
speak.sh
bundle. Tell the agent's environment where the pet lives:
export ATRI_SPEAK_URL="https://<mac-name>.<tailnet>.ts.net"
Verify from the agent host:
curl -s "$ATRI_SPEAK_URL/status"
That's it. See skills/hermes-live2d-voice/SKILL.md for the exact contract, the expression list, and the graceful-degrade behavior when the Mac is asleep or offline.
Transparent desktop petβ frameless, transparent, always-on-top; draggable and resizable** Mouse tracking**β eyes and head follow the cursor in real time, across multiple monitors** Voice lip sync**β mouth animation is driven by audio volume during playback** Expressions & motions**β 12 expressions (exp1
βexp12
) plus custom motion playbackSpeech bubblesβ typewriter-style bubbles with configurable duration, anchored to the head** HTTP API**β local REST API (default port 3210) for external programs to drive the pet** Window memory**β remembers window position and size across launches** System tray**β tray icon to lock/unlock and quit
Once the app is running, the API listens on http://127.0.0.1:3210
.
curl http://127.0.0.1:3210/status
curl -X POST http://127.0.0.1:3210/speak \
-H 'Content-Type: application/json' \
-d '{"text": "hello!", "audio_url": "file:///path/to/audio.wav", "expression": 1}'
curl -X POST http://127.0.0.1:3210/expression \
-H 'Content-Type: application/json' \
-d '{"name": "exp1"}'
curl -X POST http://127.0.0.1:3210/bubble \
-H 'Content-Type: application/json' \
-d '{"text": "thinking...", "duration": 3000}'
/speak
also accepts inline audio as base64 (audio_data
audio_format
) instead of an
audio_url
β that's how the remote hermes skill sends recordings across the network. Full endpoint reference: docs/API.md.
The model exposes 12 expressions, addressed as expression: 1
β12
(names exp1
βexp12
, ids
are 1-based); omit expression
for the neutral/default face. Each id maps to a specific mood on the loaded model β see the mood table in skills/hermes-live2d-voice/SKILL.md for the current mapping. Query the live id/name list any time with
curl http://127.0.0.1:3210/expressions
.Config lives at ~/.atri/config.json
, created on first launch:
{
"api_port": 3210
}
- Window position/size are saved to
~/.atri/window_state.json
. - Uploaded
/speak
audio is written to~/.atri/tmp/
and overwritten on each call. - Drop a custom Live2D model under
~/.atri/model/
to load it instead of the bundled Sparkle model on startup.
The bundled character is the free Spark (η«θ±) Live2D model by ε€ε’¨ww (booth.pm/en/items/8265367) β Sparkle from Honkai: Star Rail.
Claude Codeβis a TTS-agnostic example skill: generate speech with your own TTS (GPT-SoVITS, Edge-TTS, β¦) and drive the pet after each reply.examples/claude-code-skill.md
GPT-SoVITS voice (optional)β for a Japanese ATRI voice, a GPT-SoVITS model is hosted atVoidShine/atri-sovits. Not needed for the hermes path, where the agent supplies its own TTS audio.
| Layer | Technology |
|---|---|
| Desktop framework | |
PixiJS 6+pixi-live2d-displayAxumGPT-SoVITSmacOS onlyβ relies on macOS-private APIs (transparent window, click-through, etc.); no Windows or Linux support.** Apple Silicon only**β release target isaarch64-apple-darwin
.Model-bound parametersβ expression/keyform mappings are hardcoded for the bundled model; swapping in a different Live2D model requires code changes.Volume-driven lip syncβ mouth movement maps from audio volume, not viseme-accurate phonemes.** Local, unauthenticated API**β the HTTP API listens on127.0.0.1
with no auth; only expose it to trusted devices (e.g. a private tailnet), never the public internet.Mouse tracking via Tauriβcursor_position()
behavior can vary across macOS versions.
Project code is released under the MIT license.
Use of the Live2D Cubism SDK and the character model assets is subject to their respective licenses. The bundled Spark (η«θ±) model is by ε€ε’¨ww (booth.pm/en/items/8265367); follow its terms.