The 3-Second Blindspot: Solving DOM Volatility in Vision-Based Browser Agents
Vision-based browser agents take 2 to 4 seconds to capture screenshots and compute click coordinates, while modern React and Next.js SPAs re-render in milliseconds. Here is how Accessibility Tree coordinate anchoring and client-side action buffering eliminate DOM race conditions.
Vision-based computer-use and browser agents are nothing short of magical when navigating static websites.
On a modern enterprise Single Page Application (React, Vue, Next.js, or Angular), it fails nearly 50% of the time.
Why? Because between the moment the screenshot is taken ($T0$) and the moment the model returns its planned $(x, y)$ coordinate click ($T0 + 3.2\text{s}$), an asynchronous toast notification mounted, shifting the grid down by 64 pixels.
The agent clicks empty whitespace.
!Browser Agent DOM Volatility and A11y Anchoring
The Solution: Accessibility Tree Anchoring
async def execute_anchored_click(page, planned_coords: dict, semantic_hint: str):
# Wait for DOM mutations to settle
await page.evaluate("""() => new Promise(resolve => setTimeout(resolve, 80))""")
# Resolve element on live Accessibility tree
element = await page.evaluate_handle(f"""({{x, y, hint}}) => {{
let target = document.elementFromPoint(x, y);
if (!target || target.tagName === 'DIV') {{
return Array.from(document.querySelectorAll('button, a, input')).find(el => el.innerText.toLowerCase().includes(hint.toLowerCase())) || target;
}}
return target;
}}""", {"x": planned_coords["x"], "y": planned_coords["y"], "hint": semantic_hint})
box = await element.bounding_box()
if box:
await page.mouse.click(box["x"] + box["width"]/2, box["y"] + box["height"]/2)