Practice technical communication by recording video answers with live captions, getting AI-powered coaching feedback, and optionally uploading to YouTube.
- Record — Camera + mic recording with a 3-2-1 countdown, visible timer, and device selector
- Live Captions — Real-time transcription via Web Speech API (Chrome/Edge) with subtitle overlay
- Transcript Export — Editable transcript with WebVTT and SRT download
- AI Coaching — Scores for clarity, structure, tradeoffs, conciseness, and confidence; filler word count; follow-up cross-questions; rewritten answer in your tone
- YouTube Upload — OAuth 2.0 integration with resumable upload, privacy controls, and caption track upload
- Privacy-First — Video stays in your browser. Nothing is uploaded unless you choose to.
| Mode | Description |
|---|---|
| Interview | Technical interview answer practice |
| System Design | System design discussion walkthrough |
| Bug Explanation | Bug or incident explanation practice |
| Feature Walkthrough | Feature demo or walkthrough practice |
- Framework: Next.js 14+ (App Router, TypeScript)
- Styling: Tailwind CSS
- State: Zustand
- Storage: IndexedDB (browser), SQLite (server, for OAuth tokens)
- AI: OpenAI-compatible LLM abstraction (swappable providers)
- YouTube: Google APIs (googleapis)
- Testing: Jest + ts-jest
- Node.js 18+ and npm
- A modern browser (Chrome or Edge recommended for live captions)
# Clone the repo
git clone https://github.com/your-username/techspeak-studio.git
cd techspeak-studio
# Install dependencies
npm install
# Copy environment template
cp .env.example .env.local
# Create database directory
mkdir -p db
# Start development server
npm run devOpen http://localhost:3000.
The app works with LLM_PROVIDER=mock (the default in .env.example). This uses a built-in mock that returns plausible coaching feedback without calling any external API.
To enable real AI coaching, set LLM_PROVIDER=openai and provide your LLM_API_KEY.
| Variable | Required | Default | Description |
|---|---|---|---|
LLM_PROVIDER |
No | mock |
openai or mock |
LLM_BASE_URL |
No | https://api.openai.com/v1 |
OpenAI-compatible endpoint |
LLM_API_KEY |
For AI coaching | — | API key for LLM provider |
LLM_MODEL |
No | gpt-4o-mini |
Model name |
YOUTUBE_CLIENT_ID |
For YouTube | — | Google Cloud OAuth client ID |
YOUTUBE_CLIENT_SECRET |
For YouTube | — | Google Cloud OAuth client secret |
YOUTUBE_REDIRECT_URI |
No | http://localhost:3000/api/youtube/auth/callback |
OAuth redirect URI |
DB_PATH |
No | ./db/techspeak.db |
SQLite database path |
- Go to Google Cloud Console
- Create a new project (or select an existing one)
- Enable the YouTube Data API v3 in APIs & Services > Library
- Go to APIs & Services > Credentials
- Click Create Credentials > OAuth client ID
- Application type: Web application
- Add authorized redirect URI:
http://localhost:3000/api/youtube/auth/callback - Copy the Client ID and Client Secret to your
.env.local
https://www.googleapis.com/auth/youtube.upload— Upload videoshttps://www.googleapis.com/auth/youtube.force-ssl— Upload captions
During development, Google will show an "unverified app" warning when users authorize. This is normal for local development. To remove it for production:
- Go to APIs & Services > OAuth consent screen
- Submit your app for Google verification
- This requires a privacy policy URL and domain verification
The YouTube Data API has a daily quota of 10,000 units (default):
| Operation | Quota Cost |
|---|---|
videos.insert (upload) |
1,600 units |
captions.insert |
400 units |
videos.list |
1 unit |
With the default quota, you can upload approximately 6 videos per day. Request a quota increase in the Google Cloud Console if needed.
npm run dev # Start dev server
npm run build # Production build
npm run start # Start production server
npm test # Run tests
npm run lint # ESLintOr use Make:
make setup # First-time setup
make dev # Development
make test # Tests
make build # Production buildtechspeak-studio/
├── src/
│ ├── app/ # Next.js App Router
│ │ ├── page.tsx # Landing page
│ │ ├── practice/ # Record screen
│ │ ├── sessions/ # Session list + detail
│ │ └── api/ # API routes
│ │ ├── coach/ # POST /api/coach
│ │ └── youtube/ # YouTube auth, upload, captions
│ ├── components/ # React components
│ │ ├── Recorder.tsx # Main recording component
│ │ ├── CaptionOverlay.tsx # Live subtitle display
│ │ ├── TranscriptEditor.tsx
│ │ ├── CoachingReport.tsx
│ │ └── YouTubeUpload.tsx
│ ├── hooks/ # Custom React hooks
│ │ ├── useMediaRecorder.ts
│ │ ├── useSpeechRecognition.ts
│ │ └── useIndexedDB.ts
│ ├── lib/ # Core logic
│ │ ├── types.ts # TypeScript types
│ │ ├── subtitles.ts # VTT/SRT generation
│ │ ├── db.ts # SQLite for OAuth tokens
│ │ └── llm/ # LLM provider abstraction
│ │ ├── provider.ts # Interface + factory
│ │ ├── openai.ts # OpenAI-compatible implementation
│ │ ├── mock.ts # Offline mock
│ │ └── coaching-prompt.ts
│ └── store/ # Zustand state
├── __tests__/ # Jest tests
├── db/ # SQLite database (gitignored)
├── .env.example
├── README.md
├── SECURITY.md
├── PRIVACY.md
├── Dockerfile
└── Makefile
interface PracticeSession {
id: string;
mode: "interview" | "system-design" | "bug-explanation" | "feature-walkthrough";
createdAt: string;
promptContext: string;
transcript: string;
segments: CaptionSegment[];
vtt: string;
srt: string;
metrics: CoachingMetrics | null;
aiFeedback: CoachingFeedback | null;
videoBlobRef: string;
youtube: { videoId: string; privacyStatus: string; url: string } | null;
}| Method | Path | Description |
|---|---|---|
| POST | /api/coach |
Send transcript, get coaching feedback |
| POST | /api/youtube/auth/start |
Get YouTube OAuth URL |
| GET | /api/youtube/auth/callback |
OAuth callback (handles token exchange) |
| GET | /api/youtube/auth/start |
Check YouTube connection status |
| POST | /api/youtube/upload |
Upload video (multipart/form-data) |
| POST | /api/youtube/captions |
Upload captions track (VTT) |
- Camera + mic recording with MediaRecorder
- Live captions via Web Speech API
- WebVTT + SRT export
- Editable transcript
- AI coaching with mock + OpenAI providers
- YouTube upload with OAuth 2.0
- Keyboard shortcuts
- IndexedDB storage
- Privacy-first architecture
- Burn subtitles into video (ffmpeg.wasm)
- Whisper API fallback for better transcription
- Session comparison (before/after coaching)
- Export coaching report as PDF
- Dark/light theme toggle
- Multi-language support
- Team/org features (shared sessions)
- Custom coaching rubrics
- WebRTC-based mock interview with AI interviewer
- Mobile-responsive recording
- Fork the repository
- Create a feature branch:
git checkout -b feature/my-feature - Make your changes with tests
- Run
npm test && npm run lint - Submit a pull request
MIT
- Web Speech API for real-time transcription
- MediaRecorder API for in-browser recording
- YouTube Data API v3 for video uploads