Teaching Coding Agents to Tell the Visual Story of a UI Change
Agents can already drive a UI change in a real browser and screenshot every state, but the frames died in /tmp and the PR just said "verified". A rule, an agent skill and a small hosting service turn that browser pass into a visual story on every UI pull request: before, after and a GIF. Here's how it fits together, with setup in three commands.
Most of my code is written by coding agents now, and they're good at front-end work. Every Rising Company project also requires that a UI change be driven end to end in a real browser with agent-browser before it counts as done. So the agent opens the page, clicks through the change, measures the layout and takes screenshots.
Then those screenshots sat in /tmp, and the pull request said "verified in the browser".
As the reviewer, I had two options. I could check out the branch, start the dev server and look for myself, which defeats the point of delegating. Or I could approve a UI change I had never seen, on the strength of a sentence nobody could check. I did more of the second than I'd like to admit.
Capture is the missing piece that closes that gap, and you can use it too. Setup instructions are at the end of this post.
What Capture is
Capture is screenshot hosting for GitHub pull requests. You upload an image from a terminal or from an agent, and you get back a link plus a line of Markdown to paste:
capture --alt "checkout after the fix" shot.png
# 
That's the whole interface. There's a CLI for agents with a shell, a Claude Code skill that teaches the agent when and how to use it, and an MCP server for clients without a shell.
It sounds too small to be worth building, so here's why it isn't.
A PR can only show an image that has a URL
GitHub fetches images in PR comments through its camo proxy, anonymously. So an image has to live at a URL that anyone can fetch, with no cookie and no token. Committing screenshots to the repo bloats it forever. GitHub's API has no way to attach an image to a comment: the upload behind drag-and-drop works only in the web editor, so it needs a human with a browser. Neither works for an agent in a terminal.
Capture's answer is an unguessable URL. The long random key is the only lock. That's a deliberate trade, and the rules follow from it:
- Anyone with the link can view the image. Use seed or demo data. Never upload secrets, tokens or a real person's records.
- Uploading needs approval. Anyone with a Rising ID can sign in, but an admin approves the account before it can upload.
- Only what GitHub renders: PNG, JPEG, WebP and GIF, up to 4 MiB. SVG is refused.
- Deleting stops Capture serving an image within a couple of minutes, though GitHub may keep its own cached copy.
Signing in is a human's job, uploading is the agent's
capture login prints a URL and a code. I open it, sign in with Rising ID and a second factor, check that the code matches, and approve. From then on the agent uploads on its own. If the CLI isn't signed in or the account is still pending, the agent is told to say so in its report rather than quietly skip the screenshots.
The habit: a visual story on every UI pull request
A hosting service alone doesn't change anything. What changed things was writing the habit down. Our shared agent handbook now has a rule (ADR-0015): every PR that changes what a user can see carries a visual story, posted as one PR comment, in this order:
- one sentence of context
- before, for a change to an existing surface
- after
- a GIF of any interaction
- the states that matter: empty, populated, error, and 390px wide if the layout moved
A later push gets a short follow-up comment, but only if it changed what renders.
A pre-push-ui-review skill is the procedure. The agent shoots the before frames first, from main or the live site, while the old UI still exists. It keeps a frame ledger in the git directory so the frames survive handoffs and context compaction. It records the interaction. Then a script, check-visual-story.sh, refuses a comment that's missing the GIF it owes, unless it says No GIF: and why.
The key detail is that the frames come from the browser pass the agent already had to do. The handbook already required that pass. Capture just stops it from being thrown away.
What it looks like: a Rubik's cube tutorial, PR #1
Here's a real one from today. rubik is a small personal project: a step-by-step 3×3 Rubik's cube tutorial, in English and Chinese, where every cube on the page can be dragged around in 3D.
That last part was the bug. On a phone, swiping down over a cube rotated the cube instead of scrolling the page, and the page is mostly cubes. I asked an agent to fix it, plus a few layout problems I'd noticed along the way.
PR #1, "Make cube rotation opt-in on touch; add reset controls" did this:
- Rotation is opt-in on touch screens. A touch drag scrolls the page. Tapping a cube's rotate icon turns on drag-rotation for that cube and shows a blue ring. Mouse dragging on desktop is unchanged, and the icon is hidden there.
- Reset controls. Each cube gets a reset icon once it has moved, and a Reset all button appears in the sticky stage bar while any cube is off its start view.
- Layout fixes. Jumping to a step no longer hides its heading under the stage bar, and the long formulas in steps 4, 5 and 7 use full-width rows.
The page is a single static HTML file with no test suite, so the browser pass was the only verification there was. Here is what the agent posted.
The interaction, at 390px
Tap the rotate icon (ring appears), drag to rotate, then Reset all in the stage bar returns the cube and hides itself.
This is the frame that matters most. The whole bug was about how touch behaves, and a static screenshot can't show that.
Before / after: jumping to a step on mobile
Look at the step heading: before, it sits under the sticky stage bar.
Before / after: long formulas, desktop
Look at card height and alignment: the narrow cards become cube-left, formula-right rows.
The comment closed with dark mode in Chinese, showing that the reset icon and 全部复位 ("reset all") appear after a mouse drag, while the rotate icon stays hidden because a mouse doesn't need it.
The follow-up
While the PR was open, I asked the agent to restyle the page in our design system's Daylight theme. That push changed what renders, so it posted a short second comment with three more before/after pairs: the hero, step 1, and step 3 in Chinese at 390px. The hero pair is the cover of this post. That comment ends with a line the checker requires:
No GIF: this is a restyle only. The rotate and reset behaviour hasn't changed since the GIF in the first comment.
What I did as the reviewer
The PR was opened at 22:18 UTC and merged at 22:34, restyle included. I never checked out the branch.
That's not because I trusted the agent blindly. It's because I didn't have to trust it. The GIF answered "does touch work the way I asked?". The before/after pairs answered "did it fix what I saw?". The dark-mode Chinese frame answered "did it break the other theme or the other language?", the regressions I'd have worried about most on a bilingual page.
And the description was honest about what the frames couldn't show. The emulator doesn't report a touch pointer, so the agent tested touch with synthetic pointer events and forced the touch-only styles on for the screenshots, and it said so: "A quick swipe test on a real phone is still worth doing." That told me exactly what was left for me to check: one swipe on my phone, not the whole page.
Why it's worth it
Claims become checkable. "Verified in the browser" used to be a sentence. Now it's a set of frames. An agent that skipped the browser pass can't produce them, and an agent that did it badly produces frames that show it.
Review gets cheap where it was expensive. Looking at a few images and a GIF takes a minute or two. Checking out a branch, installing dependencies and starting a dev server takes ten, and on a phone it isn't possible at all. Small UI PRs stop piling up.
It costs almost nothing extra. The browser pass was already required, so the screenshots already existed. Capture adds an upload and a comment, a minute or two per PR. The before frames are the only new work, and they're the ones that make a story convincing.
The record outlives the branch. Six months from now, when someone asks why the cubes need a tap before they rotate on a phone, the answer is a PR comment with a GIF, not a diff of pointer-event handlers to imagine.
A good tool is mostly a good rule. Capture is a small service. Most of its value comes from the ADR that says when to use it, the skill that says how, and the script that refuses a story with a gap in it. Making the right thing the easy thing is what turns a habit into something an agent does every time.
Try Capture
If you're letting agents ship UI changes, I'd like you to try it. It takes three commands and a couple of minutes.
1. Install the CLI
npm i -g https://capture.rising.company/capture-cli.tgz
2. Sign in
capture login
It prints a URL and a code. Open the URL, sign in with a Rising ID, check that the code matches, and approve. New accounts start as waiting; I approve them by hand, and until then uploads are refused. capture whoami tells you where you stand. If you've been waiting a while, leave a comment on this post.
3. Teach your agent
capture skill install --global
This installs the share-screenshot skill for Claude Code in every project (leave off --global to install it into the current project's .claude/skills only). From then on, when your agent has a screenshot that belongs in a PR, it uploads it and pastes the Markdown itself. Ask it to "post a before/after of this change on the PR" and it knows what to do.
If your client can't run a shell, use the MCP server instead:
capture mcp install
That connects Claude Code to https://capture.rising.company/mcp with the token from capture login. Any other MCP client can use the same endpoint with Authorization: Bearer cap_….
Then upload something
capture --alt "settings page after the fix" /tmp/settings.png
capture list # your recent uploads
capture delete <url> # stop serving one
One rule to keep in mind: anyone with the link can see the image. Use demo data, and keep secrets and real people's records out of the frame.
Start at capture.rising.company. Your reviewers will thank you.
If you enjoyed it, you can buy me a coffee ☕.