<?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: @planetabhi</title>
    <description>The latest articles on DEV Community by @planetabhi (@planetabhi_).</description>
    <link>https://dev.to/planetabhi_</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%2F4089133%2F6102c25c-7898-4ed5-9166-c73807fa4f49.jpg</url>
      <title>DEV Community: @planetabhi</title>
      <link>https://dev.to/planetabhi_</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/planetabhi_"/>
    <language>en</language>
    <item>
      <title>Invite an AI to Look Upon Your Designs</title>
      <dc:creator>@planetabhi</dc:creator>
      <pubDate>Sat, 22 Aug 2026 06:55:16 +0000</pubDate>
      <link>https://dev.to/planetabhi_/invite-an-ai-to-look-upon-your-figma-designs-4930</link>
      <guid>https://dev.to/planetabhi_/invite-an-ai-to-look-upon-your-figma-designs-4930</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Who will look upon your work before the world does? Let it be an AI, quietly, inside Figma, reading each frame and pinning its honest thoughts to the very place they belong.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let Claude, GPT, Gemini, or any AI model wander through your designs, offer its quiet counsel, and leave its thoughts pinned upon the Figma canvas.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A gentle word before you begin.&lt;/strong&gt; This hands an AI model real power over your Figma files. It may leave comments upon the canvas, and take them away again. So begin where nothing is precious, on a blank, new, or draft file, where a mistake carries no sorrow. Try a few reviews, watch how the assistant speaks, and learn the prompts that answer you kindly. When trust has grown between you, carry it to the files that matter.&lt;/p&gt;




&lt;p&gt;The purely local &lt;a href="https://github.com/planetabhi/figma-mcp-server" rel="noopener noreferrer"&gt;Figma MCP Server&lt;/a&gt; with full Figma REST API coverage opens a door through which any AI assistant that speaks MCP may come and look upon your designs. It keeps company with clients such as GitHub Copilot in VS Code, Claude Desktop, and Cursor, and with models such as Claude, GPT, and Gemini. The assistant reads your work, offers its honest reflection, and pins its comments to the canvas. And because it runs upon your own machine, your Figma token never wanders from your hands.&lt;/p&gt;

&lt;h3&gt;
  
  
  What you can do with it
&lt;/h3&gt;

&lt;p&gt;Speak to your assistant in plain, unhurried language, and it can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Look upon your frames. It renders a screenshot of any screen, then reads the layers and components beneath when it needs to see closer.&lt;/li&gt;
&lt;li&gt;Weigh the design. It's hierarchy, spacing, contrast, alignment, and consistency.&lt;/li&gt;
&lt;li&gt;Hold the work against your design system, it's tokens and components.&lt;/li&gt;
&lt;li&gt;Leave its feedback in Figma, pinned to the very frame it speaks of.&lt;/li&gt;
&lt;li&gt;Move through the comments already there. It reads every open thread, gathers them, sets them in order of urgency, and turns them into a clear list of things to do.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Before you start
&lt;/h3&gt;

&lt;p&gt;Three small things must be gathered first:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;An AI client that supports MCP, such as GitHub Copilot in VS Code, Claude Desktop, or Cursor.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://bun.sh/" rel="noopener noreferrer"&gt;Bun.sh&lt;/a&gt; installed. It runs the server.&lt;/li&gt;
&lt;li&gt;A Figma personal access token, which you create in Step 1.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;With these three in hand, the path ahead is short.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 01: Get your Figma token
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;In Figma, go to &lt;strong&gt;Settings → Security → Personal access tokens&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Name it, and check all items under the &lt;strong&gt;Users&lt;/strong&gt; and &lt;strong&gt;Files&lt;/strong&gt; scopes. Files lets the assistant read your designs and post comments; Users lets it identify who is commenting. These are the minimum scopes it needs.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Generate token&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Copy it now, for Figma will show it only this once. Guard it as you would a password.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Step 02: Connect the server to your AI client
&lt;/h3&gt;

&lt;p&gt;Point your client toward the server but once, and offer it your token. Most clients will keep it safe in your system's secret storage, never lying bare in a plain file.&lt;/p&gt;

