Echo is a full-stack AI-powered text-to-speech platform with voice cloning. Type text, pick a curated system voice or a voice cloned from your own recording, and generate natural-sounding audio in seconds — with fine-grained control over temperature, pacing, and tone.
- Text-to-speech generation — up to 5,000 characters per generation, with adjustable temperature, top-p, top-k, and repetition penalty.
- Voice cloning — upload a short recording (10s–20MB) to create a custom voice tied to your organization.
- Voice library — curated system voices across 12 categories (Podcast, Audiobook, Conversational, Meditation, and more), plus per-org custom voices with search and filtering.
- Generation history — every generation is persisted per organization, with inline waveform playback.
- Multi-tenant — organization-scoped data isolation via Clerk; each org's voices and generations are fully separated.
- Daily generation limit — a per-user rolling 24h cap, enforced server-side, with a live "X left today" usage badge.
| Layer | Technology |
|---|---|
| Framework | Next.js 16 (App Router, React 19, TypeScript) |
| API | tRPC v11 + TanStack Query |
| Database | PostgreSQL via Prisma ORM |
| Auth | Clerk (multi-tenant / organizations) |
| Storage | Cloudflare R2 (S3-compatible) |
| AI inference | Chatterbox TTS on Modal (A10G GPU) |
| UI | Tailwind CSS v4, shadcn/ui, Radix UI, WaveSurfer.js |
| Monitoring | Sentry |
- AI inference service — a custom FastAPI wrapper around the open-source
chatterbox-ttsmodel, deployed as a Modal ASGI app with API-key auth and GPU (A10G) inference. Voice files are mounted directly from R2 into the container viaCloudBucketMountfor zero-copy audio prompt loading. - No server-side audio buffering for storage — audio never sits on the app server; generated audio is returned inline to the client immediately, while the upload to R2 (for history/persistence) runs in the background via
after()so the client doesn't wait on it. - Raw HTTP for uploads — voice creation uses a Route Handler rather than tRPC, to stream binary multipart uploads efficiently.
- Type-safe end to end — tRPC routers connect directly to Prisma, giving type safety from the database through to the client.
- Atomic rollback — if a DB write or R2 upload fails mid-creation, the partial record is cleaned up automatically.
- Node.js 20+
- A PostgreSQL database (e.g. Neon)
- A Cloudflare R2 bucket
- A Clerk application
- A deployed Chatterbox TTS inference endpoint (see
chatterbox_tts.py, deployed via Modal)
git clone https://github.com/chintondutta/echo.git
cd echo
npm install
cp .env.example .env # then fill in the values below
npx prisma migrate dev
npm run devThe app will be available at http://localhost:3000.
To seed the curated system voice library:
npx tsx scripts/seed-system-voices.ts| Variable | Description |
|---|---|
DATABASE_URL |
PostgreSQL connection string |
R2_ACCOUNT_ID / R2_ACCESS_KEY_ID / R2_SECRET_ACCESS_KEY / R2_BUCKET_NAME |
Cloudflare R2 credentials for audio storage |
CHATTERBOX_API_URL / CHATTERBOX_API_KEY |
Endpoint and API key for the deployed Chatterbox TTS inference service |
HF_ACCESS_TOKEN |
Hugging Face token used by the Chatterbox model |
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY / CLERK_SECRET_KEY |
Clerk authentication keys |
APP_URL |
Base URL of the app (e.g. http://localhost:3000) |
See .env.example for the full list.
src/
├── app/ # Next.js App Router pages and API routes
├── components/ # Shared UI components
├── features/ # Feature modules (dashboard, text-to-speech, voices)
├── trpc/ # tRPC routers and client setup
├── hooks/ # React hooks
└── lib/ # Shared utilities (db, R2, Chatterbox client, env)
prisma/ # Database schema and migrations
scripts/ # One-off setup scripts (voice seeding, API type sync)
chatterbox_tts.py # Modal-deployed TTS inference service
MIT