<?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: prof2k</title>
    <description>The latest articles on DEV Community by prof2k (@prof2k).</description>
    <link>https://dev.to/prof2k</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%2F395346%2F3277589f-c817-460f-bd0f-da4321955eb5.png</url>
      <title>DEV Community: prof2k</title>
      <link>https://dev.to/prof2k</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/prof2k"/>
    <language>en</language>
    <item>
      <title>Building FrameMill: Turning Hours of Gameplay Into an Editable First Cut</title>
      <dc:creator>prof2k</dc:creator>
      <pubDate>Mon, 31 Aug 2026 23:07:51 +0000</pubDate>
      <link>https://dev.to/prof2k/building-framemill-turning-hours-of-gameplay-into-an-editable-first-cut-4gb2</link>
      <guid>https://dev.to/prof2k/building-framemill-turning-hours-of-gameplay-into-an-editable-first-cut-4gb2</guid>
      <description>&lt;p&gt;&lt;em&gt;Written for the Gemini All Agentic AI Hackathon.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A game stream can last four hours. The moments worth turning into a video might last only a few minutes.&lt;/p&gt;

&lt;p&gt;Finding those moments usually means rewatching the recording, marking timestamps, cutting clips, and rebuilding the story manually. Existing AI clipping tools can reduce that work, but they often return disconnected clips or a finished video with little explanation or control.&lt;/p&gt;

&lt;p&gt;That is the problem I’m exploring with &lt;strong&gt;FrameMill&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;FrameMill turns long game streams into editable video projects. It analyzes what happened, uses AI to assemble a first cut, and gives the creator a real Timeline they can inspect, change, and render again.&lt;/p&gt;

&lt;h2&gt;
  
  
  The idea: AI should make the first cut
&lt;/h2&gt;

&lt;p&gt;I did not want FrameMill to be a black box that decides what a “good highlight” is.&lt;/p&gt;

&lt;p&gt;A combat event is a fact. Whether it belongs in an intense montage, a tactical breakdown, or a funny recap depends on the creator’s goal.&lt;/p&gt;

&lt;p&gt;That led to the central ownership model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Processor observes.
Gemini interprets, plans, edits, and reviews.
Server persists and executes capabilities.
Client presents and changes the Timeline.
Renderer derives video from a Timeline revision.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The media processor detects factual gameplay Activities with timestamps, confidence, and source evidence. It does not assign universal highlight scores.&lt;/p&gt;

&lt;p&gt;Gemini receives those Activities alongside bounded pieces of source media. It can map the relevant parts of a game, plan an edit, select footage, and construct a revisioned Timeline.&lt;/p&gt;

&lt;p&gt;The Timeline—not the generated video—is the canonical result.&lt;/p&gt;

&lt;h2&gt;
  
  
  How FrameMill works
&lt;/h2&gt;

&lt;p&gt;FrameMill currently supports &lt;em&gt;Call of Duty: Warzone DMZ&lt;/em&gt; as its first game.&lt;/p&gt;

&lt;p&gt;The local workflow looks like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;FFmpeg and libav decode the source footage.&lt;/li&gt;
&lt;li&gt;A deterministic Go processor detects game boundaries and gameplay Activities.&lt;/li&gt;
&lt;li&gt;The processor publishes structured evidence instead of editorial decisions.&lt;/li&gt;
&lt;li&gt;Google ADK agents use Gemini to map the game and plan an edit.&lt;/li&gt;
&lt;li&gt;The Editor agent constructs a validated Timeline with provenance.&lt;/li&gt;
&lt;li&gt;FrameMill renders a real preview from that Timeline.&lt;/li&gt;
&lt;li&gt;The creator can revise the Timeline and request another render.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The backend is written in Go and contains the local server, processing core, and agent runtime. The editing workspace uses Vue, TypeScript, Vite, Tailwind CSS, and Pinia.&lt;/p&gt;

&lt;p&gt;The hosted architecture is designed around MediaMTX, Cloud Storage, Eventarc, Cloud Run, and Firestore. A relay can forward the creator’s stream to its publishing destination while recording small, immutable segments for asynchronous analysis.&lt;/p&gt;

