DEV Community

wanjiang
wanjiang

Posted on

I got tired of robotic language apps, so I connected real-world radio stations to an AI

I got tired of robotic language apps, so I connected real-world radio stations to an AI πŸ“»

If you’ve ever tried to learn a foreign language, you know the dreaded "Intermediate Plateau."

You spend months on apps like Duolingo or textbook audio courses. You feel confident. You think you’re ready. Then, you turn on a real French news broadcast or a fast-paced American sports radio station, and suddenly... you understand absolutely nothing.

Real people don't speak like robotic text-to-speech engines. They mumble, they use slang, they talk over each other, and they speak fast.

I realized that if I wanted to truly master a language at a C1/C2 level, I needed raw, unedited, real-world audio. But I also needed a coach to help me break it down.

So, I built RadioShadow β€” a Next.js web app that captures live global radio streams and uses AI to generate real-time subtitles, translations, and pronunciation coaching.

RadioShadow Hero Image

The "Aha" Moment πŸ’‘

The core idea of RadioShadow is based on the "Shadowing" technique β€” a highly effective language learning method where you listen to a native speaker and immediately repeat what they say, mimicking their intonation and rhythm.

My goal was simple in theory but technically challenging:

  1. Tap into live radio streams across the globe (news, talk shows, music).
  2. Process the audio stream with low latency.
  3. Use an AI model to transcribe and translate the live feed instantly.
  4. Let the user use their microphone to "shadow" the broadcaster, and have the AI score their pronunciation.

Building the MVP: Map, Streams, and UX Challenges πŸ—ΊοΈ

I built the frontend using Next.js and Tailwind CSS, aiming for a clean, minimalist, light-mode SaaS aesthetic.

Live Transcript UI

One of the most interesting challenges was the UX design for station selection. When you have access to tens of thousands of global radio stations, the "Paradox of Choice" kicks in. Users would stare at the world map, see thousands of options, and get completely overwhelmed.

To fix this, I had to balance geographical exploration with guided intent.

I implemented an interactive world map where users can click on a country to filter regional streams. But what if a user just wants to learn French and doesn't know which country's station is best?

I recently shipped a "✨ Help Me Choose" wizard right at the top of the control panel.

Help Me Choose Wizard

Instead of a generic search bar or an annoying popup, it’s a lightweight conversational UI. It asks you what language you are targeting and your current vibe (e.g., Hardcore News or Relaxed Talk Show), and instantly teleports you to the perfect live stream.

The Tech Stack πŸ› οΈ

  • Frontend: Next.js (App Router), Tailwind CSS, Framer Motion (for those buttery smooth state transitions).
  • Audio Processing: Web Audio API to handle the live stream buffering and microphone capturing.
  • AI Engine: Integrating LLMs for real-time transcription, contextual translation, and acoustic scoring for the user's shadowing practice.
  • Database & Auth: Firebase & NextAuth for seamless Google sign-ins and saving user pronunciation history.

What's Next? (And I need your feedback!) πŸš€

Building this has been a wild ride of wrestling with audio streams, managing complex frontend states, and dealing with real-time AI latency. But hearing a live broadcast from Paris and having AI break it down for you instantly feels like magic.

The app is live at radioshadow.ai.studio (You can try the free real-time stream right away).

I am currently trying to refine the UI/UX and improve the AI scoring latency. Since this community has always been incredibly supportive of indie makers, I would love to hear your brutally honest feedback.

  • Does the map navigation feel intuitive?
  • How is the audio latency on your end?
  • Any features you think are missing for language learners?

Let me know in the comments! πŸ‘‡

Top comments (0)