I Built an AI IPL War Room Using Gemini Multi-Agent Reasoning 🏏

What if an IPL captain had an AI tactical war room?
Not a chatbot:)

A system where multiple AI agents debate:

  • who bowls next
  • whether Bumrah should bowl now or later
  • when to attack
  • how dew changes strategy

That idea became CaptainCool AI — a multi-agent IPL strategist powered by Google Gemini.

🧠 The Core Idea
Most sports AI apps generate one generic answer.

But real cricket decisions involve:

  • disagreement
  • tactical tradeoffs
  • risk analysis
  • momentum shifts

So instead of one AI model pretending to do everything, I built:
an AI captaincy war room.
⚡ Multi-Agent System

CaptainCool AI uses multiple Gemini-powered agents:

1.🧠 Strategist
Proposes the tactical move.

2.📊 Stats Analyst
Validates the decision using cricket context and live match state.

3.🔥 Devil’s Advocate
Challenges risky plans and forces reconsideration.

4.👑 Final Decision Engine
Combines all debate outcomes into the final captaincy call.

5.🎙️ Commentary Agent
Turns the reasoning into IPL-style live commentary.

The result feels far more human than a normal chatbot.

CaptainCool AI operates in two different modes:

🧪 Manual Tactical Mode

Users can manually simulate IPL scenarios by entering:

  • score
  • wickets
  • overs
  • pitch conditions
  • dew factor
  • captain style
  • impact player availability

This mode was designed for cinematic tactical simulations and reliable demo scenarios.

🔴 Live Match Beta Mode

I integrated CricAPI to fetch live cricket matches and auto-fill the tactical dashboard in real time.

The system processes:

  • score
  • wickets
  • overs
  • venue
  • batting side
  • match pressure

…and feeds that directly into the Gemini reasoning pipeline.

🎨 Frontend Experience

I wanted the app to feel like an IPL broadcast control room.

So the UI includes:

  • dark navy gradients
  • cyan + gold accents
  • glassmorphism cards
  • animated confidence bars
  • cricket-ball loading animations
  • sequential agent debate reveals

Built using:

  • Node.js
  • Express.js
  • EJS
  • Gemini 2.5 Flash
  • CricAPI

🧩 Biggest Challenges

1.Multi-Agent Coordination
Making agents genuinely disagree instead of repeating similar answers.

2.API Quotas
Multi-agent reasoning consumes API calls quickly, so responses had to be optimized carefully.

3.Live Match Data
Live cricket feeds often return incomplete data, requiring normalization and fallback handling.

📈 What I Learned
The biggest insight was:
AI systems become dramatically more believable when agents disagree instead of instantly agreeing.
That tactical conflict made CaptainCool AI feel much more realistic.

Agentic coding isn’t a trap. Lazy engineering is.

The hottest take in developer discourse right now isn’t about frameworks or languages. It’s about whether AI agents are silently destroying your codebase.

A viral post recently called agentic coding a “liability accelerator.” It painted a picture of codebases rotting from the inside out, maintained by developers who no longer understand what they’ve built. The post spread fast. Engineers nodded along. And I think they’re blaming the wrong thing entirely.

The real problem has a name

The issue is not with the representative. It is with the approach that exists, or the absence of it.

A counterpoint article on Dev.to actually provided production logs from a team that was using agents that had very precise well-written specs, and the results were clean output, actually, predictable output, and fewer bugs than we had experienced in their pre-agent workflow.

What made a difference was not the tool used, but rather that an individual took the time to write a solid specification before using the generation tool.

Agents don’t skip code review. You do.

This is what goes down in reality when agentic coding flops:

→ Developer gives a vague prompt with no constraints
→ Agent produces plausible-looking code that technically works
→ Developer ships it without reading it carefully
→ Three months later, nobody understands the module

Sound familiar? Now substitute “agent” with “junior developer” or “Stack Overflow copy-paste” or “that contractor we hired for two weeks.” The end result is the same. Throughout history, we’ve found ways to write code that escapes our comprehension. Agents just automated the process.

It’s the speed that frightens everyone. I understand. However, it was never the problem. Being reckless was.

The codebase comprehension problem is real but not new

The biggest legitimate concern is that developers stop understanding their own systems. That’s worth taking seriously. 🧠

However, we have to admit that this was already in motion. Enormous monorepos, automatically created boilerplate code, dependency trees that no one checks, copying and pasting infrastructure-as-code templates from blog posts. In fact, most teams were already working without a complete understanding of their stack.

Agents were not responsible for the comprehension gap. They simply brought it to our attention.

We shouldn’t eliminate the tool. But regard agent output as any other contributed code: review it, test it, understand it before it merges. If your team lacks that discipline, you had a process problem long before you had an AI problem.

What good agentic workflows actually look like

Agents tend to provide real value to teams that have a few things in common:

→ They write detailed specs before prompting — acceptance criteria, edge cases, constraints
→ They review agent output line by line, same as a human PR
→ They use agents for the boring stuff — boilerplate, tests, migrations — not architecture decisions
→ They treat the agent like a fast but inexperienced teammate, not an oracle

All of this is evolutionary. It’s engineering discipline being applied to a new-fangled tool. The type of discipline we’ve always known we needed, but often conveniently overlooked. 😅

The uncomfortable truth

Blaming agents for tech debt is comfortable. It gives us an external villain.

The developers who are now shipping unreviewed agent code are the ones who shipped unreviewed human code before. They are the ones who neglected to write tests. The same ones who approved their PRs on a Friday afternoon. The process may have changed, but their habits haven’t.

Writing code in a way that reflects your engineering culture is not a bad thing. It’s not a trap. It’s a mirror. It shows you what you are. And it does so faster and more honestly than ever before. If you don’t like what you see, the problem isn’t the reflection.

Good process makes agents a multiplier. Bad process makes agents a liability accelerator. The variable was never the agent.

So here’s my question: has your team established any specific rules or review practices for agent-generated code, or is it still the Wild West?

🏏 Building “Captain Cool”: A Multi-Agent IPL Strategist with Google Gemini

Cricket is a captain’s game. The margin between an IPL trophy and crashing out in the playoffs is often a single, split-second tactical decision: who bowls the 19th over? Do you bring in the spinner against the left-hander? What happens when the dew sets in?

For the APL GDG Hackathon, I wanted to simulate the high-pressure environment of an IPL dugout. I built Captain Cool — a Multi-Agent AI System powered entirely by the Google Gemini stack that debates and makes real-time tactical cricket decisions.

Here is how I built it.

🏗️ The Architecture (The Coaching Staff)

To make this truly agentic, one LLM call wasn’t going to cut it. A real captain doesn’t operate in a vacuum—they rely on data analysts and vice-captains.

Using Google GenAI SDK and the blazing fast gemini-2.5-flash model, I orchestrated a 3-agent debate loop:

  1. 📊 The Stats Analyst: This agent doesn’t guess. It uses Gemini Function Calling to hit my custom Python tools (get_player_stats, get_venue_stats) to pull raw numbers on strike rates, matchups, and pitch conditions based on the live match state.
  2. 🧠 The Strategist (The Captain): Takes the raw match state and the Analyst’s data to propose the next tactical move.
  3. ⚖️ The Devil’s Advocate (The Vice-Captain): This is where the magic happens. The Vice-Captain is explicitly prompted to find flaws in the Captain’s plan, highlight contrarian angles (like dew factor), and prevent groupthink.

The Captain then takes the critique, revises or defends the plan, and makes the Final Call.

