Stop chatting with a void — start collaborating with a team.
Ensemble is a full-stack AI collaboration platform featuring dual AI personalities with 3D animated avatars, real-time lip sync, voice cloning, web research tools, and persistent memory across sessions.
Meet Aimee (chaotic, creative, relentlessly fun) and Arthur (measured, precise, quietly sarcastic) — two AI personalities that share the same memory, can see each other's conversations, and have very strong opinions about each other.
"One Interface. Two Personalities. Zero Boredom." — Arthur (who wrote his own marketing copy)
Live demo: tinmanweb.vercel.app (URL pending Vercel project rename — limited, slow free API, no voice)
Full experience requires local setup — instant responses, voice cloning, lip sync, the works.
- Dual AI Personalities — Aimee & Arthur with distinct voices, avatars, and system prompts
- 3D Animated Avatars — react-three-fiber with idle animations and audio-driven lip sync
- Voice Cloning — ChatterboxTurbo for local cloned voices, ElevenLabs for cloud
- Voice Input — Push-to-talk via Web Speech API
- Web Tools — Search (DuckDuckGo), page reading (Jina Reader), YouTube transcripts, Reddit
- File Upload — Drag/drop, paste, or click to upload text files and images
- Image Vision — Drop an image and the AI analyzes it
- Persistent Memory — Three-tier system: topic index, session summaries, keyword recall
- Cross-Personality Awareness — Switch between Aimee and Arthur mid-conversation; they see each other's messages
- Markdown Rendering — Code blocks with copy buttons
- Audio Interruption — New message stops current speech
- Customizable Personalities — Edit descriptions via Settings page
- Node.js 20+ (nodejs.org)
- Ollama (ollama.com) with a chat model pulled
- PostgreSQL — Neon free tier (cloud) or local Postgres
- Git
- ChatterboxTurbo — local TTS with voice cloning
- ElevenLabs account — cloud TTS alternative (elevenlabs.io)
- Vercel account (vercel.com)
- OpenRouter API key (openrouter.ai) — free tier available
git clone https://github.com/BoldPhoenix/TinManWeb.git ensemble
cd ensemble
npm installCreate a free PostgreSQL database at neon.tech and get the connection string.
Or use a local PostgreSQL instance.
cp .env.example .envEdit .env:
# Required — PostgreSQL connection string
DATABASE_URL="postgresql://user:password@host/dbname?sslmode=require"
# Required — Ollama URL (default assumes localhost)
# If Ollama runs on a different machine, change the IP
OLLAMA_URL="http://localhost:11434"
# Optional — ElevenLabs for cloud TTS (has free tier)
# ELEVENLABS_API_KEY="your-key-here"
# Optional — OpenRouter for cloud LLM (for Vercel deployment)
# OPENROUTER_API_KEY="your-key-here"
# OPENROUTER_MODEL="nvidia/nemotron-3-super-120b-a12b:free"
# Optional — Gemini as backup cloud LLM
# GEMINI_API_KEY="your-key-here"npx prisma generate
npx prisma migrate dev --name initNote: If Prisma complains about missing
urlin the datasource, temporarily addurl = env("DATABASE_URL")to the datasource block inprisma/schema.prisma, run the migration, then remove it. This is a Prisma 7 quirk.
ollama pull gemma4:31b-cloud # Best quality (requires Ollama cloud)
# OR
ollama pull gemma3:4b # Runs locally on most GPUs
# OR
ollama pull llama3 # Good general-purpose optionThe model name is configured in app/api/chat/route.ts — change the model: value in handleOllama() to match whatever you pulled.
npm run devOpen http://localhost:3000 — you should see the Ensemble landing page.
Go to Chat and start talking to Aimee. Go to Settings to switch to Arthur.
Voice is optional but makes the experience dramatically better.
ChatterboxTurbo runs locally and clones voices from audio samples.
- Set up ChatterboxTurbo following its documentation
- Place voice samples (10-30 second
.mp3clips) in ChatterboxTurbo's voices directory:Aimee.mp3— female British voice sampleArthur.mp3— male British voice sample
- Start ChatterboxTurbo on port 8883
- Ensemble auto-detects it at
http://localhost:8883(or setTTS_URLin.env)
- Create an account at elevenlabs.io
- Get your API key from Profile → API Keys
- Add to
.env:ELEVENLABS_API_KEY="your-key-here"
- Voice IDs are configured per personality in
app/lib/personalities.ts
If no TTS provider is configured, the app uses the browser's built-in speech synthesis. Works but sounds robotic.
app/
page.tsx — Landing page
chat/page.tsx — Chat interface with 3D avatar
about/page.tsx — About page with tech stack and links
settings/page.tsx — Personality switcher and settings
api/
chat/route.ts — LLM proxy (Ollama / Gemini / OpenRouter)
tts/route.ts — TTS proxy (ChatterboxTurbo / ElevenLabs)
tools/route.ts — Web search, fetch, YouTube, Reddit
sessions/route.ts — Session CRUD
sessions/[id]/messages/route.ts — Message CRUD
memory/route.ts — Topic extraction and recall
components/
Avatar.tsx — 3D model with lip sync and backface fix
ChatPanel.tsx — Chat logic, streaming, TTS, tool calls
ChatInput.tsx — Text input, file upload, voice button
MessageBubble.tsx — Markdown rendering with code copy
VoiceButton.tsx — Push-to-talk speech recognition
Header.tsx — Navigation bar
lib/
db.ts — Prisma client singleton
audioState.ts — Shared audio state for lip sync
memory.ts — Topic extraction, recall, session summaries
personalities.ts — Personality definitions and system prompts
generated/prisma/ — Generated Prisma client (gitignored)
public/
Aimee.glb — Aimee's 3D model
Arthur.glb — Arthur's 3D model
prisma/
schema.prisma — Database schema
prisma.config.ts — Prisma 7 config
| Layer | Technology | Purpose |
|---|---|---|
| Framework | Next.js 16 / React 19 | Full-stack web framework |
| Language | TypeScript | Type safety |
| Styling | Tailwind CSS | Utility-first CSS |
| 3D | react-three-fiber / Three.js | Animated avatars with lip sync |
| LLM | Ollama / OpenRouter / Gemini | AI chat with streaming |
| TTS | ChatterboxTurbo / ElevenLabs / Web Speech API | Voice output with cloning |
| STT | Web Speech API | Voice input |
| Database | PostgreSQL / Prisma 7 | Chat and memory persistence |
| Web Tools | Jina Reader / DuckDuckGo | Search and page reading |
| Deployment | Vercel | Cloud hosting (optional) |
- Push to GitHub
- Import project in vercel.com
- Add environment variables:
DATABASE_URL— Neon Postgres connection string (no quotes!)OPENROUTER_API_KEY— for cloud LLMELEVENLABS_API_KEY— for cloud TTS (optional)
- Deploy — the build script runs
prisma generateautomatically
Edit app/lib/personalities.ts:
newcharacter: {
id: "newcharacter",
name: "Display Name",
model: "/NewCharacter.glb",
voiceLocal: "NewCharacter.mp3",
voiceCloud: "elevenlabs-voice-id",
basePrompt: "You are NewCharacter. Never break character.",
defaultDescription: "Description of personality and speaking style.",
},Drop the GLB model in public/ and the voice sample in ChatterboxTurbo's voices directory.
Built by Carl Roach — Senior Software Engineer | AI & Automation
- US Patent 11509678B2 — Automated Security Assessment Systems
- Landing page copy written by Arthur (he insisted on credit)
MIT