&lt;h4&gt;
  
  
  VS Code (GitHub Copilot)
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Open the folder you want to work in. Any project root or test folder works.&lt;/li&gt;
&lt;li&gt;Inside it, create a folder named &lt;code&gt;.vscode&lt;/code&gt;, and inside that a file named &lt;code&gt;mcp.json&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Paste this into &lt;code&gt;mcp.json&lt;/code&gt; and save:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"inputs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"promptString"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"figma-api-key"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Figma API Key"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"password"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"servers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"figma"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bunx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"figma-mcp-server"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"env"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"FIGMA_API_KEY"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"${input:figma-api-key}"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Open the &lt;strong&gt;Command Palette&lt;/strong&gt; (&lt;code&gt;Cmd+Shift+P&lt;/code&gt; on Mac, &lt;code&gt;Ctrl+Shift+P&lt;/code&gt; on Windows) and run &lt;strong&gt;MCP: List Servers&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Pick &lt;code&gt;figma&lt;/code&gt;, then &lt;strong&gt;Start Server&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Paste the token from Step 01 when prompted. VS Code stores it in your OS secret storage: macOS Keychain, Credential Manager on Windows, or your keyring on Linux.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Claude Desktop, Cursor, and other clients
&lt;/h4&gt;

&lt;p&gt;Add the same server to your client's MCP config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"figma"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bunx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"figma-mcp-server"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"env"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"FIGMA_API_KEY"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"your_figma_api_key_here"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This block keeps the token in plain sight, so keep this file away from version control. Restart the client, then see that the Figma server has joined you in its MCP settings. Seek the mode that lets the assistant reach for outside tools; each client calls it by a different name, be it agent, tools, or MCP.&lt;/p&gt;

&lt;p&gt;Once the bond is made, the server brings forth the Figma tools. In VS Code, the Output console shows the connection alive and names the tools it may use, such as reading frames and leaving comments:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;[info] Connection state: Starting
[info] Connection state: Running
[info] Discovered Figma tools
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 03: Grab a frame link
&lt;/h3&gt;

&lt;p&gt;To point the assistant at a single frame is swifter and more sure than to hand it the whole file. It attends to what you care for, rather than wandering through everything.&lt;/p&gt;

&lt;p&gt;In Figma, right-click the frame and choose &lt;strong&gt;Copy/Paste as → Copy link to selection&lt;/strong&gt;. You get a URL like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;figma.com/design/AbCdEf123456/Project?node-id=42-1337
                 ^^^^^^^^^^^^                 ^^^^^^^
                 file key                     frame
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Paste that link into your prompt just as it is. The file key tells the assistant which file to enter, and the node-id which frame to seek. To review several frames, offer a link for each, or point the assistant at a whole page and ask it to consider every top-level frame upon it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A word on cost and limits.&lt;/strong&gt; Every review spends model tokens, and the Figma API sets a limit on how often you may ask within a span of time. To review whole files, or many frames at once, wears through both the sooner. Begin with a few frames, and should you meet a limit, wait a little while and ask for less.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 04: Ask for a review
&lt;/h3&gt;

&lt;p&gt;First, give the assistant its bearings. Tell it the purpose of the screen, the device it is made for, and the people who will meet it. A frame seen alone, without its story, will be faulted for what was meant all along, its states, its breakpoints.&lt;/p&gt;

&lt;p&gt;Open your AI client and let it reach for tools or act as an agent, so it may call upon the Figma tools. In VS Code, open Copilot Chat and turn it to Agent mode. Then paste the frame link and tell it what to look for:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Review this frame: https://www.figma.com/design/AbCdEf123456/Project?node-id=42-1337. Check spacing consistency, color contrast, and visual hierarchy. List the top issues before posting anything.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The assistant enters the frame, considers it, and returns a gathered list. It will name the very components and layers, not merely "the blue button somewhere."&lt;/p&gt;

&lt;p&gt;Should it say it cannot reach the frame, look again at the token scopes from Step 1, and be sure the link leads to a frame you yourself may open.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 05: Post it to the canvas
&lt;/h3&gt;

&lt;p&gt;When the notes sit right with you, send them home to Figma:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Post each issue as a comment, pinned to the frame it's about. Suffix every comment with "(Added by Claude)" so we know which model left it.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ask the assistant to sign what it leaves (say, "Added by Claude" or "Added by Gemini"). This matters most when many hands, or many models, tend the same file: every word stays known by its author. Choose a prefix or a suffix, and hold to it.&lt;/p&gt;

