GOAL
A desktop pet / VTuber window that:
- floats on the desktop
- animates (idle, blink, talk)
- stays always‑on‑top
- runs at 60 FPS
- never blocks your main program
- communicates with your main logic (e.g., “talk”, “blink”, “angry”, “idle”)
This is the same pattern used by commercial VTuber overlays.
ARCHITECTURE (THE ONLY ONE THAT NEVER JAMS)
Main Program (LivinGrimoire engine)
- async
- skills
- STT
- TTS
- logic
VTuberRenderer (separate process)
- handles window
- handles animation
- handles transparency
- handles FPS loop
Communication channel
- multiprocessing.Queue
- or multiprocessing.Pipe
This is the canonical pattern for non‑blocking UI.
WALKTHROUGH STEP 1
Create the VTuber Renderer Process
vtuber_renderer.py:
Python:
import pygame
import multiprocessing
import time
import os
def vtuber_process(cmd_queue):
pygame.init()
# Load frames for animation
idle = pygame.image.load("idle.png")
talk = pygame.image.load("talk.png")
blink = pygame.image.load("blink.png")
current = idle
w, h = current.get_size()
screen = pygame.display.set_mode((w, h), pygame.NOFRAME)
pygame.display.set_caption("DesktopPet")
last_blink = time.time()
while True:
# Handle commands from main program
if not cmd_queue.empty():
cmd = cmd_queue.get()
if cmd == "talk":
current = talk
elif cmd == "idle":
current = idle
elif cmd == "blink":
current = blink
elif cmd == "quit":
break
# Auto-blink every 4 seconds
if time.time() - last_blink > 4:
current = blink
pygame.display.update()
time.sleep(0.1)
current = idle
last_blink = time.time()
for event in pygame.event.get():
if event.type == pygame.QUIT:
return
screen.blit(current, (0, 0))
pygame.display.update()
time.sleep(1/60) # 60 FPS
pygame.quit()
This is a real desktop pet loop:
- 60 FPS
- idle/talk/blink states
- non-blocking
- accepts commands
WALKTHROUGH STEP 2
Start the VTuber process from your main program
main.py:
Python:
import multiprocessing
import time
from vtuber_renderer import vtuber_process
if __name__ == "__main__":
cmd_queue = multiprocessing.Queue()
p = multiprocessing.Process(target=vtuber_process, args=(cmd_queue,))
p.start()
# Example: your main program runs normally
while True:
print("Main program running...")
# Make the VTuber talk
cmd_queue.put("talk")
time.sleep(1)
# Back to idle
cmd_queue.put("idle")
time.sleep(2)
Result:
Your main program runs smoothly, and the VTuber window animates independently.
WALKTHROUGH STEP 3
Integrate with LivinGrimoire Skill System
You wrap the renderer in a skill:
class VTuberSkill:
def __init__(self):
self.cmd_queue = multiprocessing.Queue()
self.p = multiprocessing.Process(
target=vtuber_process,
args=(self.cmd_queue,)
)
self.p.start()
def talk(self):
self.cmd_queue.put("talk")
def idle(self):
self.cmd_queue.put("idle")
def blink(self):
self.cmd_queue.put("blink")
def kill(self):
self.cmd_queue.put("quit")
self.p.join()
Now any skill can do:
vtuber_skill.talk()
vtuber_skill.idle()
This is exactly how you integrate a desktop pet into a modular skill engine.
WALKTHROUGH STEP 4
Add transparency + always-on-top (optional)
If you want the pet floating on the desktop:
- Windows: use pywin32 to set layered window
- Linux: use xlib
- Mac: use NSWindow flags
I can give you the exact code for your OS.
WHY THIS NEVER JAMS
Because:
- Rendering is in another process
- Python GIL cannot block it
- Your AI logic can spike CPU and the VTuber stays smooth
- Communication is async and non-blocking
This is the correct architecture for a desktop VTuber.