<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Gabriel Luis Pacheco</title>
    <description>The latest articles on DEV Community by Gabriel Luis Pacheco (@gabriel_luispacheco_6835).</description>
    <link>https://dev.to/gabriel_luispacheco_6835</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4236011%2F88c2def4-76b3-4e71-96c5-688ff7b2b862.png</url>
      <title>DEV Community: Gabriel Luis Pacheco</title>
      <link>https://dev.to/gabriel_luispacheco_6835</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/gabriel_luispacheco_6835"/>
    <language>en</language>
    <item>
      <title>Building a voice English-practice app in the browser (what broke first)</title>
      <dc:creator>Gabriel Luis Pacheco</dc:creator>
      <pubDate>Mon, 09 Nov 2026 23:08:03 +0000</pubDate>
      <link>https://dev.to/gabriel_luispacheco_6835/building-a-voice-english-practice-app-in-the-browser-what-broke-first-4ncf</link>
      <guid>https://dev.to/gabriel_luispacheco_6835/building-a-voice-english-practice-app-in-the-browser-what-broke-first-4ncf</guid>
      <description>&lt;p&gt;&lt;em&gt;I'm Gabriel Luis Pacheco, founder of &lt;a href="https://batatalk.com.br" rel="noopener noreferrer"&gt;BataTalk&lt;/a&gt; — a Brazilian product for spoken English practice with AI characters and an in-scene coach (Tato). This is a build post, not a growth flex.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The product in one breath
&lt;/h2&gt;

&lt;p&gt;BataTalk helps Brazilians who understand English but freeze when it's their turn to speak. Practice happens by voice in the browser: AI characters in everyday scenes (café, airport, interview), and Tato cues the start of the sentence when you blank. Levels A1–C2. Free plan: one guided conversation per day, no card. Paid plans are priced in BRL on &lt;a href="https://batatalk.com.br/planos" rel="noopener noreferrer"&gt;/planos&lt;/a&gt;. Honest limits: AI can err; not a certificate; not a live teacher; no accent score theater.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stack in one paragraph
&lt;/h2&gt;

&lt;p&gt;Node 24+, vanilla JS for the app shell, marketing pages SSR'd as HTML, Supabase Auth + Postgres (service role on the API, RLS as second layer), Stripe, Resend, Cloudflare Turnstile, Render + Cloudflare. Voice path: browser mic → our API → transcription / model reply. We intentionally &lt;strong&gt;do not&lt;/strong&gt; store the audio file.&lt;/p&gt;

&lt;h2&gt;
  
  
  Constraint that shaped everything
&lt;/h2&gt;

&lt;p&gt;The target user will not "just prompt ChatGPT every day." So the product ships &lt;strong&gt;scenes with goals&lt;/strong&gt; and a coach for the freeze moment. That kills tempting features: social feed, streak theater, fake certificates, pronunciation grades based on flaky ASR text.&lt;/p&gt;

&lt;p&gt;Browser-first matters in Brazil: cold start on mobile Chrome during a work break beats "download our app first."&lt;/p&gt;

&lt;h2&gt;
  
  
  What broke first
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Guest voice trial vs abuse
&lt;/h3&gt;

&lt;p&gt;"First line without signup" is great UX and catnip for bots. We gate with Turnstile + daily caps + health checks on transcription. When any dependency is down, the client must fail with a &lt;em&gt;specific&lt;/em&gt; reason. Mapping "browser has no SpeechRecognition" to the same bucket as "trial off" poisoned our analytics and made us chase the wrong incident.&lt;/p&gt;

&lt;p&gt;Lesson: error taxonomy is product infrastructure. If you collapse distinct failures into one string, you will "fix" the wrong thing for a week.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Signup friction vs fraud
&lt;/h3&gt;

