Canvas frame capture
@slashclone/canvas-frame-capture
Check a canvas or WebGL animation frame by frame when the browser cannot take screenshots (a hidden pane, a background tab, a headless run). Captures timed frames from the page itself and saves them as small images you can look at. Writes only to a frames folder you choose.
- Activity
- New: be the first to try it
- Installs
- 0 installs
- Likes
- 0 likes
- Clones
- 0 clones
How this works
- It is a skill: written instructions Claude reads when your request matches what the skill is for. You do not call it by name; just ask for the task.
- While it works, it changes files in the places it lists, nowhere else.
- It runs these commands on your computer: python3 -I scripts/frame-receiver.py.
- Nothing runs until you choose to use it, and you can remove it any time.
SKILL.md
Canvas frame capture
Goal: see what an animated canvas actually draws at chosen moments (for example 1, 2.5, and 9 seconds after it first appears), even when the browser will not take a screenshot.
Why screenshots fail
A hidden or background tab does not composite frames, and browsers pause requestAnimationFrame there. The animation never advances, and screenshot tools time out or show a stale frame. Reading pixels from the canvas itself avoids both problems.
Steps
- Make the canvas readable. A WebGL canvas keeps its pixels only if it was created with
preserveDrawingBuffer: true, or if you read it in the same frame it was drawn. If the app draws once and redraws only what changes, it probably already keeps the buffer. Do not change app code just for this without asking. Keep the animation running while hidden. Reload the page, then right away (while
document.readyStateis stillloading) run in the page:window.requestAnimationFrame = (cb) => setTimeout(() => cb(performance.now()), 16)and the matchingcancelAnimationFrame.- If the app stops when
document.hiddenis true, override that getter to return false. If you are too late (the page already loaded), the app's first frame request went to the paused real one; reload and try again.
- Schedule the captures in the page. Wait for the app's own "ready" signal (a class or data attribute it sets after the first full draw), then capture at fixed delays after it. For each capture, draw the canvas into a small 2D canvas (about 560px wide) and keep
toDataURL("image/jpeg", 0.6). Small images keep the next step simple. Get the frames out. Try the simplest route first:
- Return one data URL from a script call and decode it locally.
- If it is too large for the tool, start the receiver in this kit:
python3 -I scripts/frame-receiver.py 8766 frames. It listens on 127.0.0.1 only and saves each query parameter (a data URL) asframes/<name>.jpg. - Pages with a content security policy usually block
fetchto another origin. Navigation is not covered byconnect-src, so after all captures are taken, setlocation.hreftohttp://127.0.0.1:8766/?t1300=<data URL>&t2300=<data URL>, each value passed throughencodeURIComponent. Frame names may use letters, digits,_, and-. - The receiver accepts long request lines (Python's server normally stops at 64 KB), so several small frames fit in one navigation.
- Look at the frames with your image viewer or file reader, and compare them with what the animation should show at each moment.
- Clean up. Stop the receiver, delete the frames if they are not needed, and reload the page so the overrides are gone.
Rules
- Only run this against pages you are developing or have permission to test.
- Never send frames anywhere but
127.0.0.1. They can contain whatever the page shows. - Bind the receiver to
127.0.0.1, never0.0.0.0, and stop it when you are done. - Report what you saw at each time, and say plainly when a capture was blank or stale.
Files
- SKILL.md
- kit.json
- scripts/frame-receiver.py
Did it work?
0worked for them0did not work
Sign in to say whether it worked for you.
Nobody has said yet. Tried it? Tell the next person.
