PunithVT/ai-avatar-system

🎭 AI Avatar / digital human platform β€” upload a photo, clone a voice, talk to any face in real time with lip-sync video. Open-source, self-hosted. Claude Β· Whisper Β· Chatterbox Β· MuseTalk.

Python

505

177 commits

updated Sep 18, 2026

See the code

README

🎭 AvatarAI β€” Real-Time AI Avatar Platform

Upload a photo Β· Clone a voice Β· Talk to any face in real time

Stars Forks Issues MIT License Sponsor

Quick Start Β· Features Β· Architecture Β· GPU / AWS Deploy Β· API Β· Roadmap

The most complete open-source AI avatar / digital human system. Real-time talking-head lip-sync Β· Zero-shot voice cloning Β· Multi-LLM Β· Runs 100% locally or on AWS.


🎬 What is AvatarAI?

AvatarAI is an open-source, production-ready platform for building photorealistic AI avatar conversations. Upload any face photo, clone a voice from a 5-second audio clip, and have a real-time conversation β€” with lip-sync video generated on every single response.

[mic] β†’ Whisper STT β†’ Claude / GPT / Ollama (streaming) β†’ Chatterbox TTS β†’ MuseTalk lip-sync β†’ [video]
                              < 2–4 s to first video chunk on AWS GPU >

What makes AvatarAI different:

  • 🎀 Zero-shot voice cloning β€” 10 seconds of audio is all you need (Chatterbox Multilingual)
  • 🎭 Any face, any language β€” upload a JPEG, pick from 23 languages, start talking
  • ⚑ Token-streaming pipeline β€” the LLM streams live tokens while TTS + lip-sync run per sentence; the first video chunk plays before the model finishes its reply
  • βœ‹ Barge-in β€” speak (or hit stop) mid-reply and the avatar yields instantly, like a real conversation
  • πŸ”’ 100% local mode β€” local storage, local Whisper, local LLM via Ollama: nothing leaves your machine
  • πŸ”Œ Multi-LLM β€” Claude (with prompt caching), GPT-4o, or any local model via Ollama / vLLM / LM Studio
  • πŸš€ AWS GPU deployment β€” one-command deploy to g5.xlarge for true real-time (~30 FPS)
  • πŸ—οΈ Production-grade β€” JWT + httpOnly-cookie auth, per-user rate limiting, Postgres + Alembic, S3/CloudFront, Prometheus, CI, a real test suite β€” the only project in this niche you can ship as a product, not just a demo

βš”οΈ How AvatarAI compares

AvatarAIDuix-AvatarLinly-TalkerAIAvatarKit
Real-time conversationβœ… WebSocket streaming❌ offline video genβœ… (Gradio / WebRTC spin-off)βœ…
Lip-sync videoβœ… MuseTalk V1.5βœ… proprietary modelsβœ… multiple engines❌ (drives external avatars)
Voice cloningβœ… 10 s, 23 languagesβœ…βœ…βŒ
Barge-in / interruptionβœ…βŒβœ… (stream variant)βœ…
Local / free LLMβœ… Ollama, vLLMβŒβœ…βœ…
Web app with auth & historyβœ… Next.js + JWT + Postgres❌ Windows client❌ Gradio demo UI❌ library
Rate limiting, CI, tests, IaCβœ…βŒβŒβŒ
LicenseMITcustomMITApache-2.0

Toolkits like Linly-Talker are great research playgrounds; Duix ships a Windows product. AvatarAI is the one you can deploy as a real multi-user web service.


✨ Features

CategoryDetails
πŸ€– LLM BackendsClaude (prompt-cached) Β· GPT-4o Β· Ollama / vLLM / LM Studio (local, free)
🎀 Voice CloningRecord 10–60 s β†’ Chatterbox Multilingual zero-shot cloning
πŸ—£οΈ Speech-to-TextWhisper (faster-whisper, CUDA), decodes browser WebM natively
🎬 Lip-Sync VideoMuseTalk V1.5 persistent worker (30 FPS on GPU) · optional GFPGAN restore · FFmpeg fallback (CPU)
⚑ Streaming PipelineLive LLM tokens + per-sentence video chunks over WebSocket
βœ‹ Barge-InSpeak or hit stop mid-reply β€” in-flight turn cancels in ms
πŸ”‰ TTS Fallback Chainchatterbox (optional, separate venv) β†’ edge-tts (free neural voices) β†’ gTTS β€” never silent
😊 Emotion DetectionLive emotion badges per message
🌍 23 LanguagesWhisper multilingual STT + Chatterbox multilingual TTS
🏠 Local-First StorageUSE_LOCAL_STORAGE=true β€” no AWS needed for dev
πŸ” Auth & SessionsJWT authentication, conversation history, persistent sessions
πŸ“Š ObservabilityPrometheus Β· Celery Flower Β· Sentry Β· structured logging
πŸ§ͺ TestedFull pytest suite β€” users, avatars, sessions, health checks
πŸš€ AWS GPU DeployOne-command g5.xlarge deploy with CUDA 11.8 + float16

πŸ—οΈ Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                        Browser / Client                       β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚  β”‚Avatar Studioβ”‚  β”‚ Voice Studio β”‚  β”‚   Chat Interface     β”‚ β”‚
β”‚  β”‚  (upload)   β”‚  β”‚  (cloning)   β”‚  β”‚ Idle anim + chunks   β”‚ β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
          β”‚ REST           β”‚ REST                β”‚ WebSocket
          β–Ό                β–Ό                     β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                       FastAPI Backend                         β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”‚
β”‚  β”‚                  WebSocket Manager                    β”‚    β”‚
β”‚  β”‚  split sentences β†’ TTS β†’ MuseTalk β†’ stream chunks    β”‚    β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚  Whisper β”‚ β”‚Claude/GPT β”‚ β”‚ XTTS v2  β”‚ β”‚  MuseTalk     β”‚  β”‚
β”‚  β”‚   STT    β”‚ β”‚  / Llama  β”‚ β”‚   TTS    β”‚ β”‚  (GPU/CPU)    β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚PostgreSQLβ”‚ β”‚  Redis   β”‚  β”‚  Celery  β”‚ β”‚ Local FS / S3 β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Real-Time Data Flow (one conversation turn)

[User types / speaks]
        β”‚
        β–Ό
  Whisper STT ─────────────────► transcript
        β”‚
        β–Ό
  Claude / GPT / Llama ────────► full response text
        β”‚
        β–Ό
  Split into sentences ────────► ["Hello!", "How are you?", ...]
        β”‚
        β”œβ”€β”€ sentence 1 β†’ XTTS β†’ MuseTalk β†’ video_chunk WS β†’ browser plays
        β”œβ”€β”€ sentence 2 β†’ XTTS β†’ MuseTalk β†’ video_chunk WS β†’ queued
        └── sentence N β†’ XTTS β†’ MuseTalk β†’ video_chunk WS β†’ queued

πŸ“ Project Structure