&lt;p&gt;The comments settle where the trouble lies. Your companions open Figma and find the review pinned upon the work itself, marked with the name of whoever wrote it.&lt;/p&gt;




&lt;h4&gt;
  
  
  When not to use it
&lt;/h4&gt;

&lt;p&gt;The assistant looks upon a rendered image and the data of the layers. It cannot feel motion, nor follow a prototype's flow, nor know the touch of real use; and it does not stand in place of testing with people, or of true research.&lt;/p&gt;

&lt;h4&gt;
  
  
  Why it's worth it
&lt;/h4&gt;

&lt;p&gt;Set it up but once, and a design review becomes a single sentence spoken to your assistant. The reviews keep their steady voice across many designers, they catch the small faults of spacing and contrast before the handoff, and they lighten the rounds of review. The same quiet helper can later read your design tokens, weigh your components, or trace a file's history, all through plain conversation.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;When your work is finished.&lt;/strong&gt; Take the key from your config, then remove the token in Figma under &lt;strong&gt;Settings → Security&lt;/strong&gt;. And so it is fully revoked.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://github.com/planetabhi/figma-mcp-server" rel="noopener noreferrer"&gt;Explore Figma MCP Server ↗&lt;/a&gt;&lt;/p&gt;

</description>
      <category>figma</category>
      <category>mcp</category>
      <category>design</category>
      <category>tooling</category>
    </item>
    <item>
      <title>Design Diff, a Visual Feedback Loop for Coding Agents</title>
      <dc:creator>@planetabhi</dc:creator>
      <pubDate>Sat, 22 Aug 2026 06:33:18 +0000</pubDate>
      <link>https://dev.to/planetabhi_/design-diff-a-visual-feedback-loop-for-coding-agents-17c9</link>
      <guid>https://dev.to/planetabhi_/design-diff-a-visual-feedback-loop-for-coding-agents-17c9</guid>
      <description>&lt;p&gt;A coding agent can write the markup, wire the styles, and ship a page in seconds. It can even look at that page. What it cannot do is measure it against the design, pixel for pixel. Glancing at a screenshot, it will not catch that the heading sits a few pixels too low, that the color has cooled by a shade, that the font quietly failed to load. It builds by eye, and the page drifts from the design with nothing exact to catch it.&lt;/p&gt;

&lt;p&gt;Design Diff gives it that measure. It lays the design over a screenshot of the live page at the exact same pixel size, then returns one number for what matched and the box around what did not. Call it, read the score, fix what the box points at, call it again. That loop runs until the page matches the design, and no one has to be watching.&lt;/p&gt;

&lt;p&gt;It is built agent-first, a command and a library that speak plain JSON, so any agent or script can drive it. The same signal serves people just as well, from developers closing a handoff to design engineers building a system to designers who want proof rather than a promise.&lt;/p&gt;

&lt;h3&gt;
  
  
  Install
&lt;/h3&gt;

&lt;p&gt;It runs on &lt;a href="https://bun.sh" rel="noopener noreferrer"&gt;Bun&lt;/a&gt; and needs no global install, since &lt;code&gt;bunx&lt;/code&gt; fetches and runs it in one step. The first run downloads Chromium once. Every run after that is instant.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quickstart
&lt;/h2&gt;

&lt;p&gt;You have a running page and a PNG of the design. Point the tool at both and let them stand side by side.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bunx design-diff http://localhost:3000 &lt;span class="nt"&gt;--png&lt;/span&gt; home.png &lt;span class="nt"&gt;--open&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It answers in three ways. &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A short report in the terminal. &lt;/li&gt;
&lt;li&gt;An interactive page in the browser where you lay one image over the other. &lt;/li&gt;
&lt;li&gt;And a folder of artifacts, written to &lt;code&gt;.design-diff&lt;/code&gt; by default, one timestamped run at a time, or wherever you point &lt;code&gt;--out&lt;/code&gt;. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;An agent skips the browser and reads the JSON, but the loop is the same, question to answer and back again.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DESIGN DIFF
────────────────────────

Visual match    92.4%

Diff bounds
x=0..1440
y=812..980
page coverage=17.1%
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ninety-two percent of the pixels line up. The rest is gathered near the foot of the frame, between the lines it names. You know where to look before you even open the report, and knowing where the trouble sits is half the fix.&lt;/p&gt;