&lt;h2&gt;
  
  
  Processing hours of footage efficiently
&lt;/h2&gt;

&lt;p&gt;One of the hardest technical problems has been processing long recordings without treating the entire video as one giant request.&lt;/p&gt;

&lt;p&gt;FrameMill performs sparse checks across the footage, then uses denser 100 ms refinement only near promising regions. Persistent decoder contexts and bounded parallelism avoid repeatedly reopening the same media.&lt;/p&gt;

&lt;p&gt;An important lesson was that decoding architecture mattered more than micro-optimizing individual detectors. If every detector independently reads the video, small optimizations elsewhere do not help much.&lt;/p&gt;

&lt;p&gt;The better design is to decode once and let multiple detectors consume the same frame evidence.&lt;/p&gt;

&lt;h2&gt;
  
  
  Making AI editing recoverable
&lt;/h2&gt;

&lt;p&gt;Video analysis, model calls, and rendering can all take longer than a browser session. They can also fail independently.&lt;/p&gt;

&lt;p&gt;FrameMill therefore treats recovery as part of the product:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Media objects have immutable, generation-qualified identities.&lt;/li&gt;
&lt;li&gt;Agent tasks have stable IDs and explicit lifecycle states.&lt;/li&gt;
&lt;li&gt;Duplicate events are handled idempotently.&lt;/li&gt;
&lt;li&gt;Successful child-agent results survive sibling failures.&lt;/li&gt;
&lt;li&gt;Timeline changes use base revisions instead of silent overwrites.&lt;/li&gt;
&lt;li&gt;Rendered videos can be reproduced from a specific Timeline revision.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the browser closes, accepted work should continue. When the creator returns, the client should reconstruct the latest durable state rather than starting over.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building with Codex
&lt;/h2&gt;

&lt;p&gt;I used Codex as an engineering collaborator throughout the project.&lt;/p&gt;

&lt;p&gt;It helped me inspect unfamiliar code, investigate real gameplay footage, create tests, compare performance measurements, and coordinate changes across isolated Git worktrees.&lt;/p&gt;

&lt;p&gt;The most valuable part was not generating code quickly. It was being able to challenge architectural decisions, trace ownership across the system, and verify whether an implementation actually matched the intended product.&lt;/p&gt;

&lt;p&gt;I still retained control over the product model, system boundaries, tradeoffs, and final implementation decisions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the project is today
&lt;/h2&gt;

&lt;p&gt;The current MVP proves a real local vertical slice: source analysis, Activity mapping, Timeline validation, and FFmpeg preview rendering.&lt;/p&gt;

&lt;p&gt;The browser workspace demonstrates evidence review, Timeline editing, provenance, revision-conflict recovery, and the render workflow. Some of that workspace still uses canonical fixtures while the remaining server-backed Timeline and control endpoints are completed.&lt;/p&gt;

&lt;p&gt;The hosted path is also still being integrated end to end. I would rather label those boundaries clearly than present a demo fixture as production infrastructure.&lt;/p&gt;

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

&lt;p&gt;The biggest lesson is that AI does not need to own the entire system to be useful.&lt;/p&gt;

&lt;p&gt;Deterministic software is better at observing facts, validating contracts, tracking state, and reproducing outputs. Gemini is better used where interpretation and editorial judgment are actually required.&lt;/p&gt;

&lt;p&gt;I also learned that &lt;strong&gt;editability is part of AI trust&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A creator should be able to see why a clip was chosen, change the decision, and render a different result. The goal is not to remove the creator from editing. It is to remove the hours of searching that happen before the creative work can begin.&lt;/p&gt;

&lt;p&gt;FrameMill is my attempt to make that distinction real.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>go</category>
      <category>gamedev</category>
      <category>googlecloud</category>
    </item>
    <item>
      <title>Waypoint — Building a Universal Intent Layer for the Web</title>
      <dc:creator>prof2k</dc:creator>
      <pubDate>Mon, 16 Mar 2026 23:08:48 +0000</pubDate>
      <link>https://dev.to/prof2k/waypoint-building-a-universal-intent-layer-for-the-web-55a5</link>
      <guid>https://dev.to/prof2k/waypoint-building-a-universal-intent-layer-for-the-web-55a5</guid>
      <description>&lt;p&gt;&lt;em&gt;This article was written as part of my submission for the Google Gemini Live Agent Challenge. #GeminiLiveAgentChallenge&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The problem nobody talks about