ai-avatar-system/
β”œβ”€β”€ backend/                    # FastAPI application
β”‚   β”œβ”€β”€ app/
β”‚   β”‚   β”œβ”€β”€ api/v1/             # REST endpoints (users, avatars, sessions, messages)
β”‚   β”‚   β”œβ”€β”€ services/           # Core services (LLM, TTS, STT, animator, storage)
β”‚   β”‚   β”œβ”€β”€ models/             # SQLAlchemy DB models
β”‚   β”‚   └── websocket.py        # Real-time WebSocket handler + sentence streaming
β”‚   β”œβ”€β”€ alembic/                # Database migrations
β”‚   β”œβ”€β”€ models/MuseTalk/        # MuseTalk V1.5 (lip-sync engine)
β”‚   β”‚   └── scripts/
β”‚   β”‚       └── musetalk_worker.py  # Persistent worker (models loaded once)
β”‚   β”œβ”€β”€ tests/                  # pytest suite
β”‚   β”œβ”€β”€ Dockerfile              # CUDA 11.8 base image
β”‚   └── requirements.txt
β”œβ”€β”€ frontend/                   # Next.js 16 application
β”‚   β”œβ”€β”€ app/                    # App Router pages
β”‚   β”œβ”€β”€ components/             # React components (ChatInterface, IdleAvatar, etc.)
β”‚   β”œβ”€β”€ lib/api.ts              # Axios API client
β”‚   └── store/                  # Zustand global state
β”œβ”€β”€ nginx/
β”‚   └── nginx.conf              # Reverse proxy (HTTP β†’ backend/frontend, WebSocket)
β”œβ”€β”€ infrastructure/
β”‚   β”œβ”€β”€ main.tf                 # AWS Terraform (ECS, RDS, ElastiCache, S3, CloudFront)
β”‚   └── variables.tf
β”œβ”€β”€ scripts/
β”‚   β”œβ”€β”€ setup_musetalk.sh       # Download MuseTalk models (~9 GB)
β”‚   └── deploy-aws.sh           # One-command EC2 GPU deployment
β”œβ”€β”€ docker-compose.yml          # Development (CPU) β€” all services
β”œβ”€β”€ docker-compose.prod.yml     # Production overrides (GPU, no bind mounts, logging)
β”œβ”€β”€ deploy.sh                   # ECR push + Terraform deploy (ECS path)
β”œβ”€β”€ .env.example                # Development env template
└── .env.prod.example           # Production env template

πŸš€ Quick Start

Prerequisites

  • Docker & Docker Compose v2+ (recommended)
  • OR: Python 3.10+, Node.js 18+, FFmpeg, PostgreSQL, Redis

Option A β€” Docker / CPU (development)

git clone https://github.com/PunithVT/ai-avatar-system.git
cd ai-avatar-system
cp .env.example .env          # add your ANTHROPIC_API_KEY (or OPENAI_API_KEY)
docker compose up -d
ServiceURL
πŸ–₯️ Frontendhttp://localhost:3000
βš™οΈ Backend APIhttp://localhost:8000
πŸ“– Swagger Docshttp://localhost:8000/docs
🌸 Celery Flowerhttp://localhost:5555

No AWS required. Set USE_LOCAL_STORAGE=true (default) β€” uploads saved to backend/uploads/.

Want something to talk to immediately? Seed three ready-made demo avatars (AI-generated faces + personalities):

backend/venv/bin/python scripts/seed_demo.py            # or any python with `requests`
backend/venv/bin/python scripts/seed_demo.py --with-voices   # + cloned demo voices

Prebuilt images are also published on every release β€” ghcr.io/punithvt/ai-avatar-system-backend and …-frontend.

Option B β€” Manual (development)

# Backend
cd backend
python -m venv venv && source venv/bin/activate
pip install -r requirements.txt
cp ../.env.example ../.env
alembic upgrade head
uvicorn main:app --reload --port 8000

# Frontend (new terminal)
cd frontend
npm install
npm run dev

Option C β€” Enable MuseTalk Lip-Sync

# Download models (~9 GB, one-time)
bash scripts/setup_musetalk.sh

# Set in .env
AVATAR_ENGINE=musetalk

# Restart
docker compose restart backend

πŸš€ GPU & AWS Deployment

MuseTalk achieves 30 FPS at 256Γ—256 on a V100-class GPU (source: MuseTalk paper). On CPU it is 30–50Γ— slower. Deploying on AWS gets you genuine real-time performance.

InstanceGPUVRAMSpot $/hrMuseTalk FPS
g4dn.xlargeT416 GB~$0.16~15–20 FPS
g5.xlargeA10G24 GB~$0.30~30 FPS βœ“
g6.xlargeL424 GB~$0.24~30 FPS βœ“

Recommended: g5.xlarge Spot (~$72/mo at 8 hrs/day).

One-Command EC2 Deploy

