<?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: Lokesh</title>
    <description>The latest articles on DEV Community by Lokesh (@itslokeshx).</description>
    <link>https://dev.to/itslokeshx</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%2F2705917%2F83700e1b-f99b-45b2-aa56-d45a00ba3824.jpeg</url>
      <title>DEV Community: Lokesh</title>
      <link>https://dev.to/itslokeshx</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/itslokeshx"/>
    <language>en</language>
    <item>
      <title>Docker, Node, and Electron Walked Into My Terminal. So I Built a 3.5MB App to Kick Them All Out.</title>
      <dc:creator>Lokesh</dc:creator>
      <pubDate>Tue, 02 Jun 2026 10:05:53 +0000</pubDate>
      <link>https://dev.to/itslokeshx/docker-node-and-electron-walked-into-my-terminal-so-i-built-a-35mb-app-to-kick-them-all-out-4pea</link>
      <guid>https://dev.to/itslokeshx/docker-node-and-electron-walked-into-my-terminal-so-i-built-a-35mb-app-to-kick-them-all-out-4pea</guid>
      <description>&lt;h2&gt;
  
  
  2 AM, 4 GB of Docker, and a Very Simple Question
&lt;/h2&gt;

&lt;p&gt;I just wanted to ask Llama 3.2 about a regex.&lt;/p&gt;

&lt;p&gt;That's it. One prompt. One answer. 30 seconds of work.&lt;/p&gt;

&lt;p&gt;Here's what actually happened:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open Docker Desktop&lt;/li&gt;
&lt;li&gt;Wait for the Open WebUI container to wake up&lt;/li&gt;
&lt;li&gt;Watch it silently consume 500 MB of RAM&lt;/li&gt;
&lt;li&gt;Open a browser tab pointed at &lt;code&gt;localhost:3000&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Finally type the question&lt;/li&gt;
&lt;li&gt;Get my answer&lt;/li&gt;
&lt;li&gt;Notice the container is &lt;em&gt;still&lt;/em&gt; running, eating my battery at 3 AM&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;For a chat window. A chat window for a local model.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I closed my laptop, stared at the ceiling, and decided: this is no longer acceptable.&lt;/p&gt;