&lt;/h2&gt;

&lt;p&gt;Try navigating a website using only your keyboard right now.&lt;/p&gt;

&lt;p&gt;Press Tab. Keep pressing it. Watch the focus indicator jump unpredictably across hundreds of elements before you reach the one thing you actually wanted.&lt;/p&gt;

&lt;p&gt;This is the daily reality for millions of people with motor disabilities, visual impairments, or conditions that make using a mouse impossible or painful. Tab navigation is the web's accessibility fallback. And it's broken.&lt;/p&gt;

&lt;p&gt;Not because it doesn't work technically. But because it was designed around &lt;strong&gt;actions&lt;/strong&gt; — move to next element, press enter — rather than &lt;strong&gt;intent&lt;/strong&gt; — I want to find the navigation, I want to search, I want to buy this.&lt;/p&gt;

&lt;p&gt;The gap between those two things is where Waypoint lives.&lt;/p&gt;




&lt;h2&gt;
  
  
  What exists today and why it falls short
&lt;/h2&gt;

&lt;p&gt;Current accessibility tools fall into a few buckets:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Screen readers&lt;/strong&gt; like JAWS and NVDA are powerful but have a steep learning curve. They read what's there. They don't understand what you want.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Voice control tools&lt;/strong&gt; like Dragon NaturallySpeaking let you speak commands — but they're essentially voice-powered mouse clicks. "Click sign in." It's still action-first. It requires you to know exactly what the UI calls the thing you want.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Browser accessibility APIs&lt;/strong&gt; give developers tools to make sites accessible — but only if developers use them correctly. Most don't.&lt;/p&gt;

&lt;p&gt;The fundamental problem with all of these: &lt;strong&gt;they require the user to adapt to the interface.&lt;/strong&gt; Waypoint flips that. The interface adapts to the user.&lt;/p&gt;




&lt;h2&gt;
  
  
  The core insight: intent over action
&lt;/h2&gt;

&lt;p&gt;Every website regardless of how it's built has the same underlying anatomy:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Navigation&lt;/strong&gt; — somewhere to go&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Primary purpose&lt;/strong&gt; — a reason the page exists&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Controls&lt;/strong&gt; — things to do&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The markup might be a mess. The developer might have used &lt;code&gt;&amp;lt;div class="nav-wrapper"&amp;gt;&lt;/code&gt; instead of &lt;code&gt;&amp;lt;nav&amp;gt;&lt;/code&gt;. None of that matters if you understand what the page is for and what a user could want to accomplish on it.&lt;/p&gt;

&lt;p&gt;That's intent. Not "click this element." But "I want to navigate."&lt;/p&gt;

&lt;p&gt;Waypoint is built around three primitives:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DISCOVER&lt;/strong&gt; → &lt;strong&gt;DOCUMENT&lt;/strong&gt; → &lt;strong&gt;ACTIVATE&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;DISCOVER&lt;/strong&gt; — analyze the page, map every possible intent&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DOCUMENT&lt;/strong&gt; — store it as a living Intent Surface Map&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ACTIVATE&lt;/strong&gt; — resolve natural speech against that map and execute&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Everything in the codebase serves one of these three primitives. Nothing else.&lt;/p&gt;




&lt;h2&gt;
  
  
  The architecture
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The three-layer indexing pipeline
&lt;/h3&gt;

&lt;p&gt;The hardest problem is turning an arbitrary webpage — with inconsistent markup, no guaranteed semantic HTML, no ARIA — into a structured map of what a user can do. Waypoint solves this with a three-layer indexing pipeline that runs when the user clicks "Index this page."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Layer 0 — Structural (0ms, client-side, no AI)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The content script walks the entire DOM and stamps every interactive element — every &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;select&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;textarea&amp;gt;&lt;/code&gt; — with a &lt;code&gt;data-wp-id&lt;/code&gt; attribute. Short random IDs. Six characters. These stamps are the bridge between the AI's output and the live DOM.&lt;/p&gt;