&lt;p&gt;Every flag appears in context throughout this piece, and &lt;code&gt;design-diff --help&lt;/code&gt; lists them all.&lt;/p&gt;

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

&lt;p&gt;The loop only works if the number can be trusted, and three things make it so. A shared frame of reference, a page caught at the right moment, and a score you can read at a glance.&lt;/p&gt;

&lt;h3&gt;
  
  
  One frame of reference
&lt;/h3&gt;

&lt;p&gt;Measurement needs a common ground. Two things can be compared only on the same scale, so the design sets the size and the page is captured at that exact width and height. Laid on one measure, they line up point for point, with no room for a false reading.&lt;/p&gt;

&lt;h3&gt;
  
  
  Caught at the right moment
&lt;/h3&gt;

&lt;p&gt;Design Diff does not capture in haste. It waits for the fonts to load, for the images to arrive, for the network to settle, and it turns off animation and the blinking caret. Most false diffs come from catching a page mid-render. If the fonts still are not ready, it says so in the output instead of handing you a bad score in silence. For an agent looping toward a match, this is what keeps the signal honest. The number moves only when the page truly changed, not when it was caught too early.&lt;/p&gt;

&lt;h3&gt;
  
  
  A score you can read
&lt;/h3&gt;

&lt;p&gt;Every run prints three measures and writes them to metrics.json in the run folder.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;What it tells you&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Visual match&lt;/td&gt;
&lt;td&gt;How many pixels line up. The headline score.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Diff bounds&lt;/td&gt;
&lt;td&gt;The box enclosing every changed pixel, in CSS pixels. A tight box means one component is off. A box spanning the page points at a layout, viewport, or font problem.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Page coverage&lt;/td&gt;
&lt;td&gt;How much of the page that box spans. Tells a local issue from a global one.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The shape of the diff is the diagnosis. A small box on a button is a mistaken color. A box that spans the page is a font that never loaded or a viewport that does not match. Read the shape, not the number alone. The number says something is wrong, the shape says what. For an agent this is a ready-made reward signal. A match percentage to climb, and the diff bounds to point the next edit at.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://localhost:3000"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"matchPercent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;92.4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"diffPercent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;7.6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"changedPixels"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;107251&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"totalPixels"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1411200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"coveragePercent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;17.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"diffBounds"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"x"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"y"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;812&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"width"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1440&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"height"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;168&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"readiness"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"fontsReady"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"imagesComplete"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"paths"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"design"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;".design-diff/2026-08-16T14-22-09-482Z/design.png"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"page"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;".design-diff/2026-08-16T14-22-09-482Z/dom.png"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"heatmap"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;".design-diff/2026-08-16T14-22-09-482Z/heatmap.png"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"overlay"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;".design-diff/2026-08-16T14-22-09-482Z/overlay.html"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"metrics"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;".design-diff/2026-08-16T14-22-09-482Z/metrics.json"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Working with real designs and real pages
&lt;/h2&gt;

&lt;p&gt;Real work is messier than a clean mockup against a static page. Design Diff meets it where it is, whether the source of truth lives in Figma, the page carries content that was always going to change, loads late, sits behind a login, or exists only as a screenshot on disk.&lt;/p&gt;

&lt;h3&gt;
  
  
  Straight from Figma
&lt;/h3&gt;

&lt;p&gt;No manual export. Give it a file key and a frame id and it fetches the frame size and PNG straight from the Figma API.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;DESIGN_DIFF_FIGMA_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;figd_your_token
bunx design-diff http://localhost:3000 &lt;span class="nt"&gt;--file&lt;/span&gt; abc123 &lt;span class="nt"&gt;--frame&lt;/span&gt; 10-2 &lt;span class="nt"&gt;--open&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The frame id is the one in the frame URL, like 10-2. A read-only token is enough. This is the closest thing to a live handoff. The designer moves a component, you run it again, you see the drift at once. An agent can aim straight at a Figma frame as its target and never touch a local file.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mask what is meant to change
&lt;/h3&gt;

&lt;p&gt;Real pages carry things that were always going to change. An avatar, a timestamp, a live counter, a random hero image. Counting those as faults is a quarrel with the nature of the page. Mask them so they never count.&lt;/p&gt;