&lt;h2&gt;
  
  
  The State of Ollama UIs on Linux (It's Not Great)
&lt;/h2&gt;

&lt;p&gt;If you've tried to put a UI on Ollama on Linux, you've hit this wall. Your options are basically:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Installed Size&lt;/th&gt;
&lt;th&gt;What You Need&lt;/th&gt;
&lt;th&gt;Reality&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Open WebUI&lt;/td&gt;
&lt;td&gt;500MB+&lt;/td&gt;
&lt;td&gt;Docker daemon + container runtime&lt;/td&gt;
&lt;td&gt;Heavy, always-on background process&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lobe Chat / Hollama&lt;/td&gt;
&lt;td&gt;100MB+&lt;/td&gt;
&lt;td&gt;Node.js + &lt;code&gt;npm install&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;A web server you have to start and stop&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Msty / Jan&lt;/td&gt;
&lt;td&gt;200–400MB&lt;/td&gt;
&lt;td&gt;Bundled Electron runtime&lt;/td&gt;
&lt;td&gt;A whole Chromium browser to send text&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Browser extensions&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;A specific browser&lt;/td&gt;
&lt;td&gt;Limited, can't do file uploads properly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Just use the CLI&lt;/td&gt;
&lt;td&gt;0MB&lt;/td&gt;
&lt;td&gt;Memorize every flag&lt;/td&gt;
&lt;td&gt;Writing JSON by hand just to send an image&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;None of these feel like &lt;em&gt;software&lt;/em&gt;. They feel like workarounds. And the worst part? &lt;strong&gt;Ollama itself is fantastic on Linux&lt;/strong&gt; — fast, lean, runs anywhere. The problem was never the model. It was everything wrapped around it.&lt;/p&gt;

&lt;h2&gt;
  
  
  So I Did the Only Sane Thing: Built My Own
&lt;/h2&gt;

&lt;p&gt;Meet &lt;strong&gt;&lt;a href="https://github.com/itslokeshx/ollama-chat" rel="noopener noreferrer"&gt;Ollama Chat&lt;/a&gt;&lt;/strong&gt; — a native Linux desktop app for Ollama that weighs &lt;strong&gt;3.5MB&lt;/strong&gt; installed, uses &lt;strong&gt;~50MB of RAM&lt;/strong&gt;, and ships as a proper &lt;code&gt;.deb&lt;/code&gt; package.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/itslokeshx" rel="noopener noreferrer"&gt;
        itslokeshx
      &lt;/a&gt; / &lt;a href="https://github.com/itslokeshx/ollama-chat" rel="noopener noreferrer"&gt;
        ollama-chat
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Native Linux desktop chat app for Ollama with real-time streaming, model switching, and file/image uploads—no Docker or Node.js required.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div&gt;
&lt;a rel="noopener noreferrer" href="https://github.com/itslokeshx/ollama-chat/public/logo.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fitslokeshx%2Follama-chat%2FHEAD%2Fpublic%2Flogo.png" alt="Ollama Chat" width="80" height="80"&gt;&lt;/a&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Ollama Chat&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;A native Linux desktop UI for Ollama — ~3.5MB, no Docker, no Node.js, no nonsense.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/itslokeshx/ollama-chat/LICENSE" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/7013272bd27ece47364536a221edb554cd69683b68a46fc0ee96881174c4214c/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d626c75652e737667" alt="MIT License"&gt;&lt;/a&gt;
&lt;a href="https://tauri.app" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/cea130ff0199238cbfe36555ca8772ad7e3094075d2d74803a863c394033679d/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f54617572692d322e302d6f72616e67653f6c6f676f3d7461757269" alt="Tauri 2.0"&gt;&lt;/a&gt;
&lt;a href="https://www.typescriptlang.org" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/b9c67778d61dcc9affd40064bc546ee651918a103ed7a5442132b03e0f706f9e/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f547970655363726970742d352e352d626c75653f6c6f676f3d74797065736372697074" alt="TypeScript"&gt;&lt;/a&gt;
&lt;a href="https://react.dev" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/7332fe94c60f0d37bd0087bc7cb9873cf249ba9b01cfd92809eb202d8632b656/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f52656163742d31382d3631646166623f6c6f676f3d7265616374" alt="React"&gt;&lt;/a&gt;
&lt;a href="https://github.com/itslokeshx/ollama-chat/releases" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/6ce1b0e67b4821913aaba80bff12eae49ba9fa0bb2a3179b378a0aebcaa3c5b1/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f706c6174666f726d2d4c696e75782d6c69676874677265793f6c6f676f3d6c696e7578" alt="Platform"&gt;&lt;/a&gt;
&lt;a href="https://github.com/itslokeshx/ollama-chat#-installation" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/c3a2f0ed6fb80d2f80881eaccfba8ebdfb2bece8f927e96e971b6ebcfe44f64d/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f696e7374616c6c25323073697a652d7e332e354d422d627269676874677265656e" alt="Size"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;br&gt;
&lt;div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;🚀 &lt;strong&gt;Get Started Now&lt;/strong&gt;
&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a href="https://github.com/itslokeshx/ollama-chat/releases/download/v1.0.0/Ollama.Chat_1.0.0_amd64.deb" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/f78e3a26a16900279dc3d37512ff754ca30f3e71d4d4634ebfa92e0fcdf8ee6f/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f2545322541432538375f446f776e6c6f61642d4f6c6c616d612e436861745f312e302e305f616d6436342e6465622d3265613434663f7374796c653d666f722d7468652d6261646765266c6f676f3d6c696e7578266c6f676f436f6c6f723d7768697465266c6162656c436f6c6f723d316131613161" alt="Download Ollama Chat"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;
  &lt;strong&gt;v1.0.0&lt;/strong&gt; • &lt;strong&gt;~3.5MB&lt;/strong&gt; • &lt;strong&gt;Linux (amd64)&lt;/strong&gt; • &lt;strong&gt;Free &amp;amp; Open Source&lt;/strong&gt;
&lt;/p&gt;


&lt;/div&gt;
&lt;br&gt;
&lt;br&gt;&lt;br&gt;
&lt;a rel="noopener noreferrer" href="https://github.com/itslokeshx/ollama-chat/public/screenshot.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fitslokeshx%2Follama-chat%2FHEAD%2Fpublic%2Fscreenshot.png" alt="Ollama Chat Screenshot" width="100%"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;📋 Table of Contents&lt;/h2&gt;
&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/itslokeshx/ollama-chat#-why-i-built-this" rel="noopener noreferrer"&gt;🤔 Why I built this&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/itslokeshx/ollama-chat#-installation" rel="noopener noreferrer"&gt;⬇ Installation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/itslokeshx/ollama-chat#-getting-started-3-minutes" rel="noopener noreferrer"&gt;🎯 Getting Started&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/itslokeshx/ollama-chat#-features" rel="noopener noreferrer"&gt;✨ Features&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/itslokeshx/ollama-chat#-screenshots" rel="noopener noreferrer"&gt;🖼 Screenshots&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/itslokeshx/ollama-chat#-tech-stack" rel="noopener noreferrer"&gt;🛠 Tech Stack&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/itslokeshx/ollama-chat#-build-from-source" rel="noopener noreferrer"&gt;🏗 Build from Source&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/itslokeshx/ollama-chat#-contributing" rel="noopener noreferrer"&gt;🤝 Contributing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/itslokeshx/ollama-chat#-license" rel="noopener noreferrer"&gt;📄 License&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;🤔 Why I built this&lt;/h2&gt;

&lt;/div&gt;

&lt;p&gt;If you use Ollama on Linux, you already know the pain.&lt;/p&gt;

&lt;p&gt;You install Ollama, pull a model, and then... &lt;strong&gt;you're stuck in a terminal forever&lt;/strong&gt;. Want to switch models? Type a command. Want to use a different model mid-chat? Stop, remember the command, switch, start again. Want to upload an image? Hope you know the exact syntax. Want a chat interface like Claude or ChatGPT? &lt;strong&gt;Linux users are left behind&lt;/strong&gt; — Ollama has a beautiful native UI for macOS, but Linux gets nothing.&lt;/p&gt;

&lt;p&gt;…&lt;/p&gt;&lt;/div&gt;


&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/itslokeshx/ollama-chat" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;p&gt;It's not a web server. It's not a Docker container. It's not an Electron app. It's a real native window with native titlebar controls, sitting in your application menu like any other app you actually use.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Stack (And Why Tauri Is the Real Hero)
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Tech&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Runtime&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Tauri 2.0&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Rust + system WebKit, no bundled Chromium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;UI&lt;/td&gt;
&lt;td&gt;React 18 + TypeScript&lt;/td&gt;
&lt;td&gt;Type-safe, component-driven&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Styling&lt;/td&gt;
&lt;td&gt;Tailwind v4 + Radix UI&lt;/td&gt;
&lt;td&gt;Accessible, themeable, fast&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;State&lt;/td&gt;
&lt;td&gt;Zustand&lt;/td&gt;
&lt;td&gt;No boilerplate, no Redux&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Build&lt;/td&gt;
&lt;td&gt;Vite 5&lt;/td&gt;
&lt;td&gt;Fast dev, tiny production output&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Packaging&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;.deb&lt;/code&gt; + &lt;code&gt;.AppImage&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Real Linux install methods&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Tauri is the architectural decision that makes this whole thing possible. Electron apps ship a full Chromium + Node.js runtime — that's why they're 150–300MB. Tauri uses the &lt;strong&gt;system's existing WebKit renderer&lt;/strong&gt; plus a thin Rust backend. Result: a real native app, 1/50th the size.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Features That Actually Matter
&lt;/h2&gt;

&lt;p&gt;I'm not going to give you a feature checklist. Here's what I use every day:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Real-time streaming&lt;/strong&gt; — token by token, the way LLMs are meant to feel&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Smart model picker&lt;/strong&gt; with tier grouping (Frontier 70B+, Balanced 7–34B, Efficient &amp;lt;7B) and capability badges (vision, tools, thinking)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Drag &amp;amp; drop&lt;/strong&gt; images into the chat — works with LLaVA, Qwen2-VL, LLaMA3.2-Vision&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;System prompt presets&lt;/strong&gt; — Engineer, Writer, Analyst, Terminal, Concise&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pull and delete models&lt;/strong&gt; from inside the app, with a live progress bar&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Full model info&lt;/strong&gt; — parameters, context length, quantization, size&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Export conversations&lt;/strong&gt; as Markdown or JSON&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Light / Dark / System&lt;/strong&gt; themes&lt;/li&gt;
&lt;/ul&gt;


&lt;div class="crayons-card c-embed"&gt;

  &lt;br&gt;
The detail I'm proudest of: model switching is a dropdown, not a command. Sounds obvious. Try it after midnight and you'll understand.&lt;br&gt;

&lt;/div&gt;


&lt;h2&gt;
  
  
  3-Minute Setup
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;\&lt;/code&gt;`bash&lt;/p&gt;

&lt;h1&gt;
  
  
  1. Install Ollama (skip if you already have it)
&lt;/h1&gt;

&lt;p&gt;curl -fsSL &lt;a href="https://ollama.com/install.sh" rel="noopener noreferrer"&gt;https://ollama.com/install.sh&lt;/a&gt; | sh&lt;/p&gt;

&lt;h1&gt;
  
  
  2. Pull a model
&lt;/h1&gt;

&lt;p&gt;ollama pull llama3.2&lt;/p&gt;

&lt;h1&gt;
  
  
  3. Install Ollama Chat
&lt;/h1&gt;

&lt;p&gt;wget &lt;a href="https://github.com/itslokeshx/ollama-chat/releases/download/v1.0.0/Ollama.Chat_1.0.0_amd64.deb" rel="noopener noreferrer"&gt;https://github.com/itslokeshx/ollama-chat/releases/download/v1.0.0/Ollama.Chat_1.0.0_amd64.deb&lt;/a&gt;&lt;br&gt;
sudo dpkg -i Ollama.Chat_1.0.0_amd64.deb&lt;/p&gt;
&lt;h1&gt;
  
  
  4. Start Ollama and launch
&lt;/h1&gt;

&lt;p&gt;ollama serve &amp;amp;&lt;br&gt;
ollama-chat&lt;br&gt;
`&lt;code&gt;\&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;That's it. No Docker, no Node, no web server, no &lt;code&gt;localhost:3000&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Numbers Don't Lie
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;App&lt;/th&gt;
&lt;th&gt;Installed&lt;/th&gt;
&lt;th&gt;RAM&lt;/th&gt;
&lt;th&gt;Cold Launch&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Open WebUI (Docker)&lt;/td&gt;
&lt;td&gt;500MB+&lt;/td&gt;
&lt;td&gt;~400MB&lt;/td&gt;
&lt;td&gt;5–10s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lobe Chat (npm)&lt;/td&gt;
&lt;td&gt;100MB+&lt;/td&gt;
&lt;td&gt;~200MB&lt;/td&gt;
&lt;td&gt;2–4s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Msty (Electron)&lt;/td&gt;
&lt;td&gt;250MB+&lt;/td&gt;
&lt;td&gt;~300MB&lt;/td&gt;
&lt;td&gt;2–3s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Ollama Chat&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;3.5MB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~50MB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;&amp;lt;1s&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;3.5MB. I had to double-check. Then I triple-checked. Then I checked the &lt;code&gt;.deb&lt;/code&gt; file size on disk and laughed.&lt;/p&gt;
&lt;h2&gt;
  
  
  Build It Yourself
&lt;/h2&gt;

&lt;p&gt;It's MIT licensed, open source, and about 200 lines away from working:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;\&lt;/code&gt;&lt;code&gt;bash&lt;br&gt;
git clone https://github.com/itslokeshx/ollama-chat&lt;br&gt;
cd ollama-chat&lt;br&gt;
npm install&lt;br&gt;
npm run tauri dev&lt;br&gt;
\&lt;/code&gt;&lt;code&gt;\&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;If you want a real &lt;code&gt;.deb&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;\&lt;/code&gt;&lt;code&gt;bash&lt;br&gt;
npm run tauri build&lt;br&gt;
sudo dpkg -i src-tauri/target/release/bundle/deb/Ollama.Chat_1.0.0_amd64.deb&lt;br&gt;
\&lt;/code&gt;&lt;code&gt;\&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  If You Try It
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/itslokeshx/ollama-chat" class="crayons-btn crayons-btn--primary" rel="noopener noreferrer"&gt;⭐ If this scratches an itch, a star on GitHub means a lot. And if something breaks — open an issue, I'll fix it.&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;I'm building this in the open. Roadmap lives on the repo: cloud model support, conversation sync, plugin system. If you want a feature, open an issue. If you want to build it, open a PR.&lt;/p&gt;

&lt;p&gt;Now I want to hear from you: &lt;strong&gt;what's the most absurd dependency you've installed just to do something simple?&lt;/strong&gt; I just want to feel less alone in this.&lt;/p&gt;

</description>
      <category>linux</category>
      <category>ollama</category>
      <category>tauri</category>
      <category>opensource</category>
    </item>
    <item>
      <title>I Built My Own Spotify Because Every Music App Failed Me (Web App + Android APK, Solo)</title>
      <dc:creator>Lokesh</dc:creator>
      <pubDate>Sat, 07 Mar 2026 10:07:14 +0000</pubDate>
      <link>https://dev.to/itslokeshx/i-built-my-own-spotify-because-every-music-app-failed-me-web-app-android-apk-solo-3ohc</link>
      <guid>https://dev.to/itslokeshx/i-built-my-own-spotify-because-every-music-app-failed-me-web-app-android-apk-solo-3ohc</guid>
      <description>&lt;h1&gt;
  
  
  I Rage-Quit a Music App at 1am. Then Built My Own Spotify. (React + Node + Capacitor + Groq AI)
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Full-stack music streaming platform — web app + real native Android APK — built solo, shipped free. Here's every technical decision, every mistake, and every architecture choice that made it work.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;🌐 Live:&lt;/strong&gt; &lt;a href="https://soul-sync-beta.vercel.app" rel="noopener noreferrer"&gt;soul-sync-beta.vercel.app&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;📱 APK:&lt;/strong&gt; &lt;a href="https://github.com/itslokeshx/SoulSync/releases/latest/download/SoulSync.apk" rel="noopener noreferrer"&gt;Download SoulSync.apk&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;⭐ GitHub:&lt;/strong&gt; &lt;a href="https://github.com/itslokeshx/SoulSync" rel="noopener noreferrer"&gt;itslokeshx/SoulSync&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  The Night That Started Everything
&lt;/h2&gt;

&lt;p&gt;It was 1am.&lt;/p&gt;

&lt;p&gt;I had 60 songs I wanted in one playlist. Tamil classics, Anirudh hits, a few AR Rahman deep cuts. The kind of playlist that takes you back somewhere.&lt;/p&gt;

&lt;p&gt;So I opened the app and started:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Song 1.  Search → find → add. ✅
Song 2.  Search → wrong version → search again → add. ✅
Song 3.  Search → add. ✅
...
Song 48. 45 minutes in. 12 songs left.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I closed the app.&lt;br&gt;
Put my phone down.&lt;br&gt;
Went to sleep.&lt;/p&gt;

&lt;p&gt;Woke up the next morning — still annoyed.&lt;/p&gt;

&lt;p&gt;That annoyance built &lt;strong&gt;SoulSync&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Not inspiration. Not a market gap. Not a YouTube tutorial that said "build a music app." Pure, unfiltered frustration at 1am.&lt;/p&gt;


&lt;h2&gt;
  
  
  What Is SoulSync?
&lt;/h2&gt;

&lt;p&gt;A full-stack music streaming platform that ships as both a &lt;strong&gt;web app&lt;/strong&gt; and a &lt;strong&gt;real native Android APK&lt;/strong&gt; — from a single React codebase.&lt;/p&gt;

&lt;p&gt;Here's what it does that Spotify doesn't (or charges for):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;SoulSync&lt;/th&gt;
&lt;th&gt;Spotify Premium (₹119/mo)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;AI playlist from song list&lt;/td&gt;
&lt;td&gt;✅ Free&lt;/td&gt;
&lt;td&gt;❌ Doesn't exist&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Listen together + live chat&lt;/td&gt;
&lt;td&gt;✅ Free&lt;/td&gt;
&lt;td&gt;⚠️ Paid, no chat&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Unlimited downloads&lt;/td&gt;
&lt;td&gt;✅ Free&lt;/td&gt;
&lt;td&gt;⚠️ Paid only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Offline without account&lt;/td&gt;
&lt;td&gt;✅ APK only&lt;/td&gt;
&lt;td&gt;❌ Never&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Intelligent NLP search&lt;/td&gt;
&lt;td&gt;✅ Free&lt;/td&gt;
&lt;td&gt;❌ Literal only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Open source&lt;/td&gt;
&lt;td&gt;✅ MIT&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Price&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;₹0. Forever.&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;₹119/month&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;


&lt;h2&gt;
  
  
  The Full Stack
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Frontend  → React 18 + TypeScript + Vite + Tailwind + Zustand
Backend   → Node.js + Express + MongoDB Atlas
Realtime  → Socket.io
AI        → Groq SDK + LLaMA 3.3 70B
Mobile    → Capacitor 6 (web → native Android APK)
Search    → JioSaavn API + 7-layer caching
Cache     → Upstash Redis
Deploy    → Vercel (frontend) + Render (backend)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;One backend. One database. One auth system. Serves both the web app and APK.&lt;/p&gt;


&lt;h2&gt;
  
  
  Feature 1: The AI Playlist Builder (The Feature That Started Everything)
&lt;/h2&gt;

&lt;p&gt;That 45-minute nightmare is now 10 seconds.&lt;/p&gt;
&lt;h3&gt;
  
  
  How it works
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User pastes 100 song names
         ↓
Groq AI (LLaMA 3.3 70B) parses the list
         ↓
Generates optimized search query per song
         ↓
Parallel JioSaavn search (Promise.all — 10 concurrent)
         ↓
Confidence scoring: exact / partial / not found
         ↓
Named playlist handed back to user
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  The chunking problem
&lt;/h3&gt;

&lt;p&gt;LLaMA has a context window limit. 100 songs in one shot causes hallucinations and timeouts. The solution: chunked processing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;CHUNK_SIZE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;MAX_CONCURRENT_GROQ&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;  &lt;span class="c1"&gt;// rate limit safe&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;BATCH_DELAY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;         &lt;span class="c1"&gt;// ms between batches&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;buildPlaylist&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;songs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;chunks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;chunkArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;songs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;CHUNK_SIZE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Song&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;chunks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;MAX_CONCURRENT_GROQ&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;batch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;chunks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;MAX_CONCURRENT_GROQ&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;batchResults&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;batch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chunk&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;processChunkWithGroq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;batchResults&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flat&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

    &lt;span class="c1"&gt;// SSE progress event to frontend&lt;/span&gt;
    &lt;span class="nf"&gt;emitProgress&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;processed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;MAX_CONCURRENT_GROQ&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;CHUNK_SIZE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;songs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;total&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;songs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;found&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;MAX_CONCURRENT_GROQ&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;chunks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;BATCH_DELAY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  SSE streaming so user sees progress in real time
&lt;/h3&gt;

&lt;p&gt;Instead of a spinner for 30 seconds, users see a live progress bar:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Backend — stream results as they come in&lt;/span&gt;
&lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/build-playlist/stream&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text/event-stream&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Cache-Control&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;no-cache&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;send&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`event: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;\ndata: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;\n\n`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;buildPlaylist&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;songs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;onProgress&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;progress&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;onChunk&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;songs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;chunk_complete&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;songs&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="na"&gt;onComplete&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;all&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;complete&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;songs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;all&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;end&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Frontend — consume the stream&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;EventSource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;BACKEND&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/api/ai/build-playlist/stream?sessionId=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;progress&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;processed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;total&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;found&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nf"&gt;setProgress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;processed&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;total&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="nf"&gt;setMatchCount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;found&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;chunk_complete&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;songs&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nf"&gt;setSongs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prev&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;prev&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;songs&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;  &lt;span class="c1"&gt;// live list grows as songs are found&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt; User sees songs appearing one batch at a time, with a real progress bar. Not a fake loading animation.&lt;/p&gt;




&lt;h2&gt;
  
  
  Feature 2: SoulLink — Listen Together
&lt;/h2&gt;

&lt;p&gt;Create a room → share a 6-character code → friend joins → everything syncs live.&lt;/p&gt;

&lt;h3&gt;
  
  
  The sync architecture
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Host device                  Server                  Guest device
     │                          │                          │
     │──── duo:join ───────────▶│                          │
     │                          │◀─── duo:join ────────────│
     │                          │                          │
     │──── duo:sync-play ──────▶│──── duo:sync-play ──────▶│
     │                          │                          │
     │◀─── duo:message ─────────│◀─── duo:message ─────────│
     │                          │                          │
     │──── duo:heartbeat ──────▶│◀─── duo:heartbeat ────── │
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The hardest problem: seek sync
&lt;/h3&gt;

&lt;p&gt;When the host seeks to 2:34, the guest needs to jump to 2:34 + network latency. Without compensation, they're always slightly out of sync.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Host sends seek with timestamp&lt;/span&gt;
&lt;span class="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;duo:seek&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;audio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentTime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;sentAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;// ping compensation&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="c1"&gt;// Guest receives and compensates&lt;/span&gt;
&lt;span class="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;duo:seek&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sentAt&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;latency&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;sentAt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;  &lt;span class="c1"&gt;// seconds&lt;/span&gt;
  &lt;span class="nx"&gt;audio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;position&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;latency&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's not perfect — but it's imperceptibly close on good connections.&lt;/p&gt;




&lt;h2&gt;
  
  
  Feature 3: The Search That Went From 10s to 150ms
&lt;/h2&gt;

&lt;p&gt;This was the biggest engineering challenge.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why it was slow
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Old flow (sequential):
  Query 1 → JioSaavn → wait 1200ms
  Query 2 → JioSaavn → wait 1200ms
  Query 3 → JioSaavn → wait 1200ms
  Query 4 → JioSaavn → wait 1200ms
  Total: ~4800ms minimum + debounce + Render cold start = 10s+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The 7-layer fix
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Layer 0 — Startup warmup     → top 100 queries pre-cached at boot
Layer 1 — Client-side cache  → Map&amp;lt;query, result&amp;gt;, 5min TTL, ~0ms
Layer 2 — Debounce + Abort   → 150ms (was 400ms) + cancel inflight
Layer 3 — Redis smart cache  → normalized keys, ~5ms
Layer 4 — Parallel fetch     → Promise.allSettled, all queries at once
Layer 5 — SSE streaming      → first results visible at ~200ms TTFB
Layer 6 — Intent parser      → 5 targeted queries instead of 1 vague one
Layer 7 — Static index       → top 100 artists return instantly
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The real fix: NLP Intent Parser
&lt;/h3&gt;

&lt;p&gt;Speed alone wasn't enough. The search also needed to be &lt;strong&gt;intelligent&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The problem: &lt;code&gt;"ranjini 173"&lt;/code&gt; is a Tamil movie. The old search sent &lt;code&gt;"ranjini 173"&lt;/code&gt; to JioSaavn as a song name — 0 results.&lt;/p&gt;

&lt;p&gt;The fix: an intent parser that classifies every query before searching:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;IntentType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;movie_songs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;     &lt;span class="c1"&gt;// "ranjini 173", "leo songs", "kgf 2"&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;artist_recent&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;   &lt;span class="c1"&gt;// "latest anirudh", "new vijay songs"&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;artist_all&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;      &lt;span class="c1"&gt;// "arijit singh songs"&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mood_search&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;     &lt;span class="c1"&gt;// "sad songs", "lofi tamil night"&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bgm_search&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;      &lt;span class="c1"&gt;// "harris jayaraj bgm"&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;language_hits&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;   &lt;span class="c1"&gt;// "tamil hits 2025"&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;song_direct&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;     &lt;span class="c1"&gt;// exact song name&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;parseIntent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;ParsedIntent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// 1. Movie database check (500+ movies including "ranjini 173")&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;movieMatch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;findMovieMatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;movieMatch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;intent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;movie_songs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;movie&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;movieMatch&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// 2. Artist dictionary (500+ aliases)&lt;/span&gt;
  &lt;span class="c1"&gt;// "arr" → "A.R. Rahman", "thalapathy" → "Vijay"&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;artist&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;findArtistMatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;artist&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;buildArtistIntent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;artist&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="c1"&gt;// 3. Mood detection&lt;/span&gt;
  &lt;span class="c1"&gt;// "sad lofi" → expands to 5 mood variants&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mood&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;detectMood&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mood&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;buildMoodIntent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mood&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="c1"&gt;// 4. Language + era detection&lt;/span&gt;
  &lt;span class="c1"&gt;// "90s tamil hits" → year range + language filter&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;buildDirectIntent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For &lt;code&gt;"ranjini 173"&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Intent: movie_songs
Expanded queries (all fired in parallel):
  1. "Ranjini 173 songs"          weight: 1.0
  2. "Ranjini 173 movie songs"    weight: 0.95
  3. "Ranjini 173 audio jukebox"  weight: 0.90
  4. "songs from Ranjini 173"     weight: 0.85
  5. (album search via separate endpoint)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Movie songs appear. Not a song titled "ranjini 173".&lt;/p&gt;

&lt;p&gt;For &lt;code&gt;"sad anirudh 2024"&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Intent: artist_recent + mood
Artist: "Anirudh Ravichander"
Year: 2024
Mood: sad

Queries:
  1. "Anirudh Ravichander songs 2024"  weight: 1.0
  2. "sad Anirudh songs 2024"          weight: 0.95
  3. "Anirudh 2024 hits"               weight: 0.90
  4. "emotional Anirudh 2024"          weight: 0.85
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Result timings
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Client cache hit:     ~0ms   (repeat search same session)
Redis cache hit:      ~5ms   (server cached)
Cold query (TTFB):  ~200ms   (first results streamed)
Cold query (full):  ~600ms   (all results scored + ranked)
Before:           10,000ms+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Feature 4: One Codebase → Web App + Native Android APK
&lt;/h2&gt;

&lt;p&gt;This was the decision that changed the whole project.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why not React Native?
&lt;/h3&gt;

&lt;p&gt;React Native would have meant:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Separate component library&lt;/li&gt;
&lt;li&gt;Separate navigation (React Navigation vs React Router)&lt;/li&gt;
&lt;li&gt;Separate state management patterns&lt;/li&gt;
&lt;li&gt;Essentially rebuilding the entire app&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why Capacitor won
&lt;/h3&gt;

&lt;p&gt;Capacitor wraps your existing web app in a native shell. Same React components. Same Zustand stores. Same backend calls. Native capabilities added via plugins.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;React App (web)
     ↓
npx cap add android
     ↓
npx cap sync
     ↓
Android Studio → assembleRelease
     ↓
SoulSync.apk ✅
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What native capabilities I added
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Haptics&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ImpactStyle&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@capacitor/haptics&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Filesystem&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Directory&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@capacitor/filesystem&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Network&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@capacitor/network&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;LocalNotifications&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@capacitor/local-notifications&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="c1"&gt;// Haptic on every song tap&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;Haptics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;impact&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ImpactStyle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Light&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="c1"&gt;// Download song to native filesystem&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;Filesystem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeFile&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`songs/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;song&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.mp3`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;base64Audio&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;directory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Directory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Data&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="c1"&gt;// Network-aware offline mode&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;Network&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getStatus&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;connected&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;setOfflineMode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// Lock screen media controls&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;LocalNotifications&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;schedule&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;notifications&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
    &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;song&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;song&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;artist&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;extra&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;now_playing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}]&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Platform detection pattern
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Capacitor&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@capacitor/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isAPK&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Capacitor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isNativePlatform&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;platform&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Capacitor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getPlatform&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// 'android' | 'ios' | 'web'&lt;/span&gt;