&lt;p&gt;At the same time, semantic HTML tags — &lt;code&gt;&amp;lt;nav&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;main&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;header&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;footer&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt; — get bare-bones surfaces created immediately. No AI. Voice activates against these instantly. Zero latency for the most common intents.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Layer 1 — Text enrichment (~1s, Gemini Flash)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The backend receives three things: the containment tree (DOM structure with positions and text), the fixed/absolute elements (sticky nav, floating buttons — flagged as likely universal controls), and the flat interactives list (every stamped element). &lt;/p&gt;

&lt;p&gt;Gemini's job: group related elements into named surfaces, write voice-natural labels and trigger phrases including synonyms, classify the page type and purpose. The critical constraint in the prompt: every element in the interactives list must become a surface. No coverage gaps.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Layer 2 — Vision enrichment (~2-4s, Gemini Flash multimodal)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A screenshot is captured via &lt;code&gt;chrome.tabs.captureVisibleTab()&lt;/code&gt;. Gemini receives the screenshot AND the Layer 1 surface map. Its job is narrower: find what DOM analysis missed. Image carousels. Icon-only buttons. Visual hierarchy. Hero CTAs identified by visual prominence. Cookie banners. Chat widgets.&lt;/p&gt;

&lt;p&gt;Merge logic uses &lt;code&gt;data-wp-id&lt;/code&gt; as the deduplication key. Layer 2 wins on conflicts. Layer 0 surfaces not mentioned by Gemini are kept as-is — this is the 100% coverage guarantee.&lt;/p&gt;




&lt;h3&gt;
  
  
  The Intent Surface Map
&lt;/h3&gt;

&lt;p&gt;The output of indexing is a structured document:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;url&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="s2"&gt;https://example.com/shop&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="s2"&gt;pageType&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="s2"&gt;ecommerce&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="s2"&gt;purpose&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="s2"&gt;Browse and purchase products&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="s2"&gt;surfaces&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;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id&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="s2"&gt;nav-main&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="s2"&gt;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="s2"&gt;WAYFINDING&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="s2"&gt;label&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="s2"&gt;Main Navigation&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="s2"&gt;triggers&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;navigation&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="s2"&gt;nav&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="s2"&gt;menu&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="s2"&gt;links&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="s2"&gt;confidence&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.95&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;action&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;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="s2"&gt;SCROLL_TO&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="s2"&gt;target&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="s2"&gt;[data-wp-id=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;a3f7&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&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="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id&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="s2"&gt;btn-cart&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="s2"&gt;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="s2"&gt;CONTROLS&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="s2"&gt;label&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="s2"&gt;Add to Cart&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="s2"&gt;triggers&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;add to cart&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="s2"&gt;buy&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="s2"&gt;purchase&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="s2"&gt;cart&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="s2"&gt;action&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;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="s2"&gt;CLICK&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="s2"&gt;target&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="s2"&gt;[data-wp-id=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;c9d2&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&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="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;Four surface types: WAYFINDING, PURPOSE, CONTROLS, CONTEXT. Seven action types: OVERLAY, SCROLL_TO, FOCUS, FILL, CLICK, READ, DISMISS.&lt;/p&gt;

&lt;p&gt;The map is cached in &lt;code&gt;chrome.storage.local&lt;/code&gt; keyed by origin + pathname. On cache hit the map loads immediately and &lt;code&gt;refreshSurfaceTargets()&lt;/code&gt; re-stamps the DOM — stamps don't survive page reloads.&lt;/p&gt;




&lt;h3&gt;
  
  
  The voice pipeline — Gemini Live
&lt;/h3&gt;

&lt;p&gt;This is the architectural bet that makes Waypoint different.&lt;/p&gt;

