A Next.js prototype for practicing data structures and algorithms interviews with a voice-first AI interviewer. The current experience focuses on LeetCode Two Sum: it opens the problem in an embedded practice workspace, shows a draggable picture-in-picture interviewer overlay, listens through the browser microphone, responds with OpenAI Realtime audio, and generates an end-of-session debrief from the captured transcript.
- Voice interview flow: Start a live interview, speak your approach out loud, and hear short follow-up questions from Alex, the AI interviewer.
- OpenAI Realtime integration: The browser creates a WebRTC connection using a short-lived client secret minted by the app's server route.
- Draggable PiP overlay: The interviewer card floats above the practice workspace with mic state, elapsed time, hints, transcript preview, and end controls.
- Two Sum practice workspace: The app embeds the Two Sum problem page so the candidate can practice in a realistic environment.
- Hint requests: Ask for a small nudge without revealing the full solution.
- Session debrief: End the interview to see a verdict, score breakdown, elapsed time, hints used, transcript, strengths, and areas to improve.
- Marketing demo: The landing page includes a product demo area and feature cards for the intended user experience.
- Next.js 16 App Router
- React 19
- TypeScript
- Tailwind CSS 4
- Material UI Icons
- OpenAI Realtime API and Responses API
app/
api/
interview/route.ts # Transcript-based OpenAI Responses helper endpoint
realtime/session/route.ts # Server route that mints Realtime client secrets
globals.css # Tailwind import, theme variables, animation keyframes
layout.tsx # Root layout and metadata
page.tsx # Landing page, practice workspace, Realtime session logic
components/
ui/
avatar.tsx
button.tsx
card.tsx
pip-overlay/overlay.tsx # Floating interviewer overlay component
lib/
utils.ts # Shared class name helper
- Node.js 20 or newer
- pnpm
- An OpenAI API key with access to the Realtime API
- A modern browser with microphone and WebRTC support
The browser will ask for microphone permission when the interview starts. Realtime audio usually requires localhost or HTTPS, so local development should work from http://localhost:3000.
Create a local .env file in the project root:
OPENAI_API_KEY=sk-your-openai-api-key
GOOGLE_CLIENT_ID=your-google-oauth-client-id
GOOGLE_CLIENT_SECRET=your-google-oauth-client-secret
AUTH_SECRET=generate-a-long-random-string
DATABASE_URL=postgresql://postgres:[password]@[host]:5432/postgresOptional model and voice overrides:
OPENAI_REALTIME_MODEL=gpt-realtime-2
OPENAI_REALTIME_TRANSCRIPTION_MODEL=gpt-4o-mini-transcribe
OPENAI_REALTIME_VOICE=marin
OPENAI_MODEL=gpt-4.1-miniVariable reference:
OPENAI_API_KEYis required by both API routes. The server normalizes keys with or without aBearerprefix.OPENAI_REALTIME_MODELcontrols the voice interview model used byapp/api/realtime/session/route.ts.OPENAI_REALTIME_TRANSCRIPTION_MODELcontrols input audio transcription for the Realtime session.OPENAI_REALTIME_VOICEcontrols the generated interviewer voice.OPENAI_MODELcontrols the text-only Responses endpoint inapp/api/interview/route.ts.GOOGLE_CLIENT_IDandGOOGLE_CLIENT_SECRETare required for Google OAuth on/auth.AUTH_SECRETsigns the local session cookie. Generate a long random value and keep it private.DATABASE_URLpoints Drizzle to your Supabase Postgres database. Use the Supabase pooled connection string for deployed apps.
For local Google OAuth, add this authorized redirect URI in Google Cloud Console:
http://localhost:3000/api/auth/google/callback
This app uses Drizzle ORM with Supabase Postgres for signed-in user progress.
After setting DATABASE_URL, create/update the database schema:
pnpm db:pushThe initial SQL migration is also available at drizzle/0000_initial_users_completed_problems.sql if you prefer to run it in the Supabase SQL editor.
Do not commit .env files. They are ignored by .gitignore.
Install dependencies:
pnpm installStart the development server:
pnpm devOpen http://localhost:3000. Click Try Two Sum now or Start interview, allow microphone access, and wait for Alex to greet you.
pnpm devRuns the app locally with Next.js.
pnpm buildCreates a production build.
pnpm startStarts the production server after a successful build.
pnpm lintRuns ESLint.
- The user starts the Two Sum practice session from the landing page.
PracticeWorkspacerenders the embedded LeetCode page and the draggable overlay.- Starting the interview asks the browser for microphone access.
- The client calls
POST /api/realtime/session. - The server uses
OPENAI_API_KEYto create a short-lived Realtime client secret. - The browser creates a WebRTC offer and sends it to OpenAI's Realtime calls endpoint with that client secret.
- Realtime events stream back over a data channel and update transcript text, interviewer status, audio state, and spoken responses.
- When the interview ends, the app builds a local summary from the captured transcript, hints used, elapsed time, and Two Sum answer signals.
Creates an OpenAI Realtime client secret for the browser. This route keeps the long-lived OpenAI API key on the server and configures the interviewer instructions, voice, transcription model, and server-side voice activity detection.
Accepts a JSON body with a transcript field and returns a short text response from the configured OpenAI Responses model.
Example:
curl -X POST http://localhost:3000/api/interview \
-H "Content-Type: application/json" \
-d '{"transcript":"I would use a hash map to store values and look up the complement."}'- The live practice flow is hard-coded to LeetCode Two Sum.
- Screen understanding is represented in the product UI, but the current implementation does not inspect the candidate's code or browser screen.
- The final debrief is generated locally from transcript heuristics rather than a full evaluator model.
- The embedded LeetCode page depends on LeetCode allowing the page to load in an iframe.
- The landing page references
/two-sum-demo.mov; add that file topublic/if you want the demo video to render. - There is no persistence; refreshing the page clears the current session.
This app can be deployed anywhere that supports Next.js server routes. Set OPENAI_API_KEY and any optional model overrides in the hosting provider's environment settings. Use HTTPS in production so browser microphone and WebRTC APIs work reliably.