&lt;span class="c1"&gt;// Different storage per platform&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;storage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;isAPK&lt;/span&gt;
  &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;NativeFileStorage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;    &lt;span class="c1"&gt;// Capacitor Filesystem&lt;/span&gt;
  &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;WebStorage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;           &lt;span class="c1"&gt;// IndexedDB&lt;/span&gt;

&lt;span class="c1"&gt;// Different audio URL handling&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getPlayableUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;song&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Song&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isAPK&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;song&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;Capacitor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;convertFileSrc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;song&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;// file:// → capacitor://&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;song&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;streamUrl&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Feature 5: Personalized Dashboard
&lt;/h2&gt;

&lt;p&gt;Every session rebuilds from:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your listening history (last 90 days, MongoDB TTL index)&lt;/li&gt;
&lt;li&gt;Your language preferences (set at onboarding)&lt;/li&gt;
&lt;li&gt;Time of day (morning = energetic, night = chill)
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;buildDashboard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Section&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;history&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;prefs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;trending&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="nf"&gt;getListeningHistory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;  &lt;span class="c1"&gt;// days&lt;/span&gt;
    &lt;span class="nf"&gt;getUserPreferences&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nf"&gt;getTrendingByLanguage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prefs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;languages&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;])&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hour&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;getHours&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;timeContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;hour&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;morning&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
                    &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;hour&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;afternoon&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
                    &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;hour&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;22&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;evening&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
                    &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;late_night&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nf"&gt;buildTimeSection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;timeContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;history&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;         &lt;span class="c1"&gt;// "Morning Fresh"&lt;/span&gt;
    &lt;span class="nf"&gt;buildArtistSpotlight&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;history&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;                  &lt;span class="c1"&gt;// "Because You Listened"&lt;/span&gt;
    &lt;span class="nf"&gt;buildLanguageSection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prefs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;languages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;trending&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;// "Trending Tamil"&lt;/span&gt;
    &lt;span class="nf"&gt;buildRecentlyPlayed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;history&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nf"&gt;buildRecommended&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;history&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;prefs&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  The 5 Hardest Problems I Solved
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Background audio on Android APK dying when screen locks
&lt;/h3&gt;