# 1. Launch g5.xlarge with Ubuntu 22.04 LTS, SSH in, then:
bash <(curl -fsSL https://raw.githubusercontent.com/PunithVT/ai-avatar-system/main/scripts/deploy-aws.sh)

# 2. Fill in API keys:
nano /opt/ai-avatar-system/.env.prod

# 3. Redeploy with your keys:
bash /opt/ai-avatar-system/scripts/deploy-aws.sh --update

The script automatically:

  • Installs Docker + nvidia-docker2
  • Verifies GPU is accessible
  • Downloads MuseTalk models (~9 GB)
  • Starts all services with GPU passthrough + float16 (2Γ— faster via Tensor Cores)

Manual Production Docker

cp .env.prod.example .env.prod   # fill in your values
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

What docker-compose.prod.yml adds over development:

  • GPU reservation (nvidia driver, count=1) for backend + celery-worker
  • float16 inference enabled automatically on CUDA β†’ ~2Γ— speedup
  • Persistent musetalk_models volume (survive container restarts)
  • No source-code bind mounts (runs from built image)
  • Log rotation (100 MB max, 5 files)
  • Flower disabled (security)

Verify GPU is Working

# Check GPU is visible in container
docker exec avatar-backend python -c "
import torch
print('CUDA:', torch.cuda.is_available())
print('GPU:', torch.cuda.get_device_name(0))
print('VRAM:', round(torch.cuda.get_device_properties(0).total_memory/1024**3,1), 'GB')
"

# Expected on g5.xlarge:
# CUDA: True
# GPU: NVIDIA A10G
# VRAM: 24.0 GB

# Live GPU utilisation
docker exec avatar-backend nvidia-smi

AWS Terraform (ECS Path)

For a fully managed ECS deployment with RDS + ElastiCache + CloudFront:

cd infrastructure
terraform init
terraform apply -var="environment=production"
bash deploy.sh production

πŸŽ›οΈ Avatar Engines

AVATAR_ENGINE selects how video is generated. MuseTalk is the default and stays installed whatever else you add; any engine failure falls back to simple rather than failing the turn.

EngineVRAMSpeedUse it for
simplenoneinstantCPU hosts, no lip-sync
musetalk16–24 GB~30 FPSdefault β€” live conversation
liveavatar48 GB (FP8) / 80 GBminutes per turnoffline renders where fidelity beats latency

LiveAvatar (optional)

Alibaba LiveAvatar (Wan2.2-S2V-14B

  • LoRA, Apache 2.0) produces higher fidelity than MuseTalk and supports 10,000+ second continuous generation from a single reference image.
bash scripts/setup_liveavatar.sh    # checks VRAM first, then ~60 GB of weights
# then in .env:
AVATAR_ENGINE=liveavatar

It is not a MuseTalk replacement, and MuseTalk remains the default. Two reasons, both structural:

  • 48 GB VRAM minimum with FP8, 80 GB without. That is a different hardware tier from the g5.xlarge this project targets.
  • It loads the 14B model on every invocation. Upstream's entry point (minimal_inference/s2v_streaming_interact.py) runs once and exits β€” there is no persistent-worker mode, which is exactly what MuseTalk uses to keep per-turn cost to inference alone. So a turn costs minutes, not milliseconds.

That makes LiveAvatar a fit for the offline Celery render path, where a slow better-looking result is the point, and a poor fit for live conversation. Switching back is one setting: AVATAR_ENGINE=musetalk.

Giving LiveAvatar a persistent worker would mean patching upstream. That is the maintenance debt that got SadTalker removed from this project, so it has deliberately not been done.


πŸ” Face Restoration (optional)

MuseTalk regenerates the mouth region at 256Γ—256 and the worker scales it back up to the crop size, while the avatar itself is stored at AVATAR_RESOLUTION (512). The mouth therefore renders at roughly half the resolution of the face around it β€” and that gap, not the model, is the pipeline's visible quality ceiling. Swapping lip-sync engines doesn't close it; a face restorer does.

bash scripts/setup_face_restore.sh   # installs gfpgan + ~520 MB of weights
# then in .env:
FACE_RESTORE=gfpgan                  # off | gfpgan

gfpgan is deliberately not in requirements.txt. It depends on basicsr, which ships no wheel and builds from source, and whose setup.py collides with the CUDA base image's preinstalled cuda-toolkit β€” listing it there failed the backend image build for everyone, including people who never enable restoration. It lives in backend/requirements-face-restore.txt and is installed by the setup script, the same way MuseTalk and Chatterbox are.

For the same reason, restoration is a bare-metal / GPU-host feature today: inside the CUDA container the basicsr build needs work that hasn't been done yet.

GFPGAN runs over each composited frame after MuseTalk has blended its generated mouth back in, so it sees a complete face to detect.

It is off by default, deliberately. Restoration runs per frame, so it trades FPS for sharpness, and whether that trade is worth it depends on your GPU and how close to real-time you need to stay. A/B it against FACE_RESTORE=off on the same avatar and audio before leaving it on.

Everything about it degrades rather than breaks: missing weights, a broken gfpgan install, CPU-only hardware, or a single frame that fails to restore all fall back to plain MuseTalk output. Lip-sync is the feature; sharpness is the nicety.


🧠 Conversation Memory

The LLM context is a fixed window of the most recent turns (MAX_CONTEXT_MESSAGES, 60). Anything older used to be dropped outright, so a long conversation quietly forgot its own beginning β€” the avatar would lose your name twenty minutes in.

Turns leaving the window are now folded into a running summary carried in the system prompt, and persisted to Conversation.summary so a browser refresh doesn't lose it.

CONVERSATION_MEMORY=true          # default
MEMORY_SUMMARY_MAX_CHARS=1500

A summary rather than embeddings, deliberately. It costs one LLM call when the window overflows β€” roughly once every 30 turns, not once per reply β€” needs no vector store or embedding model, and works with every provider the project supports. Requiring an embeddings API would have broken the "runs fully local and free with Ollama" path, since Anthropic has no embeddings endpoint and a local embedding model is another dependency to install and keep working.

The tradeoff is honest: a summary is lossy. It carries the thread of a conversation, not verbatim recall of a detail from an hour ago. Fact-level retrieval is what RAG would add, and that remains open on the roadmap.

Failure never costs a turn β€” if summarisation fails, the window truncates exactly as it did before and the reply proceeds.


πŸ™Œ Hands-Free Mode

Tap the Radio button next to the mic and just talk β€” no tap-to-record. The avatar detects when you start and stop speaking, and speaking over a reply interrupts it.

How turns are decided (frontend/lib/vad.ts):

  • Adaptive threshold. The detector measures the room's ambient level for ~700 ms, then listens for speech a margin above it, and keeps tracking the floor while nobody is talking. A fixed threshold works at a desk and fails in a cafe.
  • Pause tolerance. A turn ends after ~900 ms of continuous silence, so "I think… maybe we should" stays one turn instead of three.
  • Noise rejection. Anything shorter than ~300 ms is discarded rather than sent, so a cough or a door doesn't cost an STT call and an LLM turn.
  • Echo cancellation is requested on the mic. Without it the open mic hears the avatar through the speakers and the session talks to itself.

The detector is pure β€” it takes (level, timestamp) samples and returns turn boundaries β€” so its timing logic can be exercised against a synthetic trace without a browser or a microphone.


🎀 Voice Cloning

Powered by Chatterbox Multilingual (Resemble AI) β€” zero-shot voice cloning from a 10-second sample, in 23 languages.

  1. Go to Voice tab β†’ Clone Voice
  2. Record 10–60 s of clear speech (or upload a WAV/MP3/WebM)
  3. Name it β†’ Clone β†’ select it for your session

Every TTS response then uses your cloned voice.

Chatterbox needs its own environment. It is not in requirements.txt, because it pins torch==2.6.0 / transformers==5.2.0 / librosa==0.11.0 while the MuseTalk lip-sync pipeline needs torch 2.2 / transformers 4.37 β€” the two cannot share one virtualenv. Install it separately:

python -m venv venv-tts
./venv-tts/bin/pip install -r backend/requirements-tts.txt

Without it nothing breaks: TTS falls through to edge-tts (free Microsoft neural voices, CPU-only) and then gTTS. Voice cloning is the only feature that requires Chatterbox, so set TTS_PROVIDER=chatterbox only once it is actually installed.

# REST API
curl -X POST http://localhost:8000/api/v1/voices/clone \
  -F "audio=@my_voice.wav" -F "name=My Voice" -F "language=en"

πŸ“‘ API Reference

Authentication

POST /api/v1/users/register   { "email": "...", "username": "...", "password": "..." }
POST /api/v1/users/login      form: username=... password=...   β†’ { "access_token": "..." }
POST /api/v1/users/guest      (no body)                         β†’ { "access_token": "..." }
POST /api/v1/users/logout     clears the httpOnly auth cookie

# Every resource route requires an identity β€” pass the bearer token:
Authorization: Bearer <access_token>
# ...or rely on the httpOnly cookie that /login and /guest set for browsers.

/guest backs the UI's Continue as Guest button. It creates a real but anonymous account, so a guest's avatars and transcripts are scoped to them alone β€” guests never share data with each other. Guest accounts have no usable password (they are reachable only via the token above) and are deleted after GUEST_RETENTION_HOURS of inactivity by the cleanup_guest_accounts task. Set GUEST_ACCOUNTS_ENABLED=false to require sign-up.

Registration rules: passwords are 8–128 characters, usernames are 3–39 characters of [A-Za-z0-9_-] (must start and end alphanumeric), and email addresses are normalised to lower case so User@x.com and user@x.com are the same account.

Avatars

POST   /api/v1/avatars/upload        Upload photo (multipart: file + name)
                                     limits: MAX_UPLOAD_SIZE (413 if over),
                                     ALLOWED_EXTENSIONS, name ≀ 200 chars
GET    /api/v1/avatars/              List avatars
DELETE /api/v1/avatars/{id}          Delete avatar
PUT    /api/v1/avatars/{id}/voice    Assign voice to avatar

Sessions & Messages

POST   /api/v1/sessions/create       { "avatar_id": "..." }
POST   /api/v1/sessions/{id}/end
GET    /api/v1/messages/session/{id}

WebSocket

WS  /ws/session/{session_id}

Client β†’ Server:

{ "type": "text",         "text": "Hello!" }
{ "type": "audio",        "audio": "<base64-webm>" }
{ "type": "stop" }                                  // barge-in: cancel the in-flight reply
{ "type": "set_voice",    "voice_id": "<uuid>" }    // attach a cloned voice (owner-checked)
{ "type": "set_language", "language": "es" }
{ "type": "ping" }

Server β†’ Client:

{ "type": "token",            "token": "Hel" }       // live LLM stream
{ "type": "transcription",    "text": "Hello!" }
{ "type": "message",          "content": "Hi!", "role": "assistant" }
{ "type": "video_chunk_start","total_chunks": -1 }   // -1 = streaming, total unknown
{ "type": "video_chunk",      "chunk_index": 0, "video_url": "...", "text": "Hi!" }
{ "type": "video_chunk_end",  "sent_chunks": 3 }
{ "type": "status",           "message": "Animating…", "stage": "animation" }
{ "type": "tts_fallback",     "engine": "edge-tts", "voice_cloned": false, "message": "…" }
{ "type": "interrupted",      "message": "Previous response interrupted" }
{ "type": "error",            "message": "Something went wrong" }

βš™οΈ Configuration

Key .env variables:

# LLM
LLM_PROVIDER=anthropic            # anthropic | openai | ollama (local & free)
LLM_MODEL=claude-sonnet-4-6       # or gpt-4o Β· llama3.1 Β· qwen2.5 …
ANTHROPIC_API_KEY=sk-ant-...
OPENAI_BASE_URL=                  # e.g. http://localhost:11434/v1 for Ollama / vLLM / LM Studio

# Avatar engine
AVATAR_ENGINE=musetalk            # musetalk (default) | liveavatar (48GB+) | simple (CPU)
MUSETALK_PATH=models/MuseTalk

# TTS β€” automatic fallback chain: chatterbox β†’ edge-tts β†’ gtts
# chatterbox requires backend/requirements-tts.txt in its own venv (see
# "Voice Cloning"); without it this transparently uses edge-tts.
TTS_PROVIDER=chatterbox

# STT
WHISPER_MODEL=large-v3-turbo      # tiny | base | small | medium | large-v3 | large-v3-turbo

# Storage
USE_LOCAL_STORAGE=true            # false β†’ AWS S3 (+ presigned URLs / CloudFront)
S3_BUCKET_NAME=...

# Auth (β‰₯32 chars enforced at boot)
SECRET_KEY=$(python -c "import secrets; print(secrets.token_hex(32))")
JWT_SECRET_KEY=$(python -c "import secrets; print(secrets.token_hex(32))")
JWT_EXPIRATION_HOURS=24

πŸ› οΈ Tech Stack

Frontend

LibraryPurpose
Next.js 16 + React 18App framework
TypeScript 5Type safety
Tailwind CSSStyling
ZustandGlobal state

Backend

LibraryPurpose
FastAPIAsync REST API + WebSocket
SQLAlchemy 2 (async)ORM with asyncpg
PostgreSQL 15Primary database
AlembicMigrations
Redis 7Cache + Celery broker
CeleryBackground tasks

AI / ML

ModelPurpose
Claude / GPT-4o / Ollama (local)LLM conversation
Whisper (faster-whisper)Speech-to-text
Chatterbox Multilingual (Resemble AI)TTS + zero-shot voice cloning, 23 languages
Edge TTS β†’ gTTSFree no-GPU fallback voices
MuseTalk V1.5Photorealistic lip-sync (30 FPS on GPU)

πŸ§ͺ Running Tests

cd backend
pytest -v                           # all tests
pytest tests/test_health.py         # single module
pytest --cov=app --cov-report=html  # HTML coverage

πŸ“° What's New

  • 2026-06 β€” Edge-TTS neural fallback chain Β· local LLMs via Ollama/vLLM Β· demo avatar seeding Β· prebuilt GHCR images Β· cascade-delete + WebM-STT + 429 fixes Β· SEO/metadata pass
  • 2026-05 β€” httpOnly-cookie auth (XSS-safe) Β· conversation resume from history Β· end-to-end WebSocket tests Β· perf indexes (migration 0002)
  • 2026-03 β€” Chatterbox Multilingual replaces XTTS v2 (23 languages) Β· MuseTalk persistent worker (models load once) Β· barge-in interruption Β· live token streaming

πŸ—ΊοΈ Roadmap

  • Streaming LLM β€” TTS + lip-sync start before the LLM finishes (token-by-token) βœ…
  • Barge-in β€” interrupt the avatar mid-reply by speaking βœ…
  • Local LLMs β€” Ollama / vLLM / LM Studio via OpenAI-compatible API βœ…
  • Hands-free mode β€” VAD-driven always-listening with auto end-of-turn (no tap-to-record) βœ…
  • WebRTC streaming β€” sub-second full-duplex audio/video instead of chunked MP4
  • Wav2Lip engine β€” lighter lip-sync option for weaker GPUs
  • Emotion-driven animation β€” detected emotion changes facial expression
  • Embeddable widget β€” drop a talking avatar into any website with 3 lines of JS
  • Long-term memory β€” rolling summary carries context past the window βœ… (RAG + vector DB still open for fact-level recall)
  • UI i18n β€” the pipeline speaks 23 languages; the UI should too

❓ FAQ

Do I need a GPU?

No β€” everything runs on CPU. MuseTalk takes 30–90 s/sentence on CPU (the simple engine is instant). For real-time lip-sync, use an AWS g5.xlarge (~$0.30/hr spot) or any 16 GB+ NVIDIA card.

Can I run it with no API key, fully offline?

Yes β€” set LLM_PROVIDER=ollama, run Ollama (ollama run llama3.1), and you have a fully local, free conversation stack: Whisper STT, local LLM, Chatterbox TTS, MuseTalk video.

How do I get something to talk to quickly?

Run python scripts/seed_demo.py β€” it creates three demo avatars (AI-generated faces, distinct personalities) and optionally cloned demo voices with --with-voices.

How do I get MuseTalk models?

Run bash scripts/setup_musetalk.sh β€” downloads ~9 GB of models automatically.

Why does the first response take longer?

The MuseTalk persistent worker loads all models into GPU VRAM on the first request (~60 s on GPU, ~5 min on CPU). Subsequent requests reuse the loaded models.

What happens if the TTS model can't load?

The pipeline degrades gracefully: chatterbox β†’ edge-tts (free Microsoft neural voices) β†’ gTTS. The UI shows a one-time notice when a cloned voice couldn't be applied.

What avatar photo works best?

A clear, well-lit frontal face photo (JPEG/PNG/WebP). Avoid sunglasses or heavy occlusion.


🀝 Contributing

Contributions welcome! Read CONTRIBUTING.md before opening a PR.

git clone https://github.com/PunithVT/ai-avatar-system.git
git checkout -b feat/my-feature
# make changes + tests
git commit -m "feat(backend): add my feature"
git push origin feat/my-feature

πŸ’œ Sponsor

AvatarAI is MIT-licensed and developed in the open. If it saves you time, or your company is running it in production, sponsoring directly funds the work that is expensive to do for free:

  • GPU time for testing the MuseTalk lip-sync pipeline across resolutions and hardware
  • Model evaluation as new TTS and STT releases land, so the defaults stay current instead of drifting
  • Maintenance β€” dependency and security upkeep, issue triage, and review
Sponsor on GitHub

Not in a position to sponsor? Starring the repo, filing a good bug report, or writing about what you built with it all genuinely help.


πŸ“„ License

MIT Β© 2026 β€” see LICENSE for details.


If AvatarAI saves you time or inspires your project, please ⭐ star the repo.



Star History Chart



Built with FastAPI Β· Next.js Β· MuseTalk V1.5 Β· Chatterbox Β· Whisper Β· Claude AI

ai-avatar
avatar-ai
chatterbox-tts
claude-ai
digital-human
fastapi
generative-ai
lip-sync
llm
musetalk
nextjs
real-time
speech-to-text
talking-avatar
talking-head
text-to-speech
voice-agent
voice-cloning
websocket
whisper-ai

Significant stargazers

Diego Rodrigues de Sa e Souza

1,258 followers Β· starred Aug 2026

dsd

43 followers Β· starred Jun 2026

PunithVT/ai-avatar-system

🎭 AI Avatar / digital human platform β€” upload a photo, clone a voice, talk to any face in real time with lip-sync video. Open-source, self-hosted. Claude Β· Whisper Β· Chatterbox Β· MuseTalk.

Python

505

177 commits

updated Sep 18, 2026

See the code

README

🎭 AvatarAI β€” Real-Time AI Avatar Platform

Upload a photo Β· Clone a voice Β· Talk to any face in real time

Stars Forks Issues MIT License Sponsor

Quick Start Β· Features Β· Architecture Β· GPU / AWS Deploy Β· API Β· Roadmap

The most complete open-source AI avatar / digital human system. Real-time talking-head lip-sync Β· Zero-shot voice cloning Β· Multi-LLM Β· Runs 100% locally or on AWS.


🎬 What is AvatarAI?

AvatarAI is an open-source, production-ready platform for building photorealistic AI avatar conversations. Upload any face photo, clone a voice from a 5-second audio clip, and have a real-time conversation β€” with lip-sync video generated on every single response.

[mic] β†’ Whisper STT β†’ Claude / GPT / Ollama (streaming) β†’ Chatterbox TTS β†’ MuseTalk lip-sync β†’ [video]
                              < 2–4 s to first video chunk on AWS GPU >

What makes AvatarAI different:

  • 🎀 Zero-shot voice cloning β€” 10 seconds of audio is all you need (Chatterbox Multilingual)
  • 🎭 Any face, any language β€” upload a JPEG, pick from 23 languages, start talking
  • ⚑ Token-streaming pipeline β€” the LLM streams live tokens while TTS + lip-sync run per sentence; the first video chunk plays before the model finishes its reply
  • βœ‹ Barge-in β€” speak (or hit stop) mid-reply and the avatar yields instantly, like a real conversation
  • πŸ”’ 100% local mode β€” local storage, local Whisper, local LLM via Ollama: nothing leaves your machine
  • πŸ”Œ Multi-LLM β€” Claude (with prompt caching), GPT-4o, or any local model via Ollama / vLLM / LM Studio
  • πŸš€ AWS GPU deployment β€” one-command deploy to g5.xlarge for true real-time (~30 FPS)
  • πŸ—οΈ Production-grade β€” JWT + httpOnly-cookie auth, per-user rate limiting, Postgres + Alembic, S3/CloudFront, Prometheus, CI, a real test suite β€” the only project in this niche you can ship as a product, not just a demo

βš”οΈ How AvatarAI compares

AvatarAIDuix-AvatarLinly-TalkerAIAvatarKit
Real-time conversationβœ… WebSocket streaming❌ offline video genβœ… (Gradio / WebRTC spin-off)βœ…
Lip-sync videoβœ… MuseTalk V1.5βœ… proprietary modelsβœ… multiple engines❌ (drives external avatars)
Voice cloningβœ… 10 s, 23 languagesβœ…βœ…βŒ
Barge-in / interruptionβœ…βŒβœ… (stream variant)βœ…
Local / free LLMβœ… Ollama, vLLMβŒβœ…βœ…
Web app with auth & historyβœ… Next.js + JWT + Postgres❌ Windows client❌ Gradio demo UI❌ library
Rate limiting, CI, tests, IaCβœ…βŒβŒβŒ
LicenseMITcustomMITApache-2.0

Toolkits like Linly-Talker are great research playgrounds; Duix ships a Windows product. AvatarAI is the one you can deploy as a real multi-user web service.


✨ Features

CategoryDetails
πŸ€– LLM BackendsClaude (prompt-cached) Β· GPT-4o Β· Ollama / vLLM / LM Studio (local, free)
🎀 Voice CloningRecord 10–60 s β†’ Chatterbox Multilingual zero-shot cloning
πŸ—£οΈ Speech-to-TextWhisper (faster-whisper, CUDA), decodes browser WebM natively
🎬 Lip-Sync VideoMuseTalk V1.5 persistent worker (30 FPS on GPU) · optional GFPGAN restore · FFmpeg fallback (CPU)
⚑ Streaming PipelineLive LLM tokens + per-sentence video chunks over WebSocket
βœ‹ Barge-InSpeak or hit stop mid-reply β€” in-flight turn cancels in ms
πŸ”‰ TTS Fallback Chainchatterbox (optional, separate venv) β†’ edge-tts (free neural voices) β†’ gTTS β€” never silent
😊 Emotion DetectionLive emotion badges per message
🌍 23 LanguagesWhisper multilingual STT + Chatterbox multilingual TTS
🏠 Local-First StorageUSE_LOCAL_STORAGE=true β€” no AWS needed for dev
πŸ” Auth & SessionsJWT authentication, conversation history, persistent sessions
πŸ“Š ObservabilityPrometheus Β· Celery Flower Β· Sentry Β· structured logging
πŸ§ͺ TestedFull pytest suite β€” users, avatars, sessions, health checks
πŸš€ AWS GPU DeployOne-command g5.xlarge deploy with CUDA 11.8 + float16

πŸ—οΈ Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                        Browser / Client                       β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚  β”‚Avatar Studioβ”‚  β”‚ Voice Studio β”‚  β”‚   Chat Interface     β”‚ β”‚
β”‚  β”‚  (upload)   β”‚  β”‚  (cloning)   β”‚  β”‚ Idle anim + chunks   β”‚ β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
          β”‚ REST           β”‚ REST                β”‚ WebSocket
          β–Ό                β–Ό                     β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                       FastAPI Backend                         β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”‚
β”‚  β”‚                  WebSocket Manager                    β”‚    β”‚
β”‚  β”‚  split sentences β†’ TTS β†’ MuseTalk β†’ stream chunks    β”‚    β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚  Whisper β”‚ β”‚Claude/GPT β”‚ β”‚ XTTS v2  β”‚ β”‚  MuseTalk     β”‚  β”‚
β”‚  β”‚   STT    β”‚ β”‚  / Llama  β”‚ β”‚   TTS    β”‚ β”‚  (GPU/CPU)    β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚PostgreSQLβ”‚ β”‚  Redis   β”‚  β”‚  Celery  β”‚ β”‚ Local FS / S3 β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Real-Time Data Flow (one conversation turn)

[User types / speaks]
        β”‚
        β–Ό
  Whisper STT ─────────────────► transcript
        β”‚
        β–Ό
  Claude / GPT / Llama ────────► full response text
        β”‚
        β–Ό
  Split into sentences ────────► ["Hello!", "How are you?", ...]
        β”‚
        β”œβ”€β”€ sentence 1 β†’ XTTS β†’ MuseTalk β†’ video_chunk WS β†’ browser plays
        β”œβ”€β”€ sentence 2 β†’ XTTS β†’ MuseTalk β†’ video_chunk WS β†’ queued
        └── sentence N β†’ XTTS β†’ MuseTalk β†’ video_chunk WS β†’ queued

πŸ“ Project Structure

ai-avatar-system/
β”œβ”€β”€ backend/                    # FastAPI application
β”‚   β”œβ”€β”€ app/
β”‚   β”‚   β”œβ”€β”€ api/v1/             # REST endpoints (users, avatars, sessions, messages)
β”‚   β”‚   β”œβ”€β”€ services/           # Core services (LLM, TTS, STT, animator, storage)
β”‚   β”‚   β”œβ”€β”€ models/             # SQLAlchemy DB models
β”‚   β”‚   └── websocket.py        # Real-time WebSocket handler + sentence streaming
β”‚   β”œβ”€β”€ alembic/                # Database migrations
β”‚   β”œβ”€β”€ models/MuseTalk/        # MuseTalk V1.5 (lip-sync engine)
β”‚   β”‚   └── scripts/
β”‚   β”‚       └── musetalk_worker.py  # Persistent worker (models loaded once)
β”‚   β”œβ”€β”€ tests/                  # pytest suite
β”‚   β”œβ”€β”€ Dockerfile              # CUDA 11.8 base image
β”‚   └── requirements.txt
β”œβ”€β”€ frontend/                   # Next.js 16 application
β”‚   β”œβ”€β”€ app/                    # App Router pages
β”‚   β”œβ”€β”€ components/             # React components (ChatInterface, IdleAvatar, etc.)
β”‚   β”œβ”€β”€ lib/api.ts              # Axios API client
β”‚   └── store/                  # Zustand global state
β”œβ”€β”€ nginx/
β”‚   └── nginx.conf              # Reverse proxy (HTTP β†’ backend/frontend, WebSocket)
β”œβ”€β”€ infrastructure/
β”‚   β”œβ”€β”€ main.tf                 # AWS Terraform (ECS, RDS, ElastiCache, S3, CloudFront)
β”‚   └── variables.tf
β”œβ”€β”€ scripts/
β”‚   β”œβ”€β”€ setup_musetalk.sh       # Download MuseTalk models (~9 GB)
β”‚   └── deploy-aws.sh           # One-command EC2 GPU deployment
β”œβ”€β”€ docker-compose.yml          # Development (CPU) β€” all services
β”œβ”€β”€ docker-compose.prod.yml     # Production overrides (GPU, no bind mounts, logging)
β”œβ”€β”€ deploy.sh                   # ECR push + Terraform deploy (ECS path)
β”œβ”€β”€ .env.example                # Development env template
└── .env.prod.example           # Production env template

πŸš€ Quick Start

Prerequisites

  • Docker & Docker Compose v2+ (recommended)
  • OR: Python 3.10+, Node.js 18+, FFmpeg, PostgreSQL, Redis

Option A β€” Docker / CPU (development)

git clone https://github.com/PunithVT/ai-avatar-system.git
cd ai-avatar-system
cp .env.example .env          # add your ANTHROPIC_API_KEY (or OPENAI_API_KEY)
docker compose up -d
ServiceURL
πŸ–₯️ Frontendhttp://localhost:3000
βš™οΈ Backend APIhttp://localhost:8000
πŸ“– Swagger Docshttp://localhost:8000/docs
🌸 Celery Flowerhttp://localhost:5555

No AWS required. Set USE_LOCAL_STORAGE=true (default) β€” uploads saved to backend/uploads/.

Want something to talk to immediately? Seed three ready-made demo avatars (AI-generated faces + personalities):

backend/venv/bin/python scripts/seed_demo.py            # or any python with `requests`
backend/venv/bin/python scripts/seed_demo.py --with-voices   # + cloned demo voices

Prebuilt images are also published on every release β€” ghcr.io/punithvt/ai-avatar-system-backend and …-frontend.

Option B β€” Manual (development)

# Backend
cd backend
python -m venv venv && source venv/bin/activate
pip install -r requirements.txt
cp ../.env.example ../.env
alembic upgrade head
uvicorn main:app --reload --port 8000

# Frontend (new terminal)
cd frontend
npm install
npm run dev

Option C β€” Enable MuseTalk Lip-Sync

# Download models (~9 GB, one-time)
bash scripts/setup_musetalk.sh

# Set in .env
AVATAR_ENGINE=musetalk

# Restart
docker compose restart backend

πŸš€ GPU & AWS Deployment

MuseTalk achieves 30 FPS at 256Γ—256 on a V100-class GPU (source: MuseTalk paper). On CPU it is 30–50Γ— slower. Deploying on AWS gets you genuine real-time performance.

InstanceGPUVRAMSpot $/hrMuseTalk FPS
g4dn.xlargeT416 GB~$0.16~15–20 FPS
g5.xlargeA10G24 GB~$0.30~30 FPS βœ“
g6.xlargeL424 GB~$0.24~30 FPS βœ“

Recommended: g5.xlarge Spot (~$72/mo at 8 hrs/day).

One-Command EC2 Deploy

# 1. Launch g5.xlarge with Ubuntu 22.04 LTS, SSH in, then:
bash <(curl -fsSL https://raw.githubusercontent.com/PunithVT/ai-avatar-system/main/scripts/deploy-aws.sh)

# 2. Fill in API keys:
nano /opt/ai-avatar-system/.env.prod

# 3. Redeploy with your keys:
bash /opt/ai-avatar-system/scripts/deploy-aws.sh --update

The script automatically:

  • Installs Docker + nvidia-docker2
  • Verifies GPU is accessible
  • Downloads MuseTalk models (~9 GB)
  • Starts all services with GPU passthrough + float16 (2Γ— faster via Tensor Cores)

Manual Production Docker

cp .env.prod.example .env.prod   # fill in your values
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

What docker-compose.prod.yml adds over development:

  • GPU reservation (nvidia driver, count=1) for backend + celery-worker
  • float16 inference enabled automatically on CUDA β†’ ~2Γ— speedup
  • Persistent musetalk_models volume (survive container restarts)
  • No source-code bind mounts (runs from built image)
  • Log rotation (100 MB max, 5 files)
  • Flower disabled (security)

Verify GPU is Working

# Check GPU is visible in container
docker exec avatar-backend python -c "
import torch
print('CUDA:', torch.cuda.is_available())
print('GPU:', torch.cuda.get_device_name(0))
print('VRAM:', round(torch.cuda.get_device_properties(0).total_memory/1024**3,1), 'GB')
"

# Expected on g5.xlarge:
# CUDA: True
# GPU: NVIDIA A10G
# VRAM: 24.0 GB

# Live GPU utilisation
docker exec avatar-backend nvidia-smi

AWS Terraform (ECS Path)

For a fully managed ECS deployment with RDS + ElastiCache + CloudFront:

cd infrastructure
terraform init
terraform apply -var="environment=production"
bash deploy.sh production

πŸŽ›οΈ Avatar Engines

AVATAR_ENGINE selects how video is generated. MuseTalk is the default and stays installed whatever else you add; any engine failure falls back to simple rather than failing the turn.

EngineVRAMSpeedUse it for
simplenoneinstantCPU hosts, no lip-sync
musetalk16–24 GB~30 FPSdefault β€” live conversation
liveavatar48 GB (FP8) / 80 GBminutes per turnoffline renders where fidelity beats latency

LiveAvatar (optional)

Alibaba LiveAvatar (Wan2.2-S2V-14B

  • LoRA, Apache 2.0) produces higher fidelity than MuseTalk and supports 10,000+ second continuous generation from a single reference image.
bash scripts/setup_liveavatar.sh    # checks VRAM first, then ~60 GB of weights
# then in .env:
AVATAR_ENGINE=liveavatar

It is not a MuseTalk replacement, and MuseTalk remains the default. Two reasons, both structural:

  • 48 GB VRAM minimum with FP8, 80 GB without. That is a different hardware tier from the g5.xlarge this project targets.
  • It loads the 14B model on every invocation. Upstream's entry point (minimal_inference/s2v_streaming_interact.py) runs once and exits β€” there is no persistent-worker mode, which is exactly what MuseTalk uses to keep per-turn cost to inference alone. So a turn costs minutes, not milliseconds.

That makes LiveAvatar a fit for the offline Celery render path, where a slow better-looking result is the point, and a poor fit for live conversation. Switching back is one setting: AVATAR_ENGINE=musetalk.

Giving LiveAvatar a persistent worker would mean patching upstream. That is the maintenance debt that got SadTalker removed from this project, so it has deliberately not been done.


πŸ” Face Restoration (optional)

MuseTalk regenerates the mouth region at 256Γ—256 and the worker scales it back up to the crop size, while the avatar itself is stored at AVATAR_RESOLUTION (512). The mouth therefore renders at roughly half the resolution of the face around it β€” and that gap, not the model, is the pipeline's visible quality ceiling. Swapping lip-sync engines doesn't close it; a face restorer does.

bash scripts/setup_face_restore.sh   # installs gfpgan + ~520 MB of weights
# then in .env:
FACE_RESTORE=gfpgan                  # off | gfpgan

gfpgan is deliberately not in requirements.txt. It depends on basicsr, which ships no wheel and builds from source, and whose setup.py collides with the CUDA base image's preinstalled cuda-toolkit β€” listing it there failed the backend image build for everyone, including people who never enable restoration. It lives in backend/requirements-face-restore.txt and is installed by the setup script, the same way MuseTalk and Chatterbox are.

For the same reason, restoration is a bare-metal / GPU-host feature today: inside the CUDA container the basicsr build needs work that hasn't been done yet.

GFPGAN runs over each composited frame after MuseTalk has blended its generated mouth back in, so it sees a complete face to detect.

It is off by default, deliberately. Restoration runs per frame, so it trades FPS for sharpness, and whether that trade is worth it depends on your GPU and how close to real-time you need to stay. A/B it against FACE_RESTORE=off on the same avatar and audio before leaving it on.

Everything about it degrades rather than breaks: missing weights, a broken gfpgan install, CPU-only hardware, or a single frame that fails to restore all fall back to plain MuseTalk output. Lip-sync is the feature; sharpness is the nicety.


🧠 Conversation Memory

The LLM context is a fixed window of the most recent turns (MAX_CONTEXT_MESSAGES, 60). Anything older used to be dropped outright, so a long conversation quietly forgot its own beginning β€” the avatar would lose your name twenty minutes in.

Turns leaving the window are now folded into a running summary carried in the system prompt, and persisted to Conversation.summary so a browser refresh doesn't lose it.

CONVERSATION_MEMORY=true          # default
MEMORY_SUMMARY_MAX_CHARS=1500

A summary rather than embeddings, deliberately. It costs one LLM call when the window overflows β€” roughly once every 30 turns, not once per reply β€” needs no vector store or embedding model, and works with every provider the project supports. Requiring an embeddings API would have broken the "runs fully local and free with Ollama" path, since Anthropic has no embeddings endpoint and a local embedding model is another dependency to install and keep working.

The tradeoff is honest: a summary is lossy. It carries the thread of a conversation, not verbatim recall of a detail from an hour ago. Fact-level retrieval is what RAG would add, and that remains open on the roadmap.

Failure never costs a turn β€” if summarisation fails, the window truncates exactly as it did before and the reply proceeds.


πŸ™Œ Hands-Free Mode

Tap the Radio button next to the mic and just talk β€” no tap-to-record. The avatar detects when you start and stop speaking, and speaking over a reply interrupts it.

How turns are decided (frontend/lib/vad.ts):

  • Adaptive threshold. The detector measures the room's ambient level for ~700 ms, then listens for speech a margin above it, and keeps tracking the floor while nobody is talking. A fixed threshold works at a desk and fails in a cafe.
  • Pause tolerance. A turn ends after ~900 ms of continuous silence, so "I think… maybe we should" stays one turn instead of three.
  • Noise rejection. Anything shorter than ~300 ms is discarded rather than sent, so a cough or a door doesn't cost an STT call and an LLM turn.
  • Echo cancellation is requested on the mic. Without it the open mic hears the avatar through the speakers and the session talks to itself.

The detector is pure β€” it takes (level, timestamp) samples and returns turn boundaries β€” so its timing logic can be exercised against a synthetic trace without a browser or a microphone.


🎀 Voice Cloning

Powered by Chatterbox Multilingual (Resemble AI) β€” zero-shot voice cloning from a 10-second sample, in 23 languages.

  1. Go to Voice tab β†’ Clone Voice
  2. Record 10–60 s of clear speech (or upload a WAV/MP3/WebM)
  3. Name it β†’ Clone β†’ select it for your session

Every TTS response then uses your cloned voice.

Chatterbox needs its own environment. It is not in requirements.txt, because it pins torch==2.6.0 / transformers==5.2.0 / librosa==0.11.0 while the MuseTalk lip-sync pipeline needs torch 2.2 / transformers 4.37 β€” the two cannot share one virtualenv. Install it separately:

python -m venv venv-tts
./venv-tts/bin/pip install -r backend/requirements-tts.txt

Without it nothing breaks: TTS falls through to edge-tts (free Microsoft neural voices, CPU-only) and then gTTS. Voice cloning is the only feature that requires Chatterbox, so set TTS_PROVIDER=chatterbox only once it is actually installed.

# REST API
curl -X POST http://localhost:8000/api/v1/voices/clone \
  -F "audio=@my_voice.wav" -F "name=My Voice" -F "language=en"

πŸ“‘ API Reference

Authentication

POST /api/v1/users/register   { "email": "...", "username": "...", "password": "..." }
POST /api/v1/users/login      form: username=... password=...   β†’ { "access_token": "..." }
POST /api/v1/users/guest      (no body)                         β†’ { "access_token": "..." }
POST /api/v1/users/logout     clears the httpOnly auth cookie

# Every resource route requires an identity β€” pass the bearer token:
Authorization: Bearer <access_token>
# ...or rely on the httpOnly cookie that /login and /guest set for browsers.

/guest backs the UI's Continue as Guest button. It creates a real but anonymous account, so a guest's avatars and transcripts are scoped to them alone β€” guests never share data with each other. Guest accounts have no usable password (they are reachable only via the token above) and are deleted after GUEST_RETENTION_HOURS of inactivity by the cleanup_guest_accounts task. Set GUEST_ACCOUNTS_ENABLED=false to require sign-up.

Registration rules: passwords are 8–128 characters, usernames are 3–39 characters of [A-Za-z0-9_-] (must start and end alphanumeric), and email addresses are normalised to lower case so User@x.com and user@x.com are the same account.

Avatars

POST   /api/v1/avatars/upload        Upload photo (multipart: file + name)
                                     limits: MAX_UPLOAD_SIZE (413 if over),
                                     ALLOWED_EXTENSIONS, name ≀ 200 chars
GET    /api/v1/avatars/              List avatars
DELETE /api/v1/avatars/{id}          Delete avatar
PUT    /api/v1/avatars/{id}/voice    Assign voice to avatar

Sessions & Messages

POST   /api/v1/sessions/create       { "avatar_id": "..." }
POST   /api/v1/sessions/{id}/end
GET    /api/v1/messages/session/{id}

WebSocket

WS  /ws/session/{session_id}

Client β†’ Server:

{ "type": "text",         "text": "Hello!" }
{ "type": "audio",        "audio": "<base64-webm>" }
{ "type": "stop" }                                  // barge-in: cancel the in-flight reply
{ "type": "set_voice",    "voice_id": "<uuid>" }    // attach a cloned voice (owner-checked)
{ "type": "set_language", "language": "es" }
{ "type": "ping" }

Server β†’ Client:

{ "type": "token",            "token": "Hel" }       // live LLM stream
{ "type": "transcription",    "text": "Hello!" }
{ "type": "message",          "content": "Hi!", "role": "assistant" }
{ "type": "video_chunk_start","total_chunks": -1 }   // -1 = streaming, total unknown
{ "type": "video_chunk",      "chunk_index": 0, "video_url": "...", "text": "Hi!" }
{ "type": "video_chunk_end",  "sent_chunks": 3 }
{ "type": "status",           "message": "Animating…", "stage": "animation" }
{ "type": "tts_fallback",     "engine": "edge-tts", "voice_cloned": false, "message": "…" }
{ "type": "interrupted",      "message": "Previous response interrupted" }
{ "type": "error",            "message": "Something went wrong" }

βš™οΈ Configuration

Key .env variables:

# LLM
LLM_PROVIDER=anthropic            # anthropic | openai | ollama (local & free)
LLM_MODEL=claude-sonnet-4-6       # or gpt-4o Β· llama3.1 Β· qwen2.5 …
ANTHROPIC_API_KEY=sk-ant-...
OPENAI_BASE_URL=                  # e.g. http://localhost:11434/v1 for Ollama / vLLM / LM Studio

# Avatar engine
AVATAR_ENGINE=musetalk            # musetalk (default) | liveavatar (48GB+) | simple (CPU)
MUSETALK_PATH=models/MuseTalk

# TTS β€” automatic fallback chain: chatterbox β†’ edge-tts β†’ gtts
# chatterbox requires backend/requirements-tts.txt in its own venv (see
# "Voice Cloning"); without it this transparently uses edge-tts.
TTS_PROVIDER=chatterbox

# STT
WHISPER_MODEL=large-v3-turbo      # tiny | base | small | medium | large-v3 | large-v3-turbo

# Storage
USE_LOCAL_STORAGE=true            # false β†’ AWS S3 (+ presigned URLs / CloudFront)
S3_BUCKET_NAME=...

# Auth (β‰₯32 chars enforced at boot)
SECRET_KEY=$(python -c "import secrets; print(secrets.token_hex(32))")
JWT_SECRET_KEY=$(python -c "import secrets; print(secrets.token_hex(32))")
JWT_EXPIRATION_HOURS=24

πŸ› οΈ Tech Stack

Frontend

LibraryPurpose
Next.js 16 + React 18App framework
TypeScript 5Type safety
Tailwind CSSStyling
ZustandGlobal state

Backend

LibraryPurpose
FastAPIAsync REST API + WebSocket
SQLAlchemy 2 (async)ORM with asyncpg
PostgreSQL 15Primary database
AlembicMigrations
Redis 7Cache + Celery broker
CeleryBackground tasks

AI / ML

ModelPurpose
Claude / GPT-4o / Ollama (local)LLM conversation
Whisper (faster-whisper)Speech-to-text
Chatterbox Multilingual (Resemble AI)TTS + zero-shot voice cloning, 23 languages
Edge TTS β†’ gTTSFree no-GPU fallback voices
MuseTalk V1.5Photorealistic lip-sync (30 FPS on GPU)

πŸ§ͺ Running Tests

cd backend
pytest -v                           # all tests
pytest tests/test_health.py         # single module
pytest --cov=app --cov-report=html  # HTML coverage

πŸ“° What's New

  • 2026-06 β€” Edge-TTS neural fallback chain Β· local LLMs via Ollama/vLLM Β· demo avatar seeding Β· prebuilt GHCR images Β· cascade-delete + WebM-STT + 429 fixes Β· SEO/metadata pass
  • 2026-05 β€” httpOnly-cookie auth (XSS-safe) Β· conversation resume from history Β· end-to-end WebSocket tests Β· perf indexes (migration 0002)
  • 2026-03 β€” Chatterbox Multilingual replaces XTTS v2 (23 languages) Β· MuseTalk persistent worker (models load once) Β· barge-in interruption Β· live token streaming

πŸ—ΊοΈ Roadmap

  • Streaming LLM β€” TTS + lip-sync start before the LLM finishes (token-by-token) βœ…
  • Barge-in β€” interrupt the avatar mid-reply by speaking βœ…
  • Local LLMs β€” Ollama / vLLM / LM Studio via OpenAI-compatible API βœ…
  • Hands-free mode β€” VAD-driven always-listening with auto end-of-turn (no tap-to-record) βœ…
  • WebRTC streaming β€” sub-second full-duplex audio/video instead of chunked MP4
  • Wav2Lip engine β€” lighter lip-sync option for weaker GPUs
  • Emotion-driven animation β€” detected emotion changes facial expression
  • Embeddable widget β€” drop a talking avatar into any website with 3 lines of JS
  • Long-term memory β€” rolling summary carries context past the window βœ… (RAG + vector DB still open for fact-level recall)
  • UI i18n β€” the pipeline speaks 23 languages; the UI should too

❓ FAQ

Do I need a GPU?

No β€” everything runs on CPU. MuseTalk takes 30–90 s/sentence on CPU (the simple engine is instant). For real-time lip-sync, use an AWS g5.xlarge (~$0.30/hr spot) or any 16 GB+ NVIDIA card.

Can I run it with no API key, fully offline?

Yes β€” set LLM_PROVIDER=ollama, run Ollama (ollama run llama3.1), and you have a fully local, free conversation stack: Whisper STT, local LLM, Chatterbox TTS, MuseTalk video.

How do I get something to talk to quickly?

Run python scripts/seed_demo.py β€” it creates three demo avatars (AI-generated faces, distinct personalities) and optionally cloned demo voices with --with-voices.

How do I get MuseTalk models?

Run bash scripts/setup_musetalk.sh β€” downloads ~9 GB of models automatically.

Why does the first response take longer?

The MuseTalk persistent worker loads all models into GPU VRAM on the first request (~60 s on GPU, ~5 min on CPU). Subsequent requests reuse the loaded models.

What happens if the TTS model can't load?

The pipeline degrades gracefully: chatterbox β†’ edge-tts (free Microsoft neural voices) β†’ gTTS. The UI shows a one-time notice when a cloned voice couldn't be applied.

What avatar photo works best?

A clear, well-lit frontal face photo (JPEG/PNG/WebP). Avoid sunglasses or heavy occlusion.


🀝 Contributing

Contributions welcome! Read CONTRIBUTING.md before opening a PR.

git clone https://github.com/PunithVT/ai-avatar-system.git
git checkout -b feat/my-feature
# make changes + tests
git commit -m "feat(backend): add my feature"
git push origin feat/my-feature

πŸ’œ Sponsor

AvatarAI is MIT-licensed and developed in the open. If it saves you time, or your company is running it in production, sponsoring directly funds the work that is expensive to do for free:

  • GPU time for testing the MuseTalk lip-sync pipeline across resolutions and hardware
  • Model evaluation as new TTS and STT releases land, so the defaults stay current instead of drifting
  • Maintenance β€” dependency and security upkeep, issue triage, and review
Sponsor on GitHub

Not in a position to sponsor? Starring the repo, filing a good bug report, or writing about what you built with it all genuinely help.


πŸ“„ License

MIT Β© 2026 β€” see LICENSE for details.


If AvatarAI saves you time or inspires your project, please ⭐ star the repo.



Star History Chart



Built with FastAPI Β· Next.js Β· MuseTalk V1.5 Β· Chatterbox Β· Whisper Β· Claude AI

ai-avatar
avatar-ai
chatterbox-tts
claude-ai
digital-human
fastapi
generative-ai
lip-sync
llm
musetalk
nextjs
real-time
speech-to-text
talking-avatar
talking-head
text-to-speech
voice-agent
voice-cloning
websocket
whisper-ai

Significant stargazers

Diego Rodrigues de Sa e Souza

1,258 followers Β· starred Aug 2026

dsd

43 followers Β· starred Jun 2026

Languages

Python

54.1%

TypeScript

26.5%

Shell

6.3%

HTML

6.0%

HCL

4.6%

JavaScript

1.0%