&lt;p&gt;Gemini Live handles STT, NLU, TTS, and conversation state in a single bidirectional WebSocket. No separate speech-to-text service. No separate NLP layer. No separate TTS. One connection. One stream. Everything.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How it works:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The mic stream goes through an AudioWorklet that converts Float32 samples to Int16 PCM in a separate thread. Buffers are base64-encoded and sent continuously to the WebSocket as &lt;code&gt;realtimeInput.mediaChunks&lt;/code&gt; at 16kHz.&lt;/p&gt;

&lt;p&gt;Gemini streams back PCM16 audio at 24kHz. Chunks are queued using &lt;code&gt;nextPlayAt&lt;/code&gt; — each chunk's start time is set to when the previous chunk ends, preventing gaps or overlaps.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The key architectural decision — tool calls:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Gemini doesn't return text that needs parsing. It returns structured function calls:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;activate_surface&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;surfaceId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;btn-checkout&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="nf"&gt;scroll_page&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;direction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;down&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="nf"&gt;enter_click_mode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nf"&gt;navigate_highlight&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;direction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;right&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="nf"&gt;activate_highlight&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the user says "go to checkout" Gemini calls &lt;code&gt;activate_surface({ surfaceId: "btn-checkout" })&lt;/code&gt;. The extension looks up that surface in the intent map, executes the action against the live DOM, and acknowledges the tool call back to Gemini.&lt;/p&gt;

&lt;p&gt;Natural language understanding stays in the model. DOM interaction stays in the extension. Clean separation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The system prompt:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;At session start Gemini receives the full intent map — every surface, its id, type, label, triggers, and action type. This is Gemini's complete menu of what the user can do on this page. The conversation is grounded in the actual page from the first word.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Interruption:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If the user speaks while Gemini is talking, &lt;code&gt;serverContent.interrupted&lt;/code&gt; fires. &lt;code&gt;nextPlayAt&lt;/code&gt; resets to &lt;code&gt;currentTime&lt;/code&gt; — queued audio drops immediately. Barge-in works naturally.&lt;/p&gt;




&lt;h3&gt;
  
  
  The action execution layer
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;execute(surface)&lt;/code&gt; dispatches to one of seven handlers. The interesting part is &lt;code&gt;findTarget()&lt;/code&gt; — a five-stage fallback chain that finds the actual DOM element even when things go wrong:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Parse &lt;code&gt;data-wp-id&lt;/code&gt; from the target string → &lt;code&gt;querySelector&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Try the target string as a raw CSS selector&lt;/li&gt;
&lt;li&gt;Try &lt;code&gt;[aria-label="&amp;lt;surface.label&amp;gt;"]&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Search headings for text matching surface triggers → return nearest section ancestor&lt;/li&gt;
&lt;li&gt;Broad text search across all interactive and landmark elements&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This chain exists because &lt;code&gt;data-wp-id&lt;/code&gt; stamps break on SPA navigation, dynamic renders, and cached maps. The system degrades gracefully rather than failing silently.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;doFill()&lt;/code&gt; focuses the field and switches voice mode to dictation — subsequent speech is text input, not a command. &lt;code&gt;doSubmit()&lt;/code&gt; requires a confirmation utterance before executing. &lt;code&gt;doDismiss()&lt;/code&gt; fires both an Escape keydown and clicks common close button selectors in case the modal ignores keyboard events.&lt;/p&gt;




&lt;h3&gt;
  
  
  Click mode
&lt;/h3&gt;

&lt;p&gt;Click mode handles elements not in the intent map — or when the user wants precise control.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;enter_click_mode&lt;/code&gt; → scans all focusable elements in the viewport, filters nav/header elements and invisible elements, sorts row-major (top-to-bottom, left-to-right). The first element highlights immediately.&lt;/p&gt;

&lt;p&gt;The visual overlay is a &lt;code&gt;position: fixed&lt;/code&gt; div injected directly into &lt;code&gt;document.documentElement&lt;/code&gt; — not shadow DOM, since it needs to sit above all page content. It has a pulsing animation, a label chip, and a counter badge (3/12).&lt;/p&gt;