&lt;p&gt;Password minimum length, email confirmation, Turnstile, IP rate limits. Funnel math is brutal: many client-side &lt;code&gt;signup_started&lt;/code&gt; events versus few completed accounts. Instrument failure reasons &lt;em&gt;before&lt;/em&gt; you simplify auth into oblivion. Also: mobile layout that pushes the CTA below the fold is a silent killer — we shipped a CSS order fix after measuring absurd scroll height on small screens.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. SEO vs app shell
&lt;/h3&gt;

&lt;p&gt;If marketing content only exists after JS hydration, you lose search and LLM citation. We keep substantive copy in SSR HTML for landings, FAQ, and compare pages, and we publish &lt;code&gt;/llms.txt&lt;/code&gt; with stable facts (what it is, what it is not, pricing page link, privacy posture). &lt;code&gt;robots.txt&lt;/code&gt; allows major AI crawlers on purpose — GEO needs third-party mentions too, but blocking bots while asking to be cited is self-owning.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Cost of "just one more minute of voice"
&lt;/h3&gt;

&lt;p&gt;Voice is not a free client-side feature. Quotas (daily + rolling 30-day) and plan gates are core product. The free tier teaches the loop; it is not infinite GPT with your brand on top. Day boundary at 21:00 America/Sao_Paulo surprised early testers who thought "midnight" — document clock skew in the UI, not only in the terms.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Cannibalized content clusters
&lt;/h3&gt;

&lt;p&gt;Shipping many thin intent landings (interview ×3, airport ×2) diluted authority. Consolidation with 301s toward one strong URL per intent is boring editorial work and better SEO than another 400-word page.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture notes worth copying
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Server owns money and affiliates.&lt;/strong&gt; Anything that can pay or payout stays off the anon key.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RLS is belt; server role is suspenders.&lt;/strong&gt; Do not pretend RLS alone is your authz story for billing RPCs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;First-party analytics.&lt;/strong&gt; A simple events pipeline beat installing three pixels we would later regret for LGPD and for debugging.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Public health endpoint.&lt;/strong&gt; Expose whether onboarding trial is actually ready (captcha keys + transcription). The marketing site should not promise a mic demo the API cannot honor today.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Product decisions I'd repeat
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Cue the sentence start instead of only scoring the utterance after silence.&lt;/li&gt;
&lt;li&gt;Say "AI can err" in the FAQ in plain Portuguese.&lt;/li&gt;
&lt;li&gt;Keep audio off durable storage unless you have a clear teaching reason and a retention policy you can defend.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Product decisions I'd rethink earlier
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Onboarding voice failure instrumentation on day one.&lt;/li&gt;
&lt;li&gt;A single compare hub plus deep "vs" pages beat a swamp of near-duplicate landings.&lt;/li&gt;
&lt;li&gt;Brand collisions: "BataTalk" alone is a messy query internationally; plan content around qualified phrases early.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try it / roast it
&lt;/h2&gt;

&lt;p&gt;Site: &lt;a href="https://batatalk.com.br" rel="noopener noreferrer"&gt;https://batatalk.com.br&lt;/a&gt; — first spoken line can work without an account. If you're building similar voice UX, I'm especially interested in how you handle guest quotas without training a fraud dataset on your credit card, and how you explain mic permission inside in-app browsers (Instagram/TikTok WebViews are hostile).&lt;/p&gt;

&lt;p&gt;Comments welcome — especially dissenting UX takes from people who teach English for a living.&lt;/p&gt;

&lt;h2&gt;
  
  
  Appendix: local clock and support copy
&lt;/h2&gt;

&lt;p&gt;Support tickets often start with "my free conversation disappeared." Nine times out of ten it is the Brasília day boundary, a Turnstile failure inside a WebView, or the user opening the site through Instagram's in-app browser where the mic never appears. Write those three cases into the UI empty-states. Your future self will spend fewer nights re-explaining the same incident.&lt;/p&gt;

&lt;p&gt;If you ship voice in production, budget for: transcription spend, captcha fails, and the emotional cost of a demo that works on your laptop and dies on a mid-range Android Chrome tab with three other media tabs open.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>ai</category>
      <category>startup</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