graph TD
    M[Live Match State] --> A(Stats Analyst)
    A -->|Function Calling| D[(Cricket Stats API)]
    D -->|Stats| A
    A -->|Stats Summary| S(Strategist - Captain)
    M --> S
    S -->|Initial Proposal| V(Devil's Advocate)
    V -->|Critique & Risks| S
    S -->|Final Decision| O[Final Output UI]

🗣️ The System Prompts

The secret sauce to getting authentic cricket commentary rather than generic ML jargon was in the System Prompts.

The Captain’s Prompt:

“You are ‘Captain Cool’, an elite IPL match strategist (think MS Dhoni or Rohit Sharma). You will receive the current match state and a data summary from your Stats Analyst. Your task is to make the next tactical decision: e.g., who should bowl the next over, field placement, bringing in the Impact Player, etc. Explain your reasoning in authentic cricket terminology. Be decisive and clear in your proposal.”

The Vice-Captain’s Prompt:

“You are the Devil’s Advocate (Vice-Captain) of an IPL team. Your job is to listen to the Captain’s tactical proposal and find the flaws in it. Look for contrarian angles: what if the pitch is behaving differently? What if the matchups are misleading? What about dew? Suggest a smart alternative or highlight the primary risk of the Captain’s plan. Speak in sharp, analytical cricket terms.”

🏏 Walkthrough: An End-to-End Match Scenario

Let’s look at how the agents handle a classic death-overs scenario:

The Setup:

  • Innings: 2nd Innings (Run Chase)
  • Score: 145/4 (Over 17.5) | Target: 175
  • Matchup: RCB (Batting) vs MI (Bowling)
  • At the Crease: Virat Kohli (55 off 38) & Dinesh Karthik (12 off 8)
  • Bowling Options: Hardik Pandya is finishing his spell. Jasprit Bumrah has 1 over left.
  • Conditions: Wankhede Stadium. Heavy dew is setting in.

(Insert a screenshot of the Streamlit UI Match Config here!)

The Debate Unfolds:

  1. 📊 Stats Analyst: Pulls data via tools. Highlights that Kohli is vulnerable to left-arm pace outside off, but dominates spin. Bumrah has a 7.8 death overs economy and a massive 82.3% yorker accuracy, having dismissed Kohli 4 times. Wankhede has a heavy dew factor which makes gripping the ball difficult.
  2. 🧠 Captain (Initial Plan): Suggests bringing Bumrah in immediately for the 18th over to get Kohli out before he can launch in the 19th and 20th. Keep Pandya out of the attack.
  3. ⚖️ Vice-Captain (Critique): “Hold on skip. With the heavy dew, the ball is going to resemble a bar of soap. If Bumrah bowls the 18th, who bowls the 19th and 20th? We risk saving our weaker bowlers for the final overs when the dew is at its absolute worst. We need Bumrah for the 19th.”
  4. 🏆 Final Call: The Captain agrees with the critique. “Fair point. The dew is a massive factor. We bowl Akash Madhwal for the 18th over, stack the off-side to force Kohli to hit against the spin of the wet ball, and save Bumrah’s yorkers for the crucial 19th over.”

(Insert a screenshot of the Chat UI showing the final decision here!)

🛠️ The Tech Stack & Google Integration

  • Model: gemini-2.5-flash for lightning-fast, multi-turn reasoning.
  • SDK: Google GenAI Python SDK (google-genai).
  • Tool Use: Native Gemini Function Calling hooked into a custom Python dictionary of IPL stats.
  • IDE: Vibe-coded entirely inside Google Antigravity (Google’s agentic IDE).
  • UI & Hosting: Built with Streamlit and deployed live on Streamlit Community Cloud.

🏆 Conclusion

By breaking the problem down into a multi-agent system, the AI was forced to self-correct and consider environmental factors (like dew and bowler workload) that a single zero-shot prompt usually misses.

Check out the live app here: (Insert your Streamlit Cloud URL here!)
Check out the code on GitHub: github.com/VaishnaviBardapure/CaptainCool

Built for the APL GDG Hackathon.

GDGCLOUDPUNE

Learning the Web Platform APIs As If You Built Them Yourself

If you have ever reached for a library to do something the browser already does, you have met the gap this post is about. Need a debounce? You wrote a useEffect. Need to detect when a card scrolls into view? You added a 300 line library. Need offline support? You shrugged and gave up.

The browser has changed. Modern browsers ship a generous, capable platform. Most of the libraries we still install in 2026 were written when the platform was missing the feature. They are not missing anymore.

A senior frontend engineer knows what the browser already gives them, and reaches for it before reaching for npm.

That is the gap this post fills.

What is the web platform, really

Think of the browser as a small operating system that runs in a tab. It has storage, a network stack, threads, scheduling, sensors, sometimes even a file system and Bluetooth. Each capability has an API. Many of them are excellent. Many of them are five lines of code instead of a 12KB dependency.

Two ideas drive the whole thing:

  • The platform is doing more than you think. Capabilities ship every six weeks across all the major browsers.
  • Promises and observers are the shape. Most modern APIs are either awaitable or “watch this thing and call me back”.

That is the whole vibe.

Let’s pretend we are building one

We are not building the platform. We are doing a senior level tour of it. For the running example, we will sprinkle a tiny read later notes app with native APIs as we go. Save notes offline, fetch articles, observe the page, schedule work, broadcast across tabs.

API 1: fetch (and AbortController), the network everyone forgets has features

Every frontend engineer knows fetch. Half of them only use 20% of it.

const res = await fetch("/api/notes", {
  method:      "POST",
  headers:     { "Content-Type": "application/json", Accept: "application/json" },
  body:        JSON.stringify({ title: "Read later" }),
  credentials: "include",        // send cookies on cross origin requests
  cache:       "no-cache",       // override default
  mode:        "cors",           // also "same-origin", "no-cors"
  signal:      ctrl.signal,      // AbortController
});

if (!res.ok) throw new Error(`HTTP ${res.status}`);
const data = await res.json();

The features senior engineers actually use:

AbortController for cancelling

const ctrl = new AbortController();
const timer = setTimeout(() => ctrl.abort(), 5000);   // 5s timeout

try {
  const res = await fetch(url, { signal: ctrl.signal });
} finally {
  clearTimeout(timer);
}

The same AbortController works with addEventListener, with most modern Promise APIs, and with React Query’s queries. Make it your default mental model: any long lived async work should be cancellable.

The shortcut for “fetch with timeout”:

fetch(url, { signal: AbortSignal.timeout(5000) });

Streaming responses

The body is a ReadableStream. You can consume it as it arrives, perfect for AI streaming or large downloads:

const res = await fetch("/api/stream");
const reader = res.body!.pipeThrough(new TextDecoderStream()).getReader();

while (true) {
  const { value, done } = await reader.read();
  if (done) break;
  console.log("chunk:", value);
}

Response and Request are real classes

You can build them, store them, clone them. Service Workers rely on this. The same Response API you receive from fetch is the one you return from a Service Worker.

API 2: Storage, three flavors

The browser has three storage mechanisms. Pick on purpose.

localStorage and sessionStorage

Tiny key value store. Synchronous. Strings only. Good for: small UI state (theme, last opened tab), tiny preferences. Bad for: anything large, anything secret, anything that should sync.

localStorage.setItem("theme", "dark");
localStorage.getItem("theme");
localStorage.removeItem("theme");

sessionStorage is the same, scoped to the tab. Cleared on close.

A senior level rule: never store auth tokens in localStorage. JavaScript can read it, which means every third party script can too.

Cookies

Sent automatically with every same origin request. The browser respects HttpOnly, Secure, SameSite. We covered them in the auth post. Use them for sessions and CSRF tokens.

IndexedDB, the actual database

A real client side database. Asynchronous. Stores anything structurable, including blobs. Indexed for fast queries. Quotas in the megabytes to gigabytes range.

The native API is famously verbose. Use a tiny wrapper like idb-keyval for simple cases or Dexie.js for richer querying.

import { get, set, del } from "idb-keyval";

await set("note:42", { title: "Read later", body: "..." });
const note = await get("note:42");
await del("note:42");

Use IndexedDB for: offline caches, drafts, full text search indexes, large user data, anything you need to survive a refresh and a flaky network. It is the storage that makes serious offline apps possible.

In 2026 there is also an exciting trend: SQLite in the browser, via wasm libraries like sql.js and wa-sqlite, often backed by IndexedDB or the Origin Private File System (OPFS). Real SQL queries on the client. Worth knowing about for offline first apps.

API 3: Service Workers, the engine of offline

A Service Worker is a script that runs in the background, separate from any page, and can intercept network requests for a whole origin. It is how websites become installable apps that work offline.

// app/main.ts
if ("serviceWorker" in navigator) {
  navigator.serviceWorker.register("/sw.js");
}

// public/sw.js
self.addEventListener("install", (e) => {
  e.waitUntil(caches.open("v1").then((c) => c.addAll(["/", "/styles.css", "/app.js"])));
});

self.addEventListener("fetch", (e) => {
  e.respondWith(
    caches.match(e.request).then((cached) => cached || fetch(e.request))
  );
});

What that gives you: the page loads from cache instantly, even offline.

The senior level patterns:

  • Cache first for static assets that change rarely.
  • Network first, cache fallback for HTML.
  • Stale while revalidate for API responses.
  • Use a tool, do not hand roll. Workbox (Google) and Vite PWA Plugin scaffold a sane Service Worker in a few lines. They handle caching strategies, precaching, runtime caching, navigation fallback, and updates.

The full PWA story (offline + installable + push notifications) is a real thing in 2026. Browsers across Mac, Windows, Linux, Android, and even iOS support it. For an app that runs daily, “Add to Home Screen” is real distribution.

API 4: Web Workers, threads for compute

JavaScript runs on a single thread. Long work blocks the page. A Web Worker runs a script on a separate thread, communicating by message passing.

// worker.ts
self.onmessage = (e) => {
  const result = expensiveCompute(e.data);
  self.postMessage(result);
};

// main.ts
const worker = new Worker(new URL("./worker.ts", import.meta.url), { type: "module" });
worker.postMessage(input);
worker.onmessage = (e) => setResult(e.data);

The friendlier modern API: Comlink turns the worker into a proxy you can await:

// worker.ts
import { expose } from "comlink";

expose({
  parseMarkdown(md: string) { return slowParser(md); },
});

// main.ts
import { wrap } from "comlink";
const api = wrap<{ parseMarkdown(md: string): string }>(
  new Worker(new URL("./worker.ts", import.meta.url), { type: "module" })
);
const html = await api.parseMarkdown(text);

Use Web Workers for: heavy parsing (Markdown, syntax highlighting, JSON), image processing, data transforms, anything more than 50ms of compute. The page stays smooth, INP stays under 200ms.

A close cousin: requestIdleCallback runs a function when the browser is idle. Great for non urgent work like analytics flushing.

requestIdleCallback(() => sendQueuedAnalytics(), { timeout: 2000 });

API 5: IntersectionObserver, “tell me when this is on screen”

Before this API, “is the element visible” was solved by listening to scroll and reading layout in a tight loop. It was awful.

Now:

const obs = new IntersectionObserver((entries) => {
  for (const entry of entries) {
    if (entry.isIntersecting) {
      console.log("visible:", entry.target);
    }
  }
}, { rootMargin: "200px" });

document.querySelectorAll(".lazy").forEach((el) => obs.observe(el));

Uses for senior frontends:

  • Lazy load images and components before they are needed.
  • Infinite scroll: observe a sentinel at the bottom of the list.
  • Trigger animations when an element scrolls into view.
  • Track impressions for analytics.

rootMargin lets you start the work early, so the user never sees the loading state.

A close cousin: ResizeObserver fires when an element changes size. Great for charts, tables, and components that need to re-render on layout changes:

new ResizeObserver(([entry]) => {
  drawChart(entry.contentRect.width, entry.contentRect.height);
}).observe(chartEl);

And MutationObserver for “tell me when the DOM changed”. Niche but lifesaving for browser extensions and integrations with markup you do not control.

API 6: BroadcastChannel, talking across tabs

If a user has your app open in three tabs and logs out in one, the others should know. BroadcastChannel posts messages across same origin tabs:

const ch = new BroadcastChannel("auth");
ch.postMessage({ type: "logout" });
ch.onmessage = (e) => { if (e.data.type === "logout") goToLogin(); };

Five lines, no library. Use it for: logout sync, cache invalidation across tabs, “this item just changed” notifications.

For more general cross window coordination, the Web Locks API ensures only one tab does a piece of work at a time:

await navigator.locks.request("sync-notes", async () => {
  await syncNotesWithServer();
});

Other tabs that try to acquire the lock will queue. Brilliant for “only one tab should be syncing”.

API 7: Clipboard, Share, File access, the human integration layer

These are the APIs that make a web app feel native.

Clipboard

await navigator.clipboard.writeText("hello");
const text = await navigator.clipboard.readText();

Modern, async, permission gated. The old document.execCommand("copy") is deprecated. For images and other rich data, use navigator.clipboard.write with ClipboardItem.

Web Share API

if (navigator.share) {
  await navigator.share({
    title: "Mochi's Blog",
    text:  "A great post",
    url:   "https://example.com/post",
  });
}

On mobile, this opens the system share sheet. On desktop, it falls back gracefully. One line replaces a custom share modal.

File System Access

const handle = await window.showSaveFilePicker({
  suggestedName: "notes.json",
  types: [{ description: "JSON", accept: { "application/json": [".json"] } }],
});
const writable = await handle.createWritable();
await writable.write(JSON.stringify(data));
await writable.close();

Real “save as” dialog, real file. Limited to Chromium browsers in 2026 but excellent for productivity apps.

The simpler alternative for downloads is the venerable <a download>:

const url = URL.createObjectURL(new Blob([data], { type: "application/json" }));
const a = Object.assign(document.createElement("a"), { href: url, download: "notes.json" });
a.click();
URL.revokeObjectURL(url);

API 8: View Transitions, navigation feels smooth

A genuinely magical API. Animate between any two DOM states with one line.

document.startViewTransition(() => {
  // any DOM mutation here
  setTheme("dark");
});

The browser captures a snapshot before, applies your changes, captures after, and animates between them. With CSS view-transition-name on shared elements, you get FLIP-style transitions for free.

In 2026, the cross document version (@view-transition) lets multi page apps animate between full page navigations as smoothly as SPAs. This is one of the reasons SPAs are no longer the only path to good UX.

API 9: Scheduler, Idle Detection, and modern timing

The scheduler family of APIs, increasingly supported, gives you fine grained control over priority:

// new in 2024+ browsers
await scheduler.yield();        // give the browser a frame
scheduler.postTask(work, { priority: "background" });

If scheduler.yield() is not available, the polyfill is a setTimeout(0) plus a microtask check.

The classic timing tools still matter:

  • requestAnimationFrame for any visual update tied to the next frame. Always use it for animations driven by JS.
  • performance.now() for high resolution timing.
  • PerformanceObserver to subscribe to LCP, INP, CLS, long tasks, navigation timing, resource timing. This is what web-vitals is built on.
new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) console.log(entry);
}).observe({ entryTypes: ["longtask", "layout-shift", "largest-contentful-paint"] });

