Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions interaction-skills/screenshots.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,12 @@ capture_screenshot("/tmp/shot.png", max_dim=1800)
The downscale only happens when the image actually exceeds `max_dim`, so it's safe to leave on for every shot.

Use full-page screenshots (`full=True`) only when you need to see content below the fold β€” they are much larger and slower than viewport-only.

## Backgrounded tabs freeze animations

Chrome pauses `requestAnimationFrame` in backgrounded or occluded tabs, so count-ups, reveals, and JS-driven animations render blank or stale in screenshots even though the page is "loaded". Before trusting a screenshot of anything animated:

- 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.

@cubic-dev-ai cubic-dev-ai Bot Jul 12, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
Suggested change
- 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.
Fix with cubic

- Animated values need two samples a few seconds apart that match before you read them as final.

@cubic-dev-ai cubic-dev-ai Bot Jul 12, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
Suggested change
- 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.
Fix with cubic