Currently looking to expand the emotes gallery! If you have an emote set you'd like to submit, please make a PR!
Animated pixel-art emote that lives in the top-right corner of your pi TUI session. Reacts to what the agent is doing — thinking, talking, reading, writing, using tools, etc.
Supports Kitty, iTerm2, and ASCII rendering.
Community-contributed emote sets. Submit yours via PR!
| Avatar | Name | Contributor |
|---|---|---|
![]() |
default |
@cgxeiji |
![]() |
aza_choi |
@shennguyenrs |
![]() |
aza_choi_nobg |
@shennguyenrs |
![]() |
red |
@cgxeiji |
| Avatar | Name | Contributor |
|---|---|---|
(^ ◡ ^)/ |
ascii |
@cgxeiji |
ʕ•̫͡•ʔ |
ascii-bear |
@LCorleone |
.------. |
ascii-bot |
@cgxeiji |
pi install git:github.com/cgxeiji/pi-emote| State | Trigger |
|---|---|
| hi | Session start |
| idle | Nothing happening (blinks occasionally) |
| think | Reasoning tokens streaming |
| talk | Text response streaming |
| read | read tool / reading tool output |
| write | write or edit tool |
| tool | Any other tool |
| success | Successful tool execution |
| failure | Failed tool execution |
| compact | Context compaction |
Drop a config.json in one of these paths (highest priority wins):
~/.pi/agent/extensions/pi-emote/config.json— your global prefs.pi/extensions/pi-emote/config.json— project override
Only include what you want to change:
{
"size": 12,
"emotes": [
{ "model": "*claude*", "emote-set": "my-avatar" }
]
}See config.json in the extension root for all defaults.
pi-emote can render image avatars through tmux using DCS passthrough. When tmux is detected, pi-emote auto-detects the outer terminal and picks the right image protocol.
Add these to your tmux.conf:
# Required — allow image sequences to pass through to the outer terminal
set -g allow-passthrough on
# Required — detect outer terminal when attaching from a different terminal
set -ga update-environment TERM
set -ga update-environment TERM_PROGRAM
# Recommended — reduces flicker during animation
set -sg escape-time 0Then restart tmux completely:
tmux kill-server && tmuxWithout allow-passthrough, pi-emote defaults to ASCII and shows a one-time warning with setup instructions.
| Outer Terminal | Protocol | Status |
|---|---|---|
| Ghostty | kitty-unicode | ✅ Stable, pane-safe, auto-detected |
| kitty | kitty-unicode | |
| iTerm2 | iterm2 | |
| WezTerm | iterm2 |
The outer terminal is detected via tmux show-environment TERM_PROGRAM, which reflects the currently attached terminal.
Ghostty and kitty use the kitty-unicode renderer (Unicode placeholders) which is pane-safe — images stay within their pane and clean up on session switch. This is the default when auto-detected.
iTerm2 and WezTerm use DCS passthrough for the iTerm2 image protocol. This works but has known limitations: images can bleed into adjacent panes and persist when switching sessions. Not enabled by default — opt in explicitly:
{
"terminals": [
{ "match": "tmux", "render": "iterm2" }
]
}zellij and screen are not yet supported and default to ASCII.
Force a specific renderer:
{
"terminals": [
{ "match": "tmux", "render": "kitty-unicode" }
]
}Available render values for tmux: "auto", "kitty-unicode", "kitty", "iterm2", "ascii".
"auto"— detect outer terminal; uses kitty-unicode for Ghostty/kitty, ASCII for others"kitty-unicode"— pane-safe Unicode placeholders (Ghostty, kitty)"kitty"— classic DCS passthrough (single-pane only, experimental)"iterm2"— iTerm2 DCS passthrough (single-pane only, experimental)"ascii"— text fallback
Emote sets live in emotes/<set-name>/ with PNG frames per state:
emotes/my-avatar/
├── idle/*.png
├── think/*.png
├── talk/*.png
├── read/*.png
├── write/*.png
├── tool/*.png
└── ... # hi, success, failure, compact
Not all states are required. Missing ones just won't animate.
pi-emote searches in order:
.pi/extensions/pi-emote/emotes/<name>/(project)~/.pi/agent/extensions/pi-emote/emotes/<name>/(user)- Extension built-in → falls back to
default
Glob patterns against model ID, last match wins:
{
"emotes": [
{ "model": "*", "emote-set": "default" },
{ "model": "*claude*", "emote-set": "my-avatar" },
{ "model": "*haiku*", "emote-set": "haiku-avatar" }
]
}In this example, claude models use my-avatar, but haiku ones use haiku-avatar.
See emotes/default/emotes.json for per-set frame config (blink frames, talk weights).
MIT