Drop into devtools when you want to see what is actually slow.

API 10: Notifications, Push, Background Sync

For “this app feels real” features:

  • Notifications: new Notification("Hi") with permission. Used as the visual layer for Push.
  • Push: a Service Worker can receive pushes from your server even when the page is closed. Requires the user to grant permission and your server to send via the Web Push protocol.
  • Background Sync: the browser will retry a queued task when the user comes back online. Perfect for “send this comment whenever there is connectivity”.

These are senior level features. Only enable them where they pay off. Most users have notification fatigue, so ask politely or not at all.

API 11: The “knows about the user” APIs (use sparingly)

The platform has a long tail of “tell me about the device” APIs. Senior engineers know they exist and use them with care.

  • matchMedia("(prefers-color-scheme: dark)").matches for theme detection.
  • matchMedia("(prefers-reduced-motion: reduce)").matches to respect motion preferences.
  • navigator.connection (Network Information API) for adaptive loading. Slow 3G? Send fewer images.
  • document.visibilityState to pause work when the tab is hidden.
  • navigator.locks (already mentioned).
  • Battery, Bluetooth, USB, Serial, Geolocation: each is a permissioned API, useful for very specific apps.

The principle: respect the user, ask before you peek. Permission prompts kill conversion if used carelessly.

API 12: Modern selection, drag, paste, undo