&lt;p&gt;By a fixed rectangle, in CSS pixels, repeated as often as you like.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bunx design-diff http://localhost:3000 &lt;span class="nt"&gt;--png&lt;/span&gt; home.png &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--ignore&lt;/span&gt; 24,24,48,48 &lt;span class="nt"&gt;--ignore&lt;/span&gt; 0,900,1440,120
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or by CSS selector, which is wiser when the region moves or resizes between runs. Every element that matches is masked by its own live bounding box.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bunx design-diff http://localhost:3000 &lt;span class="nt"&gt;--png&lt;/span&gt; home.png &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--ignore-selector&lt;/span&gt; &lt;span class="s2"&gt;"[data-dynamic], time, .avatar"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Masking by selector is what turns a flaky score into a steady one. It silences the noise that was never a real difference. For an agent iterating toward 100, a stable score is everything, since it needs the number to reflect its own edits and nothing else.&lt;/p&gt;

&lt;h3&gt;
  
  
  Wait for readiness
&lt;/h3&gt;

&lt;p&gt;Some pages wake late, or fetch their hero after a round-trip. Hold the capture until a chosen selector appears. If the selector never shows within fifteen seconds, it fails loudly rather than quietly handing you a bad screenshot.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bunx design-diff http://localhost:3000 &lt;span class="nt"&gt;--png&lt;/span&gt; home.png &lt;span class="nt"&gt;--wait-for&lt;/span&gt; &lt;span class="s2"&gt;".hero-loaded"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Pages behind a login
&lt;/h3&gt;

&lt;p&gt;Save a session once with Playwright, then hand it over. No credentials ever touch the tool itself. The saved file holds only cookies and local storage. When it expires, make it again.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bunx playwright codegen &lt;span class="nt"&gt;--save-storage&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;auth.json https://your-app/login
&lt;span class="c"&gt;# log in in the window, then close it&lt;/span&gt;
bunx design-diff https://your-app/dashboard &lt;span class="nt"&gt;--png&lt;/span&gt; dash.png &lt;span class="nt"&gt;--auth&lt;/span&gt; auth.json &lt;span class="nt"&gt;--open&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  From two images
&lt;/h3&gt;

&lt;p&gt;Sometimes both images already sit on disk and there is no page to visit. Let the browser sleep and lay the two side by side.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bunx design-diff &lt;span class="nt"&gt;--actual&lt;/span&gt; screenshot.png &lt;span class="nt"&gt;--png&lt;/span&gt; design.png &lt;span class="nt"&gt;--json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the fast path an agent uses in a tight loop. It has already captured the page by its own means, so Design Diff only diffs the two files and launches no browser at all. It works offline, in tests, and in sandboxes. With no live page there is no waiting and no selector masking, and scale is ignored since there is no screen to answer to, so the bounds come back in plain image pixels. Everything else is the same.&lt;/p&gt;

&lt;h2&gt;
  
  
  Outputs for agents and humans
&lt;/h2&gt;

&lt;p&gt;The same run answers two very different readers. A machine wants a number and a box. A person wants to see it. Design Diff gives each what it needs from one pass.&lt;/p&gt;

&lt;h3&gt;
  
  
  The interactive report
&lt;/h3&gt;

&lt;p&gt;The HTML report is not a static picture. It is a tool for holding two images as one.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A reveal slider wipes between the design and the page. Drag the handle to move the seam across the frame.&lt;/li&gt;
&lt;li&gt;An opacity slider fades the design onto the page so a small shift shows itself.&lt;/li&gt;
&lt;li&gt;A heatmap lights up every changed pixel so nothing hides.&lt;/li&gt;
&lt;li&gt;A bounds box outlines the changed region so your eye goes straight to it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It is retina-safe. When your export is drawn at twice the size, pass &lt;code&gt;--scale 2&lt;/code&gt; and it keeps the measure honest.&lt;/p&gt;

&lt;h3&gt;
  
  
  A shareable image
&lt;/h3&gt;

&lt;p&gt;The HTML report is good in front of you, but it cannot travel into a pull request comment. Pass &lt;code&gt;--annotate&lt;/code&gt; and it draws the diff box onto the page screenshot itself.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bunx design-diff http://localhost:3000 &lt;span class="nt"&gt;--png&lt;/span&gt; home.png &lt;span class="nt"&gt;--annotate&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No browser needed to view it. Drop it into a review, a Slack thread, a CI artifact. Nothing is written when there is no diff, so an annotated file always means there is something to look at.&lt;/p&gt;