&lt;p&gt;The web audio API pauses when the screen locks on Android. Fix: Capacitor's &lt;code&gt;@capacitor-community/background-runner&lt;/code&gt; + a MediaSession API registration.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mediaSession&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;metadata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MediaMetadata&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;song&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;artist&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;song&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;primaryArtists&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;artwork&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;song&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;sizes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;512x512&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}]&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mediaSession&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setActionHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;play&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;audioRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;play&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mediaSession&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setActionHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pause&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;audioRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;pause&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mediaSession&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setActionHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;nexttrack&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;playNext&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mediaSession&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setActionHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;previoustrack&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;playPrev&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Offline playback notification not working
&lt;/h3&gt;

&lt;p&gt;The original code had this guard:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Bug: this silently killed offline notifications&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;song&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;streamUrl&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;
&lt;span class="nf"&gt;showNowPlayingNotification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;song&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Offline songs have &lt;code&gt;filePath&lt;/code&gt;, not &lt;code&gt;streamUrl&lt;/code&gt;. Removing that guard and using &lt;code&gt;getPlayableUrl()&lt;/code&gt; fixed it.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Search cache key collision
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;"sad arijit"&lt;/code&gt; and &lt;code&gt;"arijit sad"&lt;/code&gt; should hit the same cache. The fix: normalize by sorting words alphabetically.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;normalizeKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;[^\w\s]&lt;/span&gt;&lt;span class="sr"&gt;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;           &lt;span class="c1"&gt;// ← the critical line&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;_&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// "sad arijit"  → "arijit_sad"&lt;/span&gt;
&lt;span class="c1"&gt;// "arijit sad"  → "arijit_sad"  ✅ same key&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Groq rate limits with 100 songs
&lt;/h3&gt;

