Ruby (Sinatra) demo app for Deepgram Text-to-Speech.
- Backend: Ruby (Sinatra) (Ruby) on port 8081
- Frontend: Vite + vanilla JS on port 8080 (git submodule:
text-to-speech-html) - API type: REST —
POST /api/text-to-speech - Deepgram API: Text-to-Speech (
/v1/speak) - Auth: JWT session tokens via
/api/session(WebSocket auth usesaccess_token.<jwt>subprotocol)
| File | Purpose |
|---|---|
app.rb |
Main backend — API endpoints and request handlers |
deepgram.toml |
Metadata, lifecycle commands, tags |
Makefile |
Standardized build/run targets |
sample.env |
Environment variable template |
frontend/main.js |
Frontend logic — UI controls, API calls, result rendering |
frontend/index.html |
HTML structure and UI layout |
deploy/Dockerfile |
Production container (Caddy + backend) |
deploy/Caddyfile |
Reverse proxy, rate limiting, static serving |
# Initialize (clone submodules + install deps)
make init
# Set up environment
test -f .env || cp sample.env .env # then set DEEPGRAM_API_KEY
# Start both servers
make start
# Backend: http://localhost:8081
# Frontend: http://localhost:8080Start (recommended):
make startStart separately:
# Terminal 1 — Backend
bundle exec ruby app.rb
# Terminal 2 — Frontend
cd frontend && corepack pnpm run dev -- --port 8080 --no-openStop all:
lsof -ti:8080,8081 | xargs kill -9 2>/dev/nullClean rebuild:
rm -rf vendor frontend/node_modules frontend/.vite
make init- Backend:
Gemfile— Uses Bundler. Sinatra 4.0 needs therackupgem. - Frontend:
frontend/package.json— Vite dev server - Submodules:
frontend/(text-to-speech-html),contracts/(starter-contracts)
Install: bundle install
Frontend: cd frontend && corepack pnpm install
| Endpoint | Method | Auth | Purpose |
|---|---|---|---|
/api/session |
GET | None | Issue JWT session token |
/api/metadata |
GET | None | Return app metadata (useCase, framework, language) |
/api/text-to-speech |
POST | JWT | Converts text to speech audio using Deepgram's TTS API. |
Find the DEFAULT_MODEL or model variable in the backend. Deepgram offers many voice options:
Aura 2 voices (latest, highest quality):
aura-2-thalia-en(default)aura-2-andromeda-enaura-2-arcas-enaura-2-atlas-enaura-2-luna-enaura-2-orion-enaura-2-stella-enaura-2-zeus-en
Legacy Aura voices: aura-asteria-en, aura-luna-en, aura-stella-en, etc.
The TTS API supports different output formats via query parameters:
| Parameter | Default | Options | Effect |
|---|---|---|---|
model |
aura-2-thalia-en |
See voice list | Voice selection |
encoding |
(varies) | linear16, mp3, opus, flac, alaw, mulaw |
Audio encoding |
container |
(varies) | wav, mp3, ogg, none |
Container format |
sample_rate |
24000 |
8000-48000 |
Output sample rate |
bit_rate |
(varies) | 32000-320000 |
For lossy codecs |
Backend: Add these as query params to the Deepgram API call or SDK options.
Frontend: Add dropdowns for encoding/format in frontend/main.js.
- The frontend sends
{ text }in the request body - You could add SSML support by passing SSML-formatted text
- Add a character/word limit by validating in the backend
The frontend is a git submodule from deepgram-starters/text-to-speech-html. To modify:
- Edit files in
frontend/— this is the working copy - Test locally — changes reflect immediately via Vite HMR
- Commit in the submodule:
cd frontend && git add . && git commit -m "feat: description" - Push the frontend repo:
cd frontend && git push origin main - Update the submodule ref:
cd .. && git add frontend && git commit -m "chore(deps): update frontend submodule"
IMPORTANT: Always edit frontend/ inside THIS starter directory. The standalone text-to-speech-html/ directory at the monorepo root is a separate checkout.
- Add the HTML element in
frontend/index.html(input, checkbox, dropdown, etc.) - Read the value in
frontend/main.jswhen making the API call or opening the WebSocket - Pass it as a query parameter or request body field
- Handle it in the backend
app.rb— read the param and pass it to the Deepgram API
| Variable | Required | Default | Purpose |
|---|---|---|---|
DEEPGRAM_API_KEY |
Yes | — | Deepgram API key |
PORT |
No | 8081 |
Backend server port |
HOST |
No | 0.0.0.0 |
Backend bind address |
SESSION_SECRET |
No | — | JWT signing secret (production) |
All commits must follow conventional commits format. Never include Co-Authored-By lines for Claude.
feat(ruby-text-to-speech): add diarization support
fix(ruby-text-to-speech): resolve WebSocket close handling
refactor(ruby-text-to-speech): simplify session endpoint
chore(deps): update frontend submodule
# Run conformance tests (requires app to be running)
make test
# Manual endpoint check
curl -sf http://localhost:8081/api/metadata | python3 -m json.tool
curl -sf http://localhost:8081/api/session | python3 -m json.tool