What is Bubbles
Bubbles started from a simple observation: practicing coding interviews on LeetCode is nothing like an actual interview. On LeetCode you sit quietly and type code. In a real interview, you are suddenly expected to explain your thinking, justify your decisions, handle interruptions and change solutions.
I wanted something that could help me get better in interviews and I had been meaning to play around with Gemini's voice models. So I built Bubbles, an AI interviewer - minus the human picture. I wanted to skip the uncanny valley.
You pick a problem, and Bubbles plays the role of the interviewer. It asks the question, listens while you talk through your approach, and occasionally jumps in with follow-up questions or clarifications, just like a real interviewer would. You still write code, but the focus is on explaining your thinking rather than silently grinding through the solution.
At the end of the session, Bubbles generates a report analysing how the interview went - things like clarity of explanation, algorithmic reasoning, hesitation, and how well you recovered when your first approach didn't quite work out.
The art of handling voice
I think we take for granted how elegant a conversation is. Content aside, there is a lot that goes into making a conversation sound like a conversation and I discovered this while trying to recreate it. Some of the main points:
- Playing audio without glitches: The model sends speech in bursts that can arrive late or all at once. My first player scheduled each chunk as its own Web Audio node, and these were hard to cancel when I needed to stop the voice. I replaced it with an `AudioWorklet` that plays from a 12-second ring buffer. It waits for about 120 ms of audio before it starts. If the buffer runs dry, it pauses and refills instead of stuttering. Now a single `reset` message stops the voice.
- Handling interruptions: In a real interview, people interrupt each other. They also say "mm-hm" and "right" while the other person talks. An interviewer should stop for the first and carry on through the second. I tried to implement this manually with a small gate on top of Gemini's own speech detection. It measures how long you speak and how loud you are:
- Under 550 ms: a backchannel. The interviewer keeps talking.
- From 550 ms to 1.1 s: an interruption only if you are loud enough, by average or by peak level.
- Over 1.1 s: always an interruption.
When the gate opens, Bubbles clears the playback queue and resets the ring buffer. It also drops any audio that arrives while you speak. After you go quiet for 250 ms, the interviewer may speak again.
- Knowing when you are done: Coding interviews have long silences while you think or type. Bubbles waits 2 to 3 seconds of silence before it treats your turn as finished. It also holds the turn open while you are still typing in the editor.
- Emotion in the voice: AI doesn't understand the concept of emotion in voice. Emotion is expressed as the subtle pitch in voice, the small laugh, the mmmhmmm's. It was a rabbit hole and, turns out, deep enough for companies to be working on it such as ElevenLabs.