docs(screenshots): backgrounded tabs pause rAF — screenshots of animated pages can lie#511
docs(screenshots): backgrounded tabs pause rAF — screenshots of animated pages can lie#511zacklavin11 wants to merge 1 commit into
Conversation
…ted pages can lie Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019DKKK5uNG9DzvUGtyGViye
✅ Skill review passedReviewed 1 file(s) — no findings. |
There was a problem hiding this comment.
2 issues found across 1 file
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="interaction-skills/screenshots.md">
<violation number="1" location="interaction-skills/screenshots.md:25">
P2: DOM reads avoid screenshot-pixel artifacts, but they are not immune to a paused rAF animation: they return the same frozen intermediate text and geometry that the paused callbacks last wrote. This can make the guidance report a stale count-up or layout as authoritative. Suggest describing DOM reads as an inspection of the current state and requiring visibility plus an animation-completion condition before treating the value as final.</violation>
<violation number="2" location="interaction-skills/screenshots.md:26">
P2: Two matching reads do not establish that an animated value is final; a paused or temporarily held animation yields identical intermediate values. This can recreate the stale-frame false positive the section is meant to prevent. Suggest using matching samples only as a stability check and requiring the page/app's animation completion signal (or another known terminal condition) before calling a value final.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Fix all with cubic | Re-trigger cubic
| - Probe `js("document.visibilityState")` — anything but `"visible"` means the screenshot may lie. | ||
| - Foreground the tab (`Target.activateTarget` via `cdp(...)`) and nudge it (scroll 1px or dispatch a mousemove) to restart rAF, then re-screenshot. | ||
| - For numbers and layout, prefer DOM reads (`js(...)` with `getBoundingClientRect` / `scrollHeight`) over pixels — they are immune to the pause. | ||
| - Animated values need two samples a few seconds apart that match before you read them as final. |
There was a problem hiding this comment.
P2: Two matching reads do not establish that an animated value is final; a paused or temporarily held animation yields identical intermediate values. This can recreate the stale-frame false positive the section is meant to prevent. Suggest using matching samples only as a stability check and requiring the page/app's animation completion signal (or another known terminal condition) before calling a value final.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At interaction-skills/screenshots.md, line 26:
<comment>Two matching reads do not establish that an animated value is final; a paused or temporarily held animation yields identical intermediate values. This can recreate the stale-frame false positive the section is meant to prevent. Suggest using matching samples only as a stability check and requiring the page/app's animation completion signal (or another known terminal condition) before calling a value final.</comment>
<file context>
@@ -15,3 +15,12 @@ capture_screenshot("/tmp/shot.png", max_dim=1800)
+- Probe `js("document.visibilityState")` — anything but `"visible"` means the screenshot may lie.
+- Foreground the tab (`Target.activateTarget` via `cdp(...)`) and nudge it (scroll 1px or dispatch a mousemove) to restart rAF, then re-screenshot.
+- For numbers and layout, prefer DOM reads (`js(...)` with `getBoundingClientRect` / `scrollHeight`) over pixels — they are immune to the pause.
+- Animated values need two samples a few seconds apart that match before you read them as final.
</file context>
| - Animated values need two samples a few seconds apart that match before you read them as final. | |
| - Treat matching samples only as a stability check; use the app's completion signal (or another known terminal condition) before treating animated values as final. |
|
|
||
| - Probe `js("document.visibilityState")` — anything but `"visible"` means the screenshot may lie. | ||
| - Foreground the tab (`Target.activateTarget` via `cdp(...)`) and nudge it (scroll 1px or dispatch a mousemove) to restart rAF, then re-screenshot. | ||
| - For numbers and layout, prefer DOM reads (`js(...)` with `getBoundingClientRect` / `scrollHeight`) over pixels — they are immune to the pause. |
There was a problem hiding this comment.
P2: DOM reads avoid screenshot-pixel artifacts, but they are not immune to a paused rAF animation: they return the same frozen intermediate text and geometry that the paused callbacks last wrote. This can make the guidance report a stale count-up or layout as authoritative. Suggest describing DOM reads as an inspection of the current state and requiring visibility plus an animation-completion condition before treating the value as final.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At interaction-skills/screenshots.md, line 25:
<comment>DOM reads avoid screenshot-pixel artifacts, but they are not immune to a paused rAF animation: they return the same frozen intermediate text and geometry that the paused callbacks last wrote. This can make the guidance report a stale count-up or layout as authoritative. Suggest describing DOM reads as an inspection of the current state and requiring visibility plus an animation-completion condition before treating the value as final.</comment>
<file context>
@@ -15,3 +15,12 @@ capture_screenshot("/tmp/shot.png", max_dim=1800)
+
+- Probe `js("document.visibilityState")` — anything but `"visible"` means the screenshot may lie.
+- Foreground the tab (`Target.activateTarget` via `cdp(...)`) and nudge it (scroll 1px or dispatch a mousemove) to restart rAF, then re-screenshot.
+- For numbers and layout, prefer DOM reads (`js(...)` with `getBoundingClientRect` / `scrollHeight`) over pixels — they are immune to the pause.
+- Animated values need two samples a few seconds apart that match before you read them as final.
</file context>
| - For numbers and layout, prefer DOM reads (`js(...)` with `getBoundingClientRect` / `scrollHeight`) over pixels — they are immune to the pause. | |
| - For numbers and layout, use DOM reads (`js(...)` with `getBoundingClientRect` / `scrollHeight`) to inspect the current state, but treat them as stale until the page is visible and the animation has completed. |
Field-tested across many QA runs on an animation-heavy React app: Chrome pauses
requestAnimationFramein backgrounded/occluded tabs, so screenshots of count-ups and reveals silently show blank or stale frames while the page reports loaded. This adds the gotcha and the recovery recipe (visibilityState probe, foreground + nudge, DOM reads over pixels, two-sample rule for animated values) to the screenshots skill.🤖 Generated with Claude Code
https://claude.ai/code/session_019DKKK5uNG9DzvUGtyGViye
Summary by cubic
Documented that Chrome pauses
requestAnimationFramein backgrounded or occluded tabs, which can make screenshots of animations show blank or stale frames even when the page reports loaded. Added a short checklist to detect and recover: checkdocument.visibilityState, foreground and nudge the tab (Target.activateTarget, small scroll or mousemove), prefer DOM reads (getBoundingClientRect/scrollHeight) over pixels, and confirm animated values with two matching samples a few seconds apart.Written for commit 9c2075e. Summary will update on new commits.