&lt;p&gt;&lt;code&gt;navigate_highlight(direction)&lt;/code&gt; cycles through elements. &lt;code&gt;activate_highlight()&lt;/code&gt; clicks and exits click mode automatically.&lt;/p&gt;




&lt;h3&gt;
  
  
  The backend
&lt;/h3&gt;

&lt;p&gt;Express on Cloud Run (Node 20). Two endpoints:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;POST /index/text&lt;/code&gt; — receives the pruned containment tree + flat interactives list + Layer 0 surfaces. Calls Gemini Flash text-only. Returns enriched surface map.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;POST /index/vision&lt;/code&gt; — receives base64 JPEG screenshot + current surface map. Calls Gemini Flash multimodal. Returns only new or improved surfaces — not a re-enumeration of everything.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;GET /config&lt;/code&gt; — serves the Gemini API key to the extension. The key lives in the Cloud Run environment variable. Never in the extension bundle.&lt;/p&gt;

&lt;p&gt;The Vertex AI SDK handles auth via Application Default Credentials on Cloud Run. No key management needed for backend Gemini calls.&lt;/p&gt;




&lt;h3&gt;
  
  
  Shadow DOM strategy
&lt;/h3&gt;

&lt;p&gt;Shadow DOM is used in two places for opposite reasons:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Debug panel&lt;/strong&gt; — isolates panel CSS from page CSS. Without it page styles bleed in.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Overlay host&lt;/strong&gt; — contains overlay scripts and styles so they don't affect page layout.&lt;/p&gt;

&lt;p&gt;The visual overlays (highlight box, scroll indicator) are NOT in shadow DOM — injected directly into &lt;code&gt;document.documentElement&lt;/code&gt;. They need &lt;code&gt;z-index: 2147483645&lt;/code&gt; to sit above all page content. Shadow DOM creates its own stacking context that can be clipped by the page.&lt;/p&gt;




&lt;h3&gt;
  
  
  What was intentionally removed
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Local NLU&lt;/strong&gt; — earlier versions had fuzzy matching, keyword scoring, synonym lookup on the client. All removed. Gemini Live handles this better. The latency cost is offset by richer, more reliable results.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Web Speech API&lt;/strong&gt; — removed entirely. Required a separate endpoint, had a network error loop on restricted networks, needed a separate NLU pass anyway.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Google Cloud TTS&lt;/strong&gt; — removed. Gemini Live speaks its own responses. Browser &lt;code&gt;speechSynthesis&lt;/code&gt; is kept only for system messages outside an active session.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Intent caching across pages&lt;/strong&gt; — cache is keyed by origin + pathname. Different pages get different indexes. Surfaces are too page-specific to reuse.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Confidence threshold filtering&lt;/strong&gt; — every surface Gemini returns is kept regardless of confidence. The model's judgment is trusted. Filtering happens at query time via Gemini Live.&lt;/p&gt;




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

&lt;p&gt;The hardest problem wasn't the AI integration. It was defining intent precisely enough to be useful without being so narrow it became another command parser.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;data-wp-id&lt;/code&gt; stamping approach — a persistent bridge between AI output and live DOM — turned out to be the most important implementation detail in the entire system. Everything depends on it.&lt;/p&gt;

&lt;p&gt;And the architectural bet on Gemini Live as the single voice pipeline — STT, NLU, TTS, conversation state all in one WebSocket — was the right call. Simpler system. Better results. Natural conversation without any orchestration layer.&lt;/p&gt;




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

&lt;p&gt;Persistent intent maps in Firestore — returning to a visited page is instant. Cross-session learning. A shared intent vocabulary across all Waypoint users — crowdsourced, Gemini-validated, continuously improving.&lt;/p&gt;

&lt;p&gt;The web was built for everyone. Waypoint is making that promise real.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built with Gemini 2.0 Flash, Gemini Live API, Vertex AI, Cloud Run, Chrome Extensions Manifest V3, and the Google GenAI SDK.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;#GeminiLiveAgentChallenge&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>geminihackathon</category>
      <category>googlecloud</category>
      <category>a11y</category>
    </item>
  </channel>
</rss>