&lt;p&gt;5 API keys in rotation, checked before each request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;GROQ_KEYS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GROQ_KEY_1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GROQ_KEY_2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;keyIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getGroqClient&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;GROQ_KEYS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;keyIndex&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="nx"&gt;GROQ_KEYS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="nx"&gt;keyIndex&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Groq&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. Socket.io rooms leaking memory
&lt;/h3&gt;

&lt;p&gt;Rooms weren't being cleaned up when users disconnected. After 48 hours the server would slow to a crawl.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;disconnect&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;room&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getRoomForSocket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;room&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;

  &lt;span class="nx"&gt;room&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;members&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;room&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;members&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Last person left — clean up completely&lt;/span&gt;
    &lt;span class="nx"&gt;activeRooms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;room&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Room &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;room&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; cleaned up`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Notify remaining members&lt;/span&gt;
    &lt;span class="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;room&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;duo:member-left&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;socketId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  What I'd Do Differently
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Redis from day one, not an afterthought.&lt;/strong&gt;&lt;br&gt;
I added caching after the search was already "working" at 10 seconds. If I'd designed the cache layer first, the search would have been fast from the start.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Intent parser before building the search UI.&lt;/strong&gt;&lt;br&gt;
I built the search UI for a simple text match, then had to refactor it when I realized literal string matching was useless for natural language queries.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Capacitor from day one.&lt;/strong&gt;&lt;br&gt;
I built the entire web app first, then added Capacitor. It worked, but there were 2-3 days of fixing things that broke in the native context (audio, storage, network detection). Designing with Capacitor in mind from the start would have been cleaner.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. SSE instead of polling for playlist building.&lt;/strong&gt;&lt;br&gt;
I used polling for the first version of the AI playlist builder (&lt;code&gt;setInterval&lt;/code&gt; every 2 seconds checking if it was done). SSE was a 2-hour rewrite that made the experience 10x better.&lt;/p&gt;




&lt;h2&gt;
  
  
  Performance Numbers
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Search (cold, intent-matched):    ~600ms  (was 10,000ms+)
Search (Redis cache hit):           ~5ms
Search (client cache hit):           ~0ms
AI Playlist (100 songs):           ~18s   (streamed, first results at ~3s)
SoulLink room creation:            ~120ms
Page load (Vercel edge):           ~800ms
APK cold start:                    ~1.2s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Play Store submission (APK is signed and ready)&lt;/li&gt;
&lt;li&gt;[ ] Lyrics sync display&lt;/li&gt;
&lt;li&gt;[ ] iOS build (Capacitor makes this straightforward)&lt;/li&gt;
&lt;li&gt;[ ] Audio visualizer on the full player&lt;/li&gt;
&lt;li&gt;[ ] Push notifications for new releases from followed artists&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;🌐 Web app:&lt;/strong&gt; &lt;a href="https://soul-sync-beta.vercel.app" rel="noopener noreferrer"&gt;soul-sync-beta.vercel.app&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;📱 Android APK&lt;/strong&gt; (best experience, direct install):&lt;br&gt;
&lt;a href="https://github.com/itslokeshx/SoulSync/releases/latest/download/SoulSync.apk" rel="noopener noreferrer"&gt;SoulSync.apk&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;⭐ Star on GitHub:&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://github.com/itslokeshx/SoulSync" rel="noopener noreferrer"&gt;github.com/itslokeshx/SoulSync&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Takeaway
&lt;/h2&gt;

&lt;p&gt;The best product you'll build isn't the one with the best market research.&lt;/p&gt;

&lt;p&gt;It's the one where you woke up the next morning still annoyed.&lt;/p&gt;

&lt;p&gt;That's your signal.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Questions about any part of the architecture? Drop them in the comments — happy to go deep on anything. 🎧&lt;/em&gt;&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>javascript</category>
      <category>react</category>
      <category>android</category>
    </item>
    <item>
      <title>I Built a "Second Brain" That Actually Thinks 🧠 — with GitHub Copilot CLI</title>
      <dc:creator>Lokesh</dc:creator>
      <pubDate>Sat, 14 Feb 2026 15:17:26 +0000</pubDate>
      <link>https://dev.to/itslokeshx/i-built-a-second-brain-that-actually-thinks-with-github-copilot-cli-5946</link>
      <guid>https://dev.to/itslokeshx/i-built-a-second-brain-that-actually-thinks-with-github-copilot-cli-5946</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/github-2026-01-21"&gt;GitHub Copilot CLI Challenge&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;h2&gt;
  
  
  🛑 The Problem: I Was Drowning in "Productivity"
&lt;/h2&gt;

&lt;p&gt;It was 2 AM. My desk was a mess.&lt;/p&gt;

&lt;p&gt;I had &lt;strong&gt;14 browser tabs&lt;/strong&gt; open. A Notion doc I hadn't touched in weeks. A Todoist list that was mocking me. A goal I set in January that was already dying quietly.&lt;/p&gt;

&lt;p&gt;I was drowning in tools that were supposed to save me. I realized I didn't need another "to-do list." I didn't need a static database.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I needed a brain.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;So, I opened my terminal, cleared the screen, and typed:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;gh copilot suggest "How do I build a life operating system where tasks, habits, and goals share a single consciousness?"&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;7 days later, I shipped &lt;strong&gt;CorteXia&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  🚀 What is CorteXia?
&lt;/h2&gt;

&lt;p&gt;CorteXia isn't just an app; it is an &lt;strong&gt;AI-Powered Life Operating System&lt;/strong&gt;. It combines the flexibility of Notion, the visual beauty of Linear, and the intelligence of a personal assistant into one unified workspace.&lt;/p&gt;

&lt;p&gt;But here is the killer feature: &lt;strong&gt;It talks back.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  💎 The "Superpowers"
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;🗣️ &lt;strong&gt;Voice-First Control:&lt;/strong&gt; I used the Web Speech API so you can &lt;em&gt;talk&lt;/em&gt; to your dashboard. "Hey, I just spent $15 on coffee." Boom. Budget updated.&lt;/li&gt;
&lt;li&gt;🧠 &lt;strong&gt;Persistent AI Memory:&lt;/strong&gt; It doesn't just answer; it &lt;em&gt;remembers&lt;/em&gt;. It knows your name, your deadlines, and that you're trying to quit sugar.&lt;/li&gt;
&lt;li&gt;🔥 &lt;strong&gt;The "GitHub" for Habits:&lt;/strong&gt; A 365-day contribution graph for your life habits. Because if it's not green, did it even happen?&lt;/li&gt;
&lt;li&gt;🕸️ &lt;strong&gt;The Neural Network:&lt;/strong&gt; Tasks are linked to Goals. Expenses are linked to Budgets. Moods are linked to Productivity. &lt;strong&gt;Everything is connected.&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  👇 Give it a Try!
&lt;/h3&gt;

&lt;p&gt;I poured my heart (and a lot of coffee) into this.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://corte-xia.vercel.app" rel="noopener noreferrer"&gt;🚀 Try the Live Demo&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://github.com/itslokeshx/CorteXia" rel="noopener noreferrer"&gt;⭐ Star the Repository on GitHub&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;(P.S. Try the Voice Mode. It feels like magic. ✨)&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  My Experience with GitHub Copilot CLI
&lt;/h2&gt;

&lt;p&gt;I’m Lokesh A, a third-year Computer Science Engineering student focused on full-stack development.Building real time projects!&lt;/p&gt;

&lt;p&gt;This wasn’t just CRUD. It was a &lt;strong&gt;Next.js 16 + MongoDB (12 Collections) + AI-powered system&lt;/strong&gt; with deeply connected logic.&lt;/p&gt;

&lt;p&gt;GitHub Copilot CLI became my force multiplier.&lt;/p&gt;

&lt;h3&gt;
  
  
  Architecture
&lt;/h3&gt;

&lt;p&gt;Designing a high-performance, bidirectional schema for Tasks, Goals, and Habits required system-level thinking.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gh copilot suggest "Design a Mongoose schema optimized for dashboard-heavy reads with bidirectional references"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It didn’t just generate code — it explained embedding vs referencing trade-offs and helped me make better architectural decisions.&lt;/p&gt;

&lt;h3&gt;
  
  
  AI Context Strategy
&lt;/h3&gt;

&lt;p&gt;To avoid token overflow while injecting live user data into the LLM:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gh copilot explain "Inject relevant user data into an LLM without exceeding token limits"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It guided me toward a selective context strategy (today’s tasks + active goals only), which shaped the AI design.&lt;/p&gt;

&lt;h3&gt;
  
  
  Debugging
&lt;/h3&gt;

&lt;p&gt;When optimistic UI updates caused duplicate entries:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gh copilot explain "React optimistic update causing duplicate keys when toggling quickly"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It identified the need for debouncing and helped structure a clean solution.&lt;/p&gt;

&lt;p&gt;Copilot CLI didn’t replace my learning.&lt;br&gt;
It accelerated it.&lt;/p&gt;

&lt;p&gt;It helped me think bigger, ship faster, and build with more confidence.&lt;/p&gt;




&lt;h2&gt;
  
  
  🛠️ The Tech Stack (Vibe Coding Edition)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Framework:&lt;/strong&gt; Next.js 16 (App Router + Turbopack)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Language:&lt;/strong&gt; TypeScript 5 (Strict Mode)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Database:&lt;/strong&gt; MongoDB Atlas (12 Collections)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Styling:&lt;/strong&gt; Tailwind CSS + Framer Motion (for those buttery smooth 60fps animations)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI:&lt;/strong&gt; Multi-LLM Fallback System (Groq + OpenAI)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Voice:&lt;/strong&gt; Native Web Speech API&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🔮 The Bigger Picture
&lt;/h2&gt;

&lt;p&gt;We talk a lot about AI replacing developers. After building CorteXia, I believe the opposite.&lt;/p&gt;

&lt;p&gt;Copilot CLI didn't replace my creativity; it &lt;strong&gt;accelerated&lt;/strong&gt; it. It handled the boilerplate, the schema validation, and the obscure regex patterns, so I could focus on &lt;strong&gt;User Experience&lt;/strong&gt; and &lt;strong&gt;Logic&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;CorteXia went from a "weekend idea" to a production-grade application in record time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;That's not AI replacing a developer.&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;That's a developer with a superpower.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>githubchallenge</category>
      <category>devchallenge</category>
      <category>cli</category>
      <category>githubcopilot</category>
    </item>
    <item>
      <title>I Built a Portfolio Like a System, Not a Résumé!</title>
      <dc:creator>Lokesh</dc:creator>
      <pubDate>Wed, 21 Jan 2026 16:48:13 +0000</pubDate>
      <link>https://dev.to/itslokeshx/i-built-a-portfolio-like-a-system-not-a-resume-1kh9</link>
      <guid>https://dev.to/itslokeshx/i-built-a-portfolio-like-a-system-not-a-resume-1kh9</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/new-year-new-you-google-ai-2025-12-31"&gt;New Year, New You Portfolio Challenge Presented by Google AI&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  About Me
&lt;/h2&gt;

&lt;p&gt;Hi, I’m &lt;strong&gt;Lokesh&lt;/strong&gt; — a frontend-focused web developer with growing full-stack experience.&lt;/p&gt;

&lt;p&gt;I don’t learn best by memorizing theory or following tutorials blindly. I learn by building, testing, breaking, and refining real systems.&lt;/p&gt;

&lt;p&gt;Over time, I realized that a traditional portfolio page doesn’t truly represent how I think or how I grow as a developer. So instead of creating another résumé-style website, I treated this portfolio as a &lt;strong&gt;living system&lt;/strong&gt; — one that reflects my workflow, my decisions, and my evolution.&lt;/p&gt;

&lt;p&gt;This project isn’t about perfection. It’s about process, intention, and progress.&lt;/p&gt;

&lt;h2&gt;
  
  
  Portfolio
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;🔗 Live Portfolio (copy-friendly link):&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://itslokeshx.vercel.app/" rel="noopener noreferrer"&gt;https://itslokeshx.vercel.app/&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  📂 Source Code — GitHub Repository
&lt;/h2&gt;

&lt;p&gt;Explore the full source code of this portfolio, including components, styles, and deployment configuration:&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://github.com/itslokeshx/portfolio" rel="noopener noreferrer"&gt;https://github.com/itslokeshx/portfolio&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;p&gt;This portfolio was planned first, designed with AI, and developed with AI as a collaborator — not as a shortcut.&lt;/p&gt;

&lt;h3&gt;
  
  
  🎨 Design (Google Stitch AI)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Used &lt;strong&gt;Google Stitch AI&lt;/strong&gt; to explore layout direction, hierarchy, and interaction tone.&lt;/li&gt;
&lt;li&gt;Focused on clarity over decoration.&lt;/li&gt;
&lt;li&gt;The system-style interface mirrors how I naturally reason through problems.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🧩 Development (Google Antigravity)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Built using &lt;strong&gt;Google Antigravity&lt;/strong&gt; as an AI-assisted development environment.&lt;/li&gt;
&lt;li&gt;AI helped with architecture thinking, component structuring, and refactoring.&lt;/li&gt;
&lt;li&gt;Every final decision — layout, logic, and behavior — was intentional and human-driven.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ⚙️ Stack &amp;amp; Infrastructure
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;React / Next.js&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Framer Motion&lt;/strong&gt; for subtle interaction feedback&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Docker&lt;/strong&gt; for containerization&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Google Cloud Run&lt;/strong&gt; for deployment:

&lt;ul&gt;
&lt;li&gt;Containerized build&lt;/li&gt;
&lt;li&gt;Scale-to-zero when idle&lt;/li&gt;
&lt;li&gt;Free-tier safe configuration&lt;/li&gt;
&lt;li&gt;Production-ready setup&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Instead of using many tools, I focused on using fewer tools &lt;em&gt;well&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'm Most Proud Of
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Treating a portfolio as a system that explains &lt;strong&gt;how&lt;/strong&gt; I think, not just &lt;strong&gt;what&lt;/strong&gt; I’ve built.&lt;/li&gt;
&lt;li&gt;Designing with restraint instead of visual overload.&lt;/li&gt;
&lt;li&gt;Successfully containerizing and deploying on &lt;strong&gt;Google Cloud Run&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Keeping performance, simplicity, and cost under control.&lt;/li&gt;
&lt;li&gt;Being honest about my current level while clearly showing growth.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This portfolio reflects how I want to build software going forward:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Thoughtfully. Iteratively. With intention.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;&lt;em&gt;🙏 Thanks to the DEV Community and Google AI team for creating a challenge that encourages experimentation, reflection, and responsible use of AI.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>googleaichallenge</category>
      <category>portfolio</category>
      <category>gemini</category>
    </item>
  </channel>
</rss>
