MeetEase — the build story
Most video apps treat AI like a feature you bolt on after the fact: a summary email that shows up an hour later, a transcript you have to go hunting for. I wanted the opposite — a meeting that understands itself while it's happening.
So MeetEase pipes your microphone into a live transcript and hands that transcript to Claude in real time. Ask it to summarize, pull action items, or catch you up on what you missed — mid-call, not tomorrow morning. The hard part was never the model. It was making the plumbing feel instant.
How it came together
Six months, one honest “this is where it got hard” week.
Ideation
One nagging question: what would a video app feel like if the AI weren’t a bolt-on widget in the corner, but wired into the meeting itself? I sketched the pipeline on a napkin — mic → transcript → model — and couldn’t stop thinking about it.
Design & scoping
Built the design language first: warm near-black surfaces, a rose primary, glassmorphism. Decided early that the app would run in “explore mode” — no login wall — so anyone could open it and poke around.
MVP — it makes calls
Stream for the video layer, Clerk for identity, the app shell and routing. The unglamorous foundation: you could start a call, join a call, and see other humans.
The part that broke everything
Wiring the live transcript into Claude in real time. Two components that faked their data had to become one real pipeline — and Anthropic’s streaming API speaks Server-Sent Events, not JSON. Getting the deltas to land smoothly while audio was still flowing was the week I earned this project.
Polish
Empty, loading, and error states. Accessibility passes. SEO and social cards. And the decision that aged best: make every external service degrade gracefully instead of crashing when its keys are missing.
Launch
Shipped to Vercel — and immediately hit a MIDDLEWARE_INVOCATION_FAILED 500 because the edge auth middleware throws without keys. Made the middleware crash-proof, redeployed, and watched it come up clean. Launch days are never quiet.
What it does
HD multi-party video
Low-latency WebRTC calls with grid and speaker layouts, screen share, and recordings — powered by Stream.
Live in-browser transcription
Real-time speech-to-text using the browser’s native Web Speech API. No paid STT service, no extra latency.
Claude-powered copilot
Summaries, action items, and “catch me up” — generated from the live transcript and streamed token-by-token.
Guest “explore mode”
No login wall. Anyone can open the app and browse; sign-in is optional and switches on the moment keys are configured.
Crash-proof by design
Clerk, Stream, and Anthropic can each be missing and the app still loads. Resilience is a feature, not an afterthought.
Interesting decisions & challenges
The parts worth talking about in an interview.
A single “meeting intelligence” pipeline
Live transcription and the AI copilot used to be two disconnected, faked components. I unified them behind one React context so the microphone feeds a real transcript, and that transcript is what Claude actually reads. One source of truth turned a demo into a system.
Streaming the LLM, not waiting on it
The /api/ai route proxies Anthropic’s SSE stream, parses each chunk, and re-emits only the text deltas to the client. The copilot starts answering before the model has finished thinking — which is the whole difference between “snappy” and “did it freeze?”
Graceful degradation across every service
No Anthropic key → the copilot runs an honest offline fallback. No Stream key → the app is browsable without video. No Clerk key → guest-only mode instead of a 500. Each integration fails soft, independently.
Edge middleware that can’t take down the site
Clerk’s middleware throws at the edge without keys — the exact MIDDLEWARE_INVOCATION_FAILED error this project shipped with. The fix: only invoke Clerk when configured, and wrap it so any auth hiccup falls through to guest mode. The site stays up no matter what.
Tech stack
And the one-line reason behind each choice.
Want to build something — or break something interesting?
I'm Charan, and I'm always up for a good problem. Let's talk.