&lt;h3&gt;
  
  
  JSON and the CI gate
&lt;/h3&gt;

&lt;p&gt;Agents and pipelines want the numbers, not the page. &lt;code&gt;--json&lt;/code&gt; prints only the metrics object to stdout, so a script or an agent reads it with no parsing. &lt;code&gt;--fail-under&lt;/code&gt; sets the bar. If the visual match falls below it, the process exits 1 and the build goes red.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bunx design-diff http://localhost:3000 &lt;span class="nt"&gt;--png&lt;/span&gt; home.png &lt;span class="nt"&gt;--json&lt;/span&gt; &lt;span class="nt"&gt;--fail-under&lt;/span&gt; 98
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the agent contract. Read the JSON, act on matchPercent and diffBounds, run again, and stop when the score is high enough.&lt;/p&gt;

&lt;p&gt;One thing to keep straight. &lt;code&gt;--fail-under&lt;/code&gt; is a percentage measured against the visual match. It is not &lt;code&gt;--threshold&lt;/code&gt;, which is the per-pixel color sensitivity. One keeps the gate, the other tunes how strict each pixel comparison is. When you weigh many pages, or the same page again and again, pass &lt;code&gt;--no-overlay&lt;/code&gt; to skip the HTML and keep only the metrics.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using it programmatically
&lt;/h2&gt;

&lt;p&gt;The CLI is a convenience. The loop really lives in the library, one function you can call as fast as you can edit.&lt;/p&gt;

&lt;h3&gt;
  
  
  The function
&lt;/h3&gt;

&lt;p&gt;The CLI is a thin wrapper over one function that returns everything it computed, along with the path to every artifact.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;designDiff&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;design-diff&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;designDiff&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://localhost:3000&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// or actual: "screenshot.png" for image mode&lt;/span&gt;
  &lt;span class="na"&gt;design&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;home.png&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;           &lt;span class="c1"&gt;// or { fileKey, frameId }&lt;/span&gt;
  &lt;span class="na"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;threshold&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;ignore&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;selector&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-dynamic]&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;48&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;48&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
  &lt;span class="na"&gt;waitFor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.hero-loaded&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;matchPercent&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;98&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You get back matchPercent, diffPercent, changedPixels, totalPixels, coveragePercent, diffBounds, the readiness signals, and the paths to the design, page, heatmap, overlay, annotated image, and metrics. The same object it writes to metrics.json, with nothing useful thrown away. An agent reads matchPercent to know how close it is and diffBounds to know where to edit next, then calls the function again.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reuse the browser
&lt;/h3&gt;

&lt;p&gt;Checking a whole app? Launch Chromium once and pass it into every call. It is a real speed win when a suite or an agent runs the tool over and over.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;designDiff&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;launchBrowser&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;design-diff&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;launchBrowser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;pages&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;designDiff&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;design&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;design&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;matchPercent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The honest limits
&lt;/h2&gt;

&lt;p&gt;Design Diff compares pixels, not perception, so read the score as a guide and not a verdict. Anti-aliasing and font rendering vary across machines, so a real page against a design export settles just short of a clean hundred even when it looks right. Choose a threshold rather than chase the last point. The design and the page must be the same size, since a large mismatch is a hard error by design. And a high score is necessary but not sufficient, because a shifted component can still hide inside it. The diff box and a human eye stay the final check.&lt;/p&gt;

&lt;h2&gt;
  
  
  The bottom line
&lt;/h2&gt;

&lt;p&gt;Visual regression tools compare an app to its own past, which catches change no one intended. Design Diff compares the app to the design, which catches the thing that matters at handoff, the gap between what was drawn and what was built.&lt;/p&gt;

&lt;p&gt;It is small and asks no allegiance to a service. Most tools in this space are built for a person to look at a report. This one is built to be read by a machine and acted on in a loop, so an agent can drive the page toward the design on its own, and it still hands a person a picture that ends the argument. Point it at your page, read the box, and close the gap.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/planetabhi/design-diff" rel="noopener noreferrer"&gt;Explore the Design Diff ↗&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>design</category>
      <category>tooling</category>
      <category>agents</category>
    </item>
  </channel>
</rss>
