<?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: FlowBotCommander</title>
    <description>The latest articles on DEV Community by FlowBotCommander (@flowbotcommander).</description>
    <link>https://dev.to/flowbotcommander</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%2F4139189%2F5c4b21c2-a087-40e2-aa87-9554e982096f.png</url>
      <title>DEV Community: FlowBotCommander</title>
      <link>https://dev.to/flowbotcommander</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/flowbotcommander"/>
    <language>en</language>
    <item>
      <title>Why coordinate-based desktop automation breaks, and what to do instead</title>
      <dc:creator>FlowBotCommander</dc:creator>
      <pubDate>Wed, 23 Sep 2026 10:09:44 +0000</pubDate>
      <link>https://dev.to/flowbotcommander/why-coordinate-based-desktop-automation-breaks-and-what-to-do-instead-2dl7</link>
      <guid>https://dev.to/flowbotcommander/why-coordinate-based-desktop-automation-breaks-and-what-to-do-instead-2dl7</guid>
      <description>&lt;p&gt;Every desktop automation script starts the same way. You record a click, you get &lt;code&gt;click(842, 517)&lt;/code&gt;, and it works. It keeps working for exactly as long as nothing on the screen moves.&lt;/p&gt;

&lt;p&gt;Then someone plugs in a second monitor. Or Windows scaling goes from 100% to 125%. Or the app ships an update that moves a toolbar down by twelve pixels. The script keeps running, keeps clicking 842/517, and now hits whatever happens to be there. It fails &lt;em&gt;silently&lt;/em&gt;, which is the worst way for automation to fail.&lt;/p&gt;

&lt;p&gt;I have been building a tool in this space for a while. Here are the three things I had to learn, none of which were obvious to me at the start.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Finding the button by its image is the right idea, done naively
&lt;/h2&gt;

&lt;p&gt;The obvious fix: do not store a position, store a picture of the button and look for it.&lt;/p&gt;

&lt;p&gt;AutoHotkey has &lt;code&gt;ImageSearch&lt;/code&gt;, and most automation toolkits have something similar. The problem is that the classic implementations match &lt;strong&gt;exactly&lt;/strong&gt;. Every pixel has to agree.&lt;/p&gt;

&lt;p&gt;That sounds robust until you list what changes pixels without changing what a human sees:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;font anti-aliasing differing between machines&lt;/li&gt;
&lt;li&gt;a drop shadow from another window overlapping the edge of your button&lt;/li&gt;
&lt;li&gt;the app switching between light and dark theme&lt;/li&gt;
&lt;li&gt;display scaling at 125% or 150%&lt;/li&gt;
&lt;li&gt;compression artifacts in remote sessions&lt;/li&gt;
&lt;li&gt;a hover state, because the mouse happened to be nearby&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Any one of those and an exact match returns nothing. You have swapped clicking the wrong thing for doing nothing at all. That is an improvement, because a silent wrong action is worse than a visible stall. It is not a solution.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. What you want is a tolerance, and the user has to own it
&lt;/h2&gt;

&lt;p&gt;The fix is template matching with a similarity score rather than a boolean: compute how well the template correlates with each region of the screen, take the best match, compare it against a threshold.&lt;/p&gt;

&lt;p&gt;The interesting part is not the algorithm. OpenCV's &lt;code&gt;matchTemplate&lt;/code&gt; has done this for years. The interesting part is that &lt;strong&gt;the threshold is a product decision, not an implementation detail.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Set it too high and you are back to exact matching. Set it too low and your Confirm button matches the Cancel button next to it, because at 70% similarity two grey rounded rectangles with a word on them are basically the same thing.&lt;/p&gt;

&lt;p&gt;There is no value that is right for everyone. So the threshold has to be visible and adjustable per step, with a way to &lt;em&gt;see&lt;/em&gt; what matched. A confidence number in a log file is not enough. People need the box drawn around what the tool found. Almost every support question I get traces back to a threshold that was fine on one screen and wrong on another.&lt;/p&gt;

&lt;p&gt;A related trap: two visually identical buttons where one is disabled and greyed out. A tolerant match will happily click the disabled one. Brightness and saturation checks alongside the shape comparison turned out to matter more than I expected.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Clicking is the easy half. Reading is the hard half.
&lt;/h2&gt;

&lt;p&gt;Automation that only clicks is a macro. Automation that &lt;em&gt;reacts&lt;/em&gt; has to read the screen, and that means OCR.&lt;/p&gt;

&lt;p&gt;OCR on screen content is a different problem from OCR on scanned documents. It is cleaner, because you have crisp rendered text instead of paper artifacts. It is also less forgiving, because you usually want one specific number rather than a paragraph, and you want it right every single time. A 5 read as a 6 in a document is a typo. In an automation loop it is a wrong decision, repeated.&lt;/p&gt;

&lt;p&gt;Two things helped more than reaching for a better model: restricting recognition to a small region the user defines, and restricting the expected character set. If a field can only hold digits and a decimal separator, say so, and a whole class of errors disappears.&lt;/p&gt;

&lt;h2&gt;
  
  
  When not to do any of this
&lt;/h2&gt;

&lt;p&gt;This is the part tools in this category tend to leave out, so let me be direct: screen automation is a workaround. It is the right tool when there is no better interface, and the wrong one when there is.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;If the program has an API, an export or a command-line interface, use it.&lt;/strong&gt; It will be faster and it will not break when a button moves.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;If you only need hotkeys and text expansion&lt;/strong&gt;, AutoHotkey is free, mature and built exactly for that.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;If it is a one-off job&lt;/strong&gt;, doing it by hand is probably quicker than building the flow.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check what you are allowed to automate.&lt;/strong&gt; Terms of service exist, and in games in particular, single-player only is a line worth respecting.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Screen automation earns its place in a narrow band: repetitive work, in software you do not control, where no interface exists. That band is wider than it should be, mostly filled with line-of-business software nobody maintains any more. But it is a band, not a universe.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Disclosure: I build &lt;a href="https://flowbotcommander.com/?utm_source=devto" rel="noopener noreferrer"&gt;FlowBotCommander&lt;/a&gt;, a Windows tool that does the above in a visual editor. It is free for 100 runs a day. This post was drafted with AI assistance and edited by me.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>automation</category>
      <category>windows</category>
      <category>opencv</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