A handful of small APIs that delete entire libraries:

  • getSelection() for the current text selection.
  • document.execCommand is deprecated. For rich text, use contenteditable plus a library like TipTap or Lexical.
  • InputEvent with inputType (“insertText”, “deleteContentBackward”, etc.) for fine grained input handling.
  • HTMLDialogElement.showModal() for native modals (we covered this in the HTML post).
  • <details> and <summary> for native disclosures, no JS needed.
  • <input type="search">, type="date", type="time", type="color" for native pickers on mobile.

A surprising amount of “I need a library for this” turns into “the browser already does it” once you check first.

API 13: WebRTC, WebSockets, SSE, beyond request/response

Three transports for real time:

  • WebSockets for bidirectional persistent connections. We mentioned this in the HTTP post.
  • Server-Sent Events (EventSource) for one way streaming from server to client. Simpler than WebSockets, plays nicely with HTTP infrastructure.
  • WebRTC for peer to peer audio, video, and data. The transport behind every browser based video call.

Most apps need SSE for live updates and never touch the others. Reach for them when you have a specific need (real time collaboration, video, gaming).

A peek under the hood

What really happens when you call a platform API:

  1. JavaScript calls into the browser’s C++ implementation.
  2. The browser does the work (often on another thread or in another process).
  3. The result comes back as a Promise resolution, an event, or a callback.
  4. Your code runs in the JS event loop.

Two consequences for senior engineers:

  • Native APIs are usually faster than userland equivalents because the heavy lifting happens off the main thread.
  • Browser support varies. Use caniuse.com before committing. For features you must have everywhere, polyfills exist. For features that gracefully degrade, feature detect with if ("foo" in window) and ship the better experience to capable browsers.

Tiny tips that will save you later

  • Read MDN. It is the single best web platform documentation, and it is free.
  • Use caniuse.com before adopting any platform API. Browser share matters.
  • Feature detect, do not user agent sniff. if ("share" in navigator) is the right check.
  • Cancel everything. AbortController works with fetch, with addEventListener, with most modern APIs.
  • Use IntersectionObserver instead of scroll listeners.
  • Use ResizeObserver instead of window resize listeners when you only care about an element.
  • Use BroadcastChannel for cross tab communication.
  • Use idb-keyval or Dexie for IndexedDB unless you really enjoy callbacks.
  • Use Workbox or Vite PWA for Service Workers.
  • Use view-transition for navigation animations. Free polish.
  • Respect prefers-reduced-motion and prefers-color-scheme. They are one query away.
  • Keep platform APIs small. Wrap each in a tiny module so the call sites do not get noisy.

Wrapping up

So that is the whole story. The web platform stopped being a dumb document viewer years ago. Modern browsers ship a small operating system: a network stack with cancellation and streaming, three storage tiers, real threads, real schedulers, observers for everything that used to need a polling loop, broadcast channels for cross tab life, file system access, share sheets, push notifications, and beautiful view transitions.

A senior frontend engineer treats these as the first toolkit, not the last resort. The libraries on npm exist because the platform was missing the feature. Many features have shipped. Many libraries can come out of your package.json if you check what the browser already does.

Once that map is in your head, you write less code, ship smaller bundles, and build apps that feel like the device they run on.

Happy platforming, and may your dependency list stay short.

Captain Cool AI: A Multi-Agent IPL Tactical Strategist Built Powered by Google Gemini

Cricket is often called a batter’s game, but in T20 cricket, it is equally a captain’s game. One tactical mistake—bowling spin into heavy dew, using your best death bowler too early, or making the wrong substitution—can completely change the outcome of a match. That is exactly what made this Google Gemini Hackathon problem statement so interesting to me.

The challenge was to build an agentic AI system that behaves like a virtual IPL captain, capable of making live tactical decisions in high-pressure match situations. Instead of building a simple chatbot that produces generic cricket advice, I wanted to create something that actually thinks through decisions the way a real dugout might. That idea became Captain Cool AI.

Captain Cool AI is a multi-agent tactical cricket strategist built entirely on Google’s AI ecosystem using the google-genai Python SDK, Gemini 2.5 Flash, Streamlit, and Python orchestration. The goal was to simulate how a captain, analyst, and tactical advisor might collectively arrive at the next big decision during a live match.

What made this project especially exciting was the multi-agent design. A single AI prompt might simply say, “Use your best bowler now,” but cricket strategy is rarely that straightforward. Real decision-making involves competing perspectives. One person looks at the numbers, another thinks aggressively, someone else challenges the logic, and only then does the final call emerge. I wanted to recreate that process.

To achieve this, I designed four specialized Gemini-powered agents. The Stats Analyst focuses entirely on match intelligence such as venue conditions, batter vs bowler matchups, chase pressure, and dew factor. The Strategist acts like the captain, making the first tactical recommendation. The Devil’s Advocate exists to challenge that decision, forcing the system to consider risks and alternative scenarios. Finally, the Commentator transforms the outcome into cricket-style explainability so the reasoning feels intuitive and engaging rather than technical.

To make the system more grounded, I added supporting tools instead of relying only on prompting. A cricket tactical engine provides matchup heuristics and venue intelligence, a win probability engine adds confidence scoring and counterfactual analysis, and a live weather API helps factor in environmental conditions like dew, which can dramatically affect death-over planning. This combination made the recommendations feel much more realistic and trustworthy.

For the interface, I built a Streamlit dashboard where users can enter the current match state—innings, score, wickets, over, batting and bowling teams, target, required run rate, striker, venue, dew factor, and Impact Player availability. Once submitted, the system runs the multi-agent debate and produces a tactical recommendation along with reasoning, confidence, and alternative scenario analysis.

One of my favorite simulations involved CSK chasing 195 at Wankhede, with 154/4 on the board, MS Dhoni on strike, and heavy dew expected. The Stats Analyst highlighted historical Bumrah vs Dhoni matchups and the impact of weather conditions. The Strategist initially recommended attacking with Bumrah immediately. The Devil’s Advocate pushed back, arguing that using Bumrah too early could create greater risk later in the innings. The Strategist then revised the decision, saving Bumrah for a more decisive over and adjusting the tactical approach. That visible shift in reasoning was exactly the kind of behavior I wanted this system to demonstrate.

Building this project also came with practical lessons. Multi-agent systems produce much richer reasoning than single-shot prompting, but they also introduce latency and quota challenges because each decision involves multiple model calls. Prompt control became critical because early outputs were too verbose for a live demo. Adding deterministic tools significantly improved trust, since recommendations were grounded in actual logic instead of pure language generation.

This project gave me a much better understanding of agentic AI design—not just how to generate responses, but how to orchestrate structured reasoning between specialized agents. That was the most exciting part of the entire build.

Architecture Diagram

graph TD
UI[Streamlit UI] --> O[Orchestrator]
O --> A[Stats Analyst]
A --> S[Strategist]
S --> D[Devil's Advocate]
D --> R[Strategist Revision]
R --> C[Commentator]
C --> UI

The full source code is available here:
GitHub Repository: https://github.com/DhanashriQAEngineer/captain-cool-ai

This was a genuinely fun hackathon build, and watching AI agents debate how to bowl to MS Dhoni in a death-over chase was far more entertaining than I expected.

Special tag: @antrixsh_gupta

Captain Cool — A Multi-Agent IPL Strategist Built on Gemini

Captain Cool: A Multi-Agent IPL Strategist Built on Gemini

Cricket is not just about big hits and wickets. It is also about smart decisions. Every over, every bowling change, and every batting order move can change the result of the match. That is the idea behind Captain Cool — an AI-powered IPL strategist built using the Google Gemini stack.

What is Captain Cool?

Captain Cool is a multi-agent AI system designed for IPL match strategy analysis. Instead of using one chatbot for everything, the project uses three different AI agents that debate with each other before making a final decision.

The system can analyze live match situations and suggest:

  • Which bowler should bowl next
  • When to use the Impact Player
  • Which batter should come in next
  • Tactical changes based on pitch and match conditions

The best part is that the AI explains its decisions in cricket language, making it feel like a real team discussion.

The Three AI Agents

1. Numbers

Numbers is the data expert. It focuses only on statistics and player matchups. It does not make decisions but provides useful insights using historical data.

Example:

  • Strike rates
  • Bowling economy
  • Player performance under pressure

2. Captain Cool

This agent is inspired by MS Dhoni and acts like a calm IPL captain. It studies the match situation and makes the tactical decision. It also uses live web search to understand current conditions such as pitch reports and dew factor.

3. Ravi

Ravi acts like a commentator and devil’s advocate. Inspired by Ravi Shastri, it questions the captain’s strategy and highlights risks using win probability analysis.

This debate between agents creates more realistic and balanced decisions.

How the Debate Works

The workflow is simple:

  1. Numbers provides statistics
  2. Captain Cool suggests a strategy
  3. Ravi challenges the decision
  4. Captain Cool revises the final plan

This approach makes the system more intelligent than a normal chatbot because every decision gets reviewed before becoming final.

Example Match Scenario

Imagine this IPL situation:

  • CSK chasing 192 against MI
  • Score: 148/4 after 16 overs
  • Dew factor is high

The agents start debating:

  • Numbers shares player statistics
  • Captain Cool suggests bowling strategy
  • Ravi questions the risk
  • Final decision gets adjusted

This creates an experience similar to a real IPL dressing room discussion.

Technology Used

The project is built using modern AI and web technologies:

  • Gemini 2.5 Pro
  • Gemini 2.5 Flash
  • Next.js 16
  • React 19
  • Tailwind CSS
  • Web Speech API

The project was created during the Agentic Premier League hackathon using the Google Gemini ecosystem.

Why This Project is Interesting

Most AI chatbots use a single model for all tasks. Captain Cool takes a different approach by dividing responsibilities among multiple AI agents. Each agent has:

  • A unique personality
  • A dedicated role
  • Different tools and responsibilities

This makes the system more realistic, scalable, and engaging.

Final Thoughts

Captain Cool shows how AI can be used creatively in sports strategy. Instead of just answering questions, the AI agents debate, challenge each other, and collaborate before making decisions.

For cricket fans and developers, this project is a great example of how multi-agent AI systems can create smarter and more interactive applications. Whether it is IPL strategy, live match analysis, or cricket discussions, projects like Captain Cool show the future of AI-powered sports intelligence.

Captain Cool: I Built a Multi-Agent IPL Strategist with Google Gemini 2.5 Flash and ADK

The Idea
What if an AI could think like MS Dhoni in the 18th over of a chase?
That was the question behind Captain Cool — a multi-agent AI system that acts as a virtual IPL captain, making real tactical decisions the way Dhoni, Rohit, or Hardik would. You give it the match state. It gives you the next decision, the reasoning, and the internal debate that led there.
This was built for the Google Gemini Hackathon in a single session using Google Antigravity, Gemini 2.5 Flash, and the Agent Development Kit (ADK).
🔗 Live Demo: https://captain-cool-ruddy.vercel.app/
🔗 GitHub: https://github.com/CodeCatalyst-07/captain_cool

The Problem
IPL captaincy is one of the most high-stakes real-time decision problems in sports. Every over, a captain must answer:

Who bowls next — and against which batter?
Should I use my Impact Player now or save them?
Is dew going to affect my death-over plan?
Do I attack or protect wickets here?

Current AI cricket tools give you stats. None of them reason like a captain. I wanted to build something that does.

Architecture Overview
Captain Cool is a 4-agent system built on Google ADK. Each agent has a distinct role, its own system prompt, and runs as a separate LlmAgent instance powered by Gemini 2.5 Flash.
USER INPUT (Match State)
↓
┌─────────────────────┐
│ STATS ANALYST │ → Fetches live data, weather, builds tactical picture
└─────────────────────┘
↓
┌─────────────────────┐
│ STRATEGIST │ → Proposes next decision in captain-speak
└─────────────────────┘
↓
┌─────────────────────┐
│ DEVIL’S ADVOCATE │ → Raises 2 hard objections using cricket analytics
└─────────────────────┘
↓
┌─────────────────────┐
│ STRATEGIST │ → Defends or revises based on challenge
└─────────────────────┘
↓
┌─────────────────────┐
│ COMMENTATOR │ → Narrates final decision like Harsha + Shastri
└─────────────────────┘
↓
FINAL DECISION + WIN PROBABILITY BEFORE vs AFTER
This is a genuine multi-turn debate loop — not a single prompt wearing four hats. Each agent runs independently with its own session, its own system prompt, and passes context forward to the next round.

The Four Agents

  1. Stats Analyst 📊
    Role: Data gatherer and tactical picture builder
    This agent is equipped with two function-calling tools:

get_live_cricket_state() — calls CricketData.org API for live match data
get_pitch_weather() — calls OpenWeatherMap for dew risk and humidity at the venue

It outputs a structured JSON summary covering phase context (powerplay/middle/death), key matchups, bowler overs remaining, weather conditions, and a batting depth rating from 0 to 1.
System prompt excerpt:

“You are a cricket data analyst. Your job is to fetch live match data and build a precise tactical picture. Always output valid JSON with keys: phase, key_matchups, bowlers_remaining, batting_depth_rating, weather_summary, run_rate_context.”

  1. Strategist 🏏
    Role: The captain — proposes the next tactical decision
    This is the core decision-making agent. It has the instinct of Dhoni, Rohit, and Hardik combined. It receives the Stats Analyst’s output and proposes a concrete plan covering the next bowler, field setup, Impact Player usage, and batting approach.
    It uses compute_win_probability() as a tool to ground its decisions in real numbers.
    Output format is always: DECISION / RATIONALE / FIELD SETUP / CONTINGENCY
    System prompt excerpt:

“You are an IPL T20 captain with the combined instinct of MS Dhoni, Rohit Sharma, and Hardik Pandya. Propose the next tactical decision. Use cricket language only — no data science jargon. Structure your output as DECISION, RATIONALE, FIELD SETUP, CONTINGENCY.”

  1. Devil’s Advocate 😈
    Role: The critic — challenges every proposal
    This agent receives the Strategist’s proposal and must raise exactly 2 specific objections using phase analytics, matchup data, dew conditions, or boundary dimension logic. It must either force a revision or accept the defense with a clear verdict: VERDICT: REVISION REQUIRED or VERDICT: ACCEPTED.
    System prompt excerpt:

“You are a ruthless IPL data analyst. Your job is to challenge every strategy. Raise exactly 2 objections using phase-based analytics, matchup data, dew conditions, or bowling economy. End with VERDICT: REVISION REQUIRED or VERDICT: ACCEPTED.”

  1. Commentator 🎙️
    Role: Narrates the final decision
    This agent takes the entire debate transcript and writes one punchy paragraph of cricket commentary in the voice of Harsha Bhogle and Ravi Shastri combined. Zero data science language. Pure cricket emotion.
    System prompt excerpt:

“You are Harsha Bhogle and Ravi Shastri combined. Read the full debate transcript and narrate the final decision in one paragraph. Be emotional, technical, and vivid. Never use ML or data science language.”

The Tools
Three Gemini function-calling tools power the system:

  1. get_live_cricket_state(match_id)
    Calls CricketData.org free API. Returns current score, overs, batsmen, bowler, and phase context.
  2. get_pitch_weather(venue)
    Calls OpenWeatherMap free API. Extracts city from venue string automatically (“Wankhede Stadium Mumbai” → “Mumbai”). Returns temperature, humidity, and dew risk (high/medium/low).
  3. compute_win_probability(target, runs, wickets, overs, dew_risk, batting_depth_rating)
    Pure local calculation. Base 0.5 with adjustments for required run rate brackets, wickets in hand, dew factor (+0.05 if high), and batting depth. Returns win probability as a float clamped between 0.0 and 1.0.

The Debate Loop
This is the heart of the system. Here’s the actual flow in orchestrator.py:
python# Round 1: Stats Analyst builds the picture
stats_summary = await _run_agent_turn(stats_analyst_agent, match_state_prompt)

Round 2: Strategist proposes a decision

initial_proposal = await _run_agent_turn(strategist_agent, stats_summary)

Round 3: Devil’s Advocate challenges it

devils_challenge = await _run_agent_turn(devils_advocate_agent, initial_proposal)

Round 4: Strategist defends or revises

final_decision = await _run_agent_turn(strategist_agent,
f”Prior proposal: {initial_proposal}nChallenge: {devils_challenge}nNow defend or revise.”)

Round 5: Commentator narrates

commentary = await _run_agent_turn(commentator_agent,
f”Full debate: {initial_proposal}n{devils_challenge}n{final_decision}”)
Each agent gets its own session_id with a UUID suffix for concurrency safety. Context is threaded as a cumulative transcript — no shared session state needed.

A Real Match Scenario
Input:

Innings: 2, Over: 14, Score: 134/4
MI batting, CSK bowling
Bumrah: 2 overs remaining, Chahar: 3 overs remaining
Venue: Wankhede Stadium Mumbai
Target: 185, Dew Factor: ON

Stats Analyst output:
json{
“phase”: “death”,
“bowlers_remaining”: {“Bumrah”: 2, “Chahar”: 3},
“batting_depth_rating”: 0.7,
“weather_summary”: “haze with medium dew risk”,
}
Strategist’s initial proposal:

“Bring Bumrah on immediately. With a batting depth rating of 0.7 and 36 balls left, this is the moment to strike. His yorker is the only answer on a dewy Wankhede surface.”

Devil’s Advocate:

“OBJECTION 1: Medium dew risk means the ball is already skidding — Bumrah’s straight yorker becomes a half-volley. Wide yorkers are safer.
OBJECTION 2: Burning both Bumrah overs now leaves overs 18 and 20 uncovered if this partnership holds.”

Strategist’s final decision:

“DECISION: Bring Bumrah on but instruct wide yorkers and back-of-length cutters only — no straight full tosses.
RATIONALE: The DevilsAdvocate is right about dew. Adjust the length, not the bowler.
FIELD SETUP: Two slips, a gully, deep square leg, fine leg, deep cover.
CONTINGENCY: If no wicket in this over, hold Bumrah’s second spell for over 19.”

Commentary:

“And there it is — Dhoni’s ghost walking the boundary! The skipper backs his gun, but with a tactical tweak that only a true student of the game would make. No straight yorkers tonight, Harsha — the dew has stolen that weapon. But Bumrah with wide yorkers and cutters? That’s still a nightmare for any batter alive!”

Win Probability: 62% → 66% (+4% strategy impact)

Tech Stack
LayerTechnologyAI ModelsGoogle Gemini 2.5 FlashAgent FrameworkGoogle ADK (Agent Development Kit)Function Callinggoogle-genai SDKBackendFastAPI + UvicornFrontendReact 18 + Vite + Tailwind CSSCricket DataCricketData.org (free tier)WeatherOpenWeatherMap (free tier)Frontend DeployVercelBackend DeployRender.com

Project Structure
captain-cool/
├── captain_cool/
│ ├── config/settings.py # API keys + constants
│ ├── tools/
│ │ ├── cricket_api.py # Live cricket data tool
│ │ ├── weather_tool.py # Dew/weather tool
│ │ └── win_probability.py # Win probability calculator
│ ├── agents/
│ │ ├── stats_analyst.py # Agent 1
│ │ ├── strategist.py # Agent 2
│ │ ├── devils_advocate.py # Agent 3
│ │ └── commentator.py # Agent 4
│ ├── orchestrator.py # Debate loop
│ └── api/server.py # FastAPI backend
├── frontend/
│ ├── src/
│ │ ├── pages/
│ │ │ ├── LandingPage.jsx # Hero + agents showcase
│ │ │ ├── AnalyzePage.jsx # Form + results
│ │ │ └── HowItWorksPage.jsx # Architecture diagram
│ │ └── components/
│ │ ├── MatchForm.jsx
│ │ ├── ResultCards.jsx
│ │ ├── WinProbBar.jsx
│ │ └── LoadingSpinner.jsx
└── tests/
├── test_tools.py # 25 tests
└── test_agents.py # 13 tests

What I Learned

  1. Multi-agent debate is genuinely better than single-agent prompting.
    The Devil’s Advocate consistently caught things the Strategist missed — especially dew factor implications and bowling resource management. The final decisions were measurably more nuanced after the challenge round.
  2. ADK’s session isolation matters.
    Giving each agent its own session_id with a UUID suffix prevented context bleed between rounds. Without this, the Commentator would sometimes “remember” the Strategist’s early proposals and contradict the final decision.
  3. Free APIs are enough for a hackathon.
    CricketData.org’s free tier (100 calls/day) and OpenWeatherMap’s free tier (1000 calls/day) were more than sufficient for the demo. The agents handle API failures gracefully and still produce quality output from the manually entered match state.
  4. Gemini 2.5 Flash understands cricket.
    I didn’t need to explain IPL rules, Impact Player mechanics, or death-over conventions in detail. The model had strong prior knowledge of T20 cricket strategy that made the system prompts much shorter than expected.

What’s Next

Real-time mode: Paste a Cricbuzz URL and the system scrapes live state automatically using Gemini’s URL context tool
Voice mode: Web Speech API input + Gemini Live API output so the captain literally talks back
Memory across overs: Gemini context caching for multi-over strategic continuity
Confidence scores: “If you’d bowled X instead, win probability drops 8%”

AI Video Scripts That Actually Convert: The Workflow Top Creators Use

Your AI is writing scripts that rank nowhere because it’s optimizing for grammatical perfection instead of viewer retention. Here’s how top creators are actually using models to generate 10x more content without sounding robotic.

I’ve watched creators spend $500/month on AI tools and still post videos that flatline at 200 views. The problem isn’t the tools. It’s how they’re prompting them.

Most creators copy-paste a generic prompt like “write a YouTube script about [topic]” and wonder why the output sounds like a corporate press release. Meanwhile, a small group of creators is generating 15–20 scripts per week, maintaining 65%+ average view duration, and doing it with a workflow most people aren’t discussing publicly.

The Script Generation Trap: Why AI-Written Videos Feel Soulless

YouTube data from creator briefings and retention analytics from channels like Think Media and MKBHD shows that average view duration drops roughly 23% when the first 30 seconds feel scripted and impersonal.

That number should concern you.

The trap works like this: you ask an AI to write a script, it produces something grammatically flawless, topically accurate, and completely devoid of personality. It starts with “In this video, we’re going to explore…” and your viewer’s thumb is already moving.

The problem is structural. Most AI models optimize for coherence and completeness—great for research reports, terrible for video hooks.

Here’s what happened: Marcus, who runs a 180K subscriber personal finance channel, switched to pure AI scripts last year. His click-through rate held at 8.2%, but average view duration collapsed from 54% to 31% in six weeks. Viewers clicked, heard the robotic cadence in the first 10 seconds, and left. CTR is vanity. Retention is revenue.

The fix isn’t abandoning AI. It’s training the model on how you think, not just what you want to say.

The Personality Injection Framework: Embedding Your Voice Without Manual Rewrites

Top creators scaling production aren’t using different tools. They’re using a three-layer prompting system called Voice DNA.

Layer 1: The Sample Bank

Before generating any script, feed the model 5–7 transcript excerpts from your best-performing videos—specifically moments that got the most comments or timestamps in comments. You’re not asking the AI to copy these. You’re establishing a behavioral reference.

Here’s the prompt structure:

“The following are excerpts from my highest-retention videos. Analyze the sentence length patterns, the way I transition between ideas, the specific types of analogies I use, and my cadence when making key points. [PASTE TRANSCRIPTS]. Now write a script about [TOPIC] that maintains these structural patterns while covering these beats: [BEATS].”

The difference is immediate. Priya, who runs a 90K subscriber productivity tools channel, tested identical topics with and without the sample bank layer. Scripts with her Voice DNA prompt averaged 61% view duration. Without it: 38%.

Layer 2: The Contrarian Flag

Your best videos almost certainly contain a moment where you said something surprising. The comment section lights up. These moments are algorithmically valuable because they trigger emotional engagement.

Add this to every prompt: “Include one counterintuitive claim in the first 90 seconds that challenges conventional wisdom about [TOPIC]. This should feel like something I personally discovered, not a generic hot take.”

Being mildly controversial is far less damaging than being forgettable.

Layer 3: The Unfinished Sentence Technique

Instruct the AI to leave 3–4 moments marked as [YOUR RIFF HERE]. These are 10-second gaps where you insert something spontaneous when recording.

This breaks the robotic cadence pattern that both algorithms and human ears detect, while preserving the authentic improvisation that longtime viewers expect. The script becomes scaffolding, not a cage.

Platform-Specific Optimization: Structure That Actually Performs

A script that works on YouTube long-form will kill your TikTok account.

YouTube Long-Form (8–20 minutes)

The structure that’s performing now is the Problem-Proof-System loop. Open with a specific problem (“I was spending 4 hours per week editing” beats “editing is time-consuming”). Provide proof you’ve solved it (a number, result, or screenshot). Deliver the system in 90-second chunks.

Key prompt addition: “Structure each section to end with a forward-looking sentence implying the next section is necessary.” This creates the binge effect.

Channels doing this well—Matteo French on productivity, Ali Abdaal’s team on study techniques—see 55–70% average view duration on videos over 12 minutes.

YouTube Shorts and TikTok (Under 60 seconds)

These reward pattern interrupts, not information delivery: Unexpected hook (0–3s) → Bold claim (3–8s) → Fastest proof (8–25s) → Restatement with twist (25–45s) → CTA that feels like a new story (45–60s).

For short-form, add: “The script should feel unfinished, like I’m sharing a discovery I just made, not teaching a lesson I’ve known for years.”

Instagram Reels (The Hybrid)

Reels viewers tolerate more structure than TikTok but have less patience than YouTube Shorts. Use: 3-second visual hook, one bold sentence that works on mute (60% of Reels are watched muted initially), and a comment-bait ending.

Prompt your AI: “End with a question that has no obvious right answer but every viewer has an opinion on.”

Real Workflow: The Tool Stack Top Creators Use

Step 1: Claude for Structure

Claude (3.5 or later) handles structural architecture better than other models because it maintains longer contextual coherence. When building 15-minute scripts with multiple segments, it doesn’t lose the thread.

Use Claude for macro-level outline, transition logic, and argument structure. Feed it your Voice DNA samples first. Expect 2–3 iterations on structure.

Step 2: Custom GPT or Fine-Tuned Model for Tone

This is where personality lives and where most creators skip. Build a custom GPT trained on your transcript library. It handles sentence-level tone adjustments—how you phrase things, your filler-word patterns, your joke structure.

Workflow: get structure from Claude, paste into your custom GPT with: “Rewrite this in my voice, maintaining all structural beats.” This two-model workflow adds 8 minutes and produces noticeably more authentic output.

Step 3: Multimodal Validation

Run the script through text-to-speech (ElevenLabs with voice clone or native TikTok reader) before recording. Listen for moments that sound wrong—these are your [YOUR RIFF HERE] markers.

One 400K tech channel creator said this step cut re-record rate from 40% to under 10%.

Time breakdown: Full workflow—Voice DNA prompt, Claude structure, custom GPT tone pass, validation—takes 35–45 minutes per script. Manual scripting typically takes 2–3 hours. You’re faster with a higher quality floor.

Avoiding the Detection Penalty: What YouTube Actually Cares About

YouTube isn’t uniformly penalizing AI-generated content. They’re penalizing low-effort, undifferentiated content—and AI scripts are the fastest way to produce that at scale.

YouTube tracks “satisfaction signals”: comment sentiment, share rate, full vs. partial view ratio, and subscription conversions after watching. AI-generated scripts tend to produce low share rates and near-zero subscribe conversions because they don’t create the parasocial connection that drives growth.

The signal YouTube reads: if 1,000 people watch and none subscribe, the algorithm infers that while the title/thumbnail worked, the content didn’t create lasting interest. Distribution gets throttled accordingly.

Your AI scripts need moments that feel unreplicable—specific personal details, real numbers, opinions clearly yours. These aren’t decoration. They’re conversion events.

Add this to every prompt: “At three points, flag a [PERSONAL INSERT] marker where I should add a specific detail, anecdote, or real data point from my own experience. Place these at moments of highest emotional or logical weight.”

This also addresses detection tool concerns. Services like Originality.ai are used by brand partners and MCNs. Make your content genuinely personal, not just structurally varied.

Winning creators treat the model as a research assistant and structural partner, not a ghostwriter. They’re in the video. Their opinions are in the video. The AI helped them get there faster.

Action This Week

Don’t overhaul your entire workflow. Do this instead:

Take your three best-performing videos (by average view duration), run them through transcription (Descript or Otter.ai), and save transcripts in one document. That’s your Voice DNA sample bank.

Next time you generate a script, start with: “Here are transcripts from my three highest-retention videos. Before writing anything, identify five specific patterns: sentence length, transition style, analogy type, question placement, and emotional cadence. Then use those patterns for a script about [TOPIC].”

Run that prompt once. Compare the output to what you’ve been getting.

The money on the table isn’t views. It’s the compounding effect of a 20-percentage-point improvement in average view duration, applied across 52 weeks of content. That’s the gap between a channel that stagnates at 50K subscribers and one that hits 500K.

Your voice is the asset. AI is the production infrastructure. Understanding that distinction separates creators building something lasting.

Follow for more practical AI and productivity content.

OpenAgent for Obsidian: Local-Only Grounded Research with Gemma 4

This is a submission for the Gemma 4 Challenge: Build with Gemma 4

What I Built

Obsidian is a local-first notes app where your notes live as Markdown files on your machine.

I built a grounded-research mode for an Obsidian AI plugin called OpenAgent. The users I care about most are the ones who can’t paste their notes into a cloud LLM: a lawyer with client material, a doctor reviewing patient records, a researcher with a proprietary corpus, a founder with confidential strategy notes. These users already live in vaults like Obsidian, and today they have nowhere good to run AI over their actual work.

OpenAgent’s grounded research mode runs entirely against a local OpenAI-compatible endpoint — MLX on Apple silicon by default — and adds something single-shot note chat doesn’t: every claim it surfaces has been verified against the cited note text. Instead of returning one ungrounded answer, it runs a retriever -> synthesizer -> verifier pipeline. Retrieval pulls candidate notes. Synthesis produces structured claims grounded in those notes. Verification checks whether each cited quote actually supports the claim. Only the survivors reach the user as verified facts. Nothing leaves the machine.

The Gemma 4 angle is what makes this practical on a personal laptop. Different stages of the pipeline want different model shapes:

  • Gemma 4 E4B for retrieval, where speed matters more than reasoning depth
  • Gemma 4 31B Dense for synthesis, where multi-note grounded reasoning benefits from the strongest model in the stack
  • Gemma 4 26B A4B for verification, where repeated structured support checks need to be cheap to run

Same vault, same local endpoint, three Gemma 4 sizes coordinated through a single OpenAI-compatible API. That orchestration is the core design decision — it turns a private vault into a real multi-step agentic workflow without ever uploading the data.

To measure whether verification actually helps, I built a live end-to-end evaluation against a labeled Nobel Physics corpus — 24 queries with expected claims and source quotes. The grounded path reduced hallucination rate from 54.2% to 46.3%, a 7.8-point improvement. That delta is a conservative lower bound: the benchmark scores quote wording strictly, so some factually correct claims still get penalized. The verifier correctly handles hard cases like the false-premise Rutherford prize query by correcting the record to Chemistry instead of validating the Physics premise, and it grounds queries such as Bardeen winning twice, Lawrence Bragg as the youngest physics laureate, Chadwick’s neutron discovery, and Rontgen as the first Physics Nobel recipient on the expected notes.

Demo

Code

Repository: https://github.com/nikitaclicks/obsidian-openagent

Helpful links:

  • Hackathon overview: hackathon/README.md
  • Final results: hackathon/RESULTS.md

How I Used Gemma 4

I used Gemma 4 as a staged local system rather than a single model doing everything.

  • Gemma 4 E4B handles retrieval because it is fast enough to scan candidate notes and summarize likely evidence without making the workflow feel heavy.
  • Gemma 4 31B Dense handles synthesis because multi-note grounded answering and structured claim generation benefit from stronger reasoning.
  • Gemma 4 26B A4B handles verification because the verifier needs to do repeated structured support checks cheaply and locally.

This orchestration is the main design choice in the project. Different Gemma 4 model sizes do different jobs over the same local user data, which makes local-only grounded research and agentic note workflows practical on a personal machine.

How I Used Gemma 4

The PACELC Theorem — in a few words

captionless image

The PACELC theorem (often pronounced as pack-else) was proposed by Daniel Abadi in 2010 as an extension to the CAP theorem. While the CAP theorem focuses only on the behavior of a distributed system during a network partition, the PACELC theorem also pays lots of attention to a different state of a system – a state without any network partitions.

Similarly as we did with the CAP theorem, we won’t just quote it and call it a day, instead, we will try to devise it ourselves from the ground up.

But first, we should agree on the terminology! Two terms will be important for us today:

  • Latency — the PACELC theorem operates with the Latency as a (client-observed) duration of a write operation. The longer a client would have to wait until a write operation he has initiated completed — the higher the latency would be. This is not only the duration of the write operation on a single node, but the overall duration of the write operation as observed by the client. For clarity, the network round-trip time between the client and the system is not included in the latency.
  • Consistency — while the CAP theorem operates only with the strong consistency (linearizability), for the PACELC theorem the consistency is a spectrum: it might vary from the strong consistency to the eventual consistency. For today’s discussion, we will consider the consistency as the degree to which all nodes in the system have the same data after a write operation completes, ranging from 1 (only entry node has written the new data) to N (all nodes have written the new data).

Setting up the stage

We are going to use a simple key-value store as a model for our thought experiment. This distributed system would:

  • be able to execute only one operation — write;
  • consist of N nodes (obviously, at least two nodes — otherwise it won’t be a distributed system);
  • accept and execute write operation requests on each node;
  • store the whole data set on each node (i.e., replicate all writes to all nodes).

The overall design of this toy key-value store would look like this:

captionless image

For illustration, here’s a simple API definition for such a system:

We won’t focus on data read operations today, but we will assume that the system is able to read data as well.

Maximizing consistency

At first, we will try to ensure the strong consistency for our system. This means that a write operation should be considered completed only when all nodes have acknowledged it. Therefore, the write operation could only complete when the data change has been replicated to all nodes.

captionless image

This design would ensure that data consistency is maximized — once a write operation is completed, all nodes would have the same data. However, this comes at a cost of increased latency — the write operation would have to wait for the slowest node to acknowledge it. In a real-world scenario, this could be further exacerbated by network delays, node failures, or other issues that could slow down the replication process.

Still, this design would provide the strongest consistency guarantees possible.

Minimizing latency

Next, we will try to minimize the latency of our system. To achieve this, we could modify our write operation to complete as soon as the data is written to the first (entry) node, without waiting for the replication to other nodes to complete. The replication process would occur asynchronously in the background.

captionless image

This design would significantly reduce the latency of write operations, as they would complete as soon as the entry node acknowledges them. However, this comes at the cost of reduced consistency — there is a window of time during which not all nodes have the same data. If a read operation is performed during this window, it may return stale data from nodes that have not yet received the latest write.

Even worse, a failure of the entry node before replication completes could lead to data loss, despite the write operation being acknowledged to the client!

The trade-off

As we can see, there is a clear trade-off between maximizing consistency and minimizing latency in our distributed system:

  • Maximizing consistency leads to higher latency, as write operations must wait for all nodes to acknowledge them.
  • Minimizing latency leads to reduced consistency, as write operations can complete before all nodes have the latest data.

Remembering the CAP theorem, we might think that this is a binary choice — either we have consistency or low latency. Let’s prove that this is not the case.

We will alter the design of our system to allow for a configurable trade-off between consistency and latency. We can introduce a parameter W (write quorum) that specifies the number of nodes that must acknowledge a write operation before it is considered complete. In this way, the value of W would affect both the consistency and latency of the system:

  • With W=N we would have the strongest consistency – as we have seen before in the “Maximizing consistency” chapter.
  • With W=0 we would have the lowest latency – as we have seen before in the “Minimizing latency” chapter.
  • With 0 < W < N we would have a balance between consistency and latency – the higher the value of W, the stronger the consistency and the higher the latency, and vice versa.

Let’s illustrate one example — with W=N/2 + 1 (majority quorum):

captionless image

This design would provide a middle ground between consistency and latency. While the strong consistency is lost, the system would still provide a reasonable level of consistency:

  • The majority of nodes would have the latest data after a write operation completes.
  • The latency would be defined by the latency of the slowest node in the majority, which is typically lower than waiting for all nodes. That said, the latency would still be higher than waiting for just one node, but it would be affected in a lesser degree by the latency spikes on a single node.

A good example of such a system is MongoDB, which allows clients to configure the write concern for write operations, including options for majority quorum — via so called write concern option.

Finally, the PACELC theorem

While we can clearly see the connection between our thought experiment and the CAP theorem, there are some important differences to note:

  • The CAP theorem focuses on the trade-offs during network partitions. We put our focus on the other case — when there is no network partition.
  • The CAP theorem presents a binary choice between consistency and availability during partitions. We demonstrated a spectrum of trade-offs between consistency and latency in the absence of partitions.

All of this leads us to the PACELC theorem:

In the presence of a network partition, a distributed system must choose between strong consistency and availability.

Else, when there is no network partition, a distributed system must choose between latency and the degree of consistency.

It’s easy to remember and decode the name of the theorem:

  • PAC means: “if Partition happens, choose between Availability and Consistency” – the CAP theorem.
  • ELC means: “Else, choose between Latency and Consistency”.

Now, it’s a great theorem — its whole meaning is encoded in its name!

I would like to note one point, important for completeness.

In the CAP theorem, we speak about strong consistency (linearizability) only. However, in the PACELC theorem, the consistency is a spectrum — from strong consistency to eventual consistency. And eventual consistency is non-binary — there are different degrees of eventual consistency:

  • Ensuring that N-1 nodes might serve the stale data for some time is a weaker form of eventual consistency.
  • Ensuring that only the minority of nodes might serve the stale data for some time is a stronger form of eventual consistency.
  • Ensuring that not a single node might serve the stale data for some time is a strong consistency, not an eventual one.

It’s a caveat worth mentioning!