This guide covers creating avatars that render directly in your terminal using ASCII art or images, as an alternative to the browser-based viewer.
Terminal avatars display animated characters using:
- ASCII art frames - Text-based art that animates by cycling through frames
- Image mode - Renders actual images in supported terminals (iTerm2, Kitty, WezTerm)
┌─────────────────────────────────────────────────────────────────────┐
│ CLI Process │
│ ┌─────────────────┐ ┌────────────────────────────────────────┐ │
│ │ byteside │───>│ Terminal Renderer │ │
│ │ (server) │ │ - Renders avatar in terminal region │ │
│ └─────────────────┘ │ - Updates on state changes │ │
│ └────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
Why terminal mode?
- Lightweight - No browser window needed
- SSH-friendly - Works over remote connections
- Integrated - Avatar appears alongside server output
- Retro aesthetic - ASCII art has a unique charm
- Add terminal configuration to your avatar's
manifest.json - Create ASCII frame files in a
terminal/subdirectory - Run
byteside- terminal mode activates automatically when enabled
Add a terminal block to your manifest.json:
{
"name": "my-avatar",
"author": "Your Name",
"version": "1.0.0",
"format": "webm",
"states": {
"idle": { "file": "idle.webm" },
"thinking": { "file": "thinking.webm" }
},
"terminal": {
"enabled": true,
"mode": "ascii",
"states": {
"idle": { "frames": ["terminal/idle/01.txt", "terminal/idle/02.txt"] },
"thinking": { "frames": ["terminal/thinking/01.txt"] }
}
}
}{
"name": "my-avatar",
"author": "Your Name",
"version": "1.0.0",
"format": "webm",
"states": {
"idle": { "file": "idle.webm" },
"thinking": { "file": "thinking.webm" },
"writing": { "file": "writing.webm" },
"bash": { "file": "bash.webm" },
"error": { "file": "error.webm" },
"success": { "file": "success.webm" },
"waiting": { "file": "waiting.webm" }
},
"terminal": {
"enabled": true,
"mode": "ascii",
"framerate": 8,
"size": { "width": 40, "height": 20 },
"states": {
"idle": {
"frames": [
"terminal/idle/01.txt",
"terminal/idle/02.txt",
"terminal/idle/03.txt",
"terminal/idle/04.txt"
]
},
"thinking": { "frames": ["terminal/thinking/01.txt", "terminal/thinking/02.txt"] },
"writing": { "frames": ["terminal/writing/01.txt", "terminal/writing/02.txt"] },
"bash": { "frames": ["terminal/bash/01.txt"] },
"error": { "frames": ["terminal/error/01.txt"] },
"success": { "frames": ["terminal/success/01.txt"] },
"waiting": { "frames": ["terminal/waiting/01.txt"] }
}
}
}For terminals that support inline images (iTerm2, Kitty, WezTerm):
{
"terminal": {
"enabled": true,
"mode": "image",
"framerate": 4,
"size": { "width": 40, "height": 20 },
"states": {
"idle": { "image": "terminal/idle.png" },
"thinking": { "image": "terminal/thinking.png" },
"writing": { "image": "terminal/writing.png" }
}
}
}| Field | Required | Type | Default | Description |
|---|---|---|---|---|
enabled |
Yes | boolean | - | Enable terminal mode |
mode |
Yes | string | - | "ascii" or "image" |
framerate |
No | number | 8 |
Frames per second for animation |
size.width |
No | number | 40 |
Width in characters |
size.height |
No | number | 20 |
Height in lines |
states |
Yes | object | - | Map of state names to frame configs |
For ASCII mode (mode: "ascii"):
| Field | Required | Type | Description |
|---|---|---|---|
frames |
Yes | string[] | Array of paths to ASCII frame files |
For Image mode (mode: "image"):
| Field | Required | Type | Description |
|---|---|---|---|
image |
Yes | string | Path to image file (PNG, JPG, GIF) |
my-avatar/
├── manifest.json
├── idle.webm # Browser video files
├── thinking.webm
├── ...
└── terminal/ # Terminal assets
├── idle/
│ ├── 01.txt # ASCII frame 1
│ ├── 02.txt # ASCII frame 2
│ ├── 03.txt # ASCII frame 3
│ └── 04.txt # ASCII frame 4
├── thinking/
│ ├── 01.txt
│ └── 02.txt
├── writing/
│ └── 01.txt
├── bash/
│ └── 01.txt
├── error/
│ └── 01.txt
├── success/
│ └── 01.txt
└── waiting/
└── 01.txt
Each frame is a plain text file. The content is rendered exactly as-is:
terminal/idle/01.txt:
.-"""-.
/ \
| O O |
| __ |
| '--' |
\ /
'------'
| |
/| |\
terminal/idle/02.txt:
.-"""-.
/ \
| O O |
| __ |
| '--' |
\ /
'------'
| |
/ | | \
- Keep frames the same size - All frames in a state should have identical dimensions
- Subtle changes - Small differences between frames create smooth animation
- Loop-friendly - Design so the last frame transitions smoothly to the first
You can include ANSI escape codes for colored ASCII art:
.-"""-.
/ \
| [34m O O [0m |
| [33m__[0m |
| [32m'--'[0m |
\ /
'------'
Common ANSI codes:
[30m-[37m: Standard colors (black, red, green, yellow, blue, magenta, cyan, white)[90m-[97m: Bright colors[1m: Bold[2m: Dim[0m: Reset
Generators:
- ASCII Art Generator - Convert images to ASCII
- Text to ASCII - Text banners
- Figlet - Command-line ASCII text
Collections:
- ASCII Art Archive - Curated ASCII art
- Christopher Johnson's ASCII Art - Classic collection
Tools:
jp2a- Convert JPEG to ASCII:jp2a --width=40 image.jpgimg2txt(libcaca) - Image to colored ASCIIchafa- Modern terminal graphics:chafa -s 40x20 image.png
For terminals with image support, you can use actual images instead of ASCII.
| Terminal | Protocol | Image Support |
|---|---|---|
| iTerm2 | iTerm | Full |
| Kitty | Kitty | Full |
| WezTerm | Kitty | Full |
| VS Code Terminal | Limited | Partial |
| Standard terminals | ANSI only | ASCII fallback |
| Property | Recommended | Notes |
|---|---|---|
| Format | PNG | Best quality, supports transparency |
| Size | 200x200 px | Will be scaled to terminal size |
| Aspect ratio | 1:1 | Square works best |
| Transparency | Optional | Looks great on dark terminals |
You can use any image editor. For animated states, consider:
- Static images per state - One image per state, simpler to manage
- GIF frames - Extract GIF frames for animation
Extract frames from GIF:
# Extract all frames
ffmpeg -i animated.gif -vsync 0 frame_%02d.png
# Or use ImageMagick
convert animated.gif frame_%02d.pngTerminal mode and browser mode are mutually exclusive:
terminal.enabled |
Behavior |
|---|---|
true |
Avatar renders in terminal, browser does NOT open |
false or missing |
Browser opens, shows WebM videos (default) |
Even if an avatar has terminal support, you can force browser mode:
byteside --no-terminalbyteside validate ./my-avatar# If terminal.enabled is true, it auto-activates
byteside --avatar my-avatar# In another terminal
byteside trigger thinking
byteside trigger writing
byteside trigger success
byteside trigger idleThe avatar should render below the server status messages and animate through its frames.
Here's a complete minimal example:
manifest.json:
{
"name": "blinky",
"author": "byteside",
"version": "1.0.0",
"format": "webm",
"states": {
"idle": { "file": "idle.webm" },
"thinking": { "file": "thinking.webm" },
"writing": { "file": "writing.webm" },
"bash": { "file": "bash.webm" },
"error": { "file": "error.webm" },
"success": { "file": "success.webm" },
"waiting": { "file": "waiting.webm" }
},
"terminal": {
"enabled": true,
"mode": "ascii",
"framerate": 2,
"size": { "width": 20, "height": 10 },
"states": {
"idle": {
"frames": ["terminal/idle/open.txt", "terminal/idle/open.txt", "terminal/idle/open.txt", "terminal/idle/closed.txt"]
},
"thinking": { "frames": ["terminal/thinking.txt"] },
"writing": { "frames": ["terminal/writing.txt"] },
"bash": { "frames": ["terminal/bash.txt"] },
"error": { "frames": ["terminal/error.txt"] },
"success": { "frames": ["terminal/success.txt"] },
"waiting": { "frames": ["terminal/waiting.txt"] }
}
}
}terminal/idle/open.txt:
.--------.
/ \
| O O |
| > |
| \__/ |
\ /
'--------'
terminal/idle/closed.txt:
.--------.
/ \
| - - |
| > |
| \__/ |
\ /
'--------'
terminal/thinking.txt:
.--------.
/ \
| O O | ?
| > |
| ---- |
\ /
'--------'
terminal/success.txt:
.--------.
/ \
| ^ ^ |
| > |
| \__/ |
\ /
'--------'
- Check
terminal.enabledistruein manifest - Verify TTY - Terminal mode requires an interactive terminal (not piped output)
- Check frame files exist - Run
byteside validateto verify paths
- Lower the
framerate(try4or2) - Reduce frame complexity (simpler ASCII art)
- Ensure frames are the same size
- Your terminal may not support ANSI colors
- Check terminal emulator settings
- Try a simpler color scheme
- Verify your terminal supports inline images (iTerm2, Kitty, WezTerm)
- Check that image files exist at the specified paths
- Try ASCII mode as fallback
All frames should have the same dimensions. If frames have different sizes, the animation may look jumpy. Use a fixed-width font and pad lines to equal length:
# Pad all lines in a file to 40 characters
awk '{printf "%-40s\n", $0}' frame.txt > frame_padded.txtYou can convert your existing WebM videos to ASCII art frames:
# Extract frames from video
ffmpeg -i idle.webm -r 8 frame_%03d.jpg
# Convert each frame to ASCII
for f in frame_*.jpg; do
jp2a --width=40 --output="${f%.jpg}.txt" "$f"
done
# Clean up JPGs
rm frame_*.jpg# Extract frames
ffmpeg -i idle.webm -r 8 frame_%03d.png
# Convert with colors
for f in frame_*.png; do
chafa -s 40x20 --format=symbols "$f" > "${f%.png}.txt"
done# Extract frames
ffmpeg -i idle.webm -r 8 frame_%03d.png
# Convert to ASCII
for f in frame_*.png; do
img2txt -W 40 -H 20 "$f" > "${f%.png}.txt"
done- Start simple - Create a basic blinking face avatar
- Add states - Design different expressions for each state
- Refine animation - Adjust framerate and frame count for smooth motion
- Test thoroughly - Try on different terminals
- Share - Publish your terminal avatar for others to enjoy
For creating the browser-based video assets, see the Avatar Creation Guide and Kling Motion Control Guide.