
Full Page Screenshot
Capture pixel-accurate full-page screenshots of live URLs or open Chrome tabs for visual QA, docs, and launch assets without a separate design tool.
Overview
Full Page Screenshot is an agent skill most often used in Ship (also Launch, Build) that captures full-length page images via Chrome CDP with check, list, URL, and tab-target modes.
Install
npx skills add https://github.com/alirezarezvani/claude-skills --skill full-page-screenshotWhat is this skill?
- Four modes: --check (environment), --list (open tabs JSON), --url (open/wait/capture/close), or screenshot by tab target
- Chrome CDP capture with configurable --width (default 1200), --dpr, and --wait page load timeout (default 15000 ms)
- Requires Node.js 22+ and Chrome with remote debugging enabled
- Standalone full-page stitch via full-page-screenshot.mjs—no separate Puppeteer project required
- Outputs image files to a path you specify alongside optional custom CSS injection via --css
- 4 CLI modes (--check, --list, --url, targetId capture)
- Default viewport width 1200 CSS pixels
- Default page load wait 15000 ms
Adoption & trust: 524 installs on skills.sh; 17.5k GitHub stars; 2/3 security scanners passed (skills.sh audits).
What problem does it solve?
You need full-page visuals of staging or prod pages but viewport-only screenshots and manual scrolling hide layout bugs and marketing-ready assets.
Who is it for?
Builders who already run Chrome with remote debugging and want a fast CLI snapshot for visual QA or asset generation.
Skip if: Headless CI-only pipelines without Chrome debugging, or teams that need authenticated cloud browser grids out of the box.
When should I use this skill?
User needs full-page screenshots via Chrome CDP, environment check, tab listing, or URL capture with width/DPR/wait options.
What do I get? / Deliverables
You get a saved full-page image file from a URL or live tab with tuned width, DPR, and load wait—ready for QA notes or distribution.
- Full-page image file (PNG or chosen output path)
- JSON tab list when using --list
Recommended Skills
Journey fit
Spans multiple journey phases - primary shelf plus alternate fits below.
Ship / testing is the primary shelf because the tool’s main job is verifying how pages render end-to-end before release. Testing covers visual capture for regression checks, marketing previews, and bug reports tied to real browser state.
Where it fits
Run --url against staging with --wait to catch broken footers before tag.
Capture a DPR 2 full page for Product Hunt or social posts from production URL.
Use --list and targetId to iterate on a long settings page without reloading manually.
Archive full blog renders for changelog or lifecycle email assets.
How it compares
Local Chrome CDP CLI capture—not a hosted screenshot API service and not a Playwright test suite.
Common Questions / FAQ
Who is full-page-screenshot for?
Solo developers and designers shipping web apps who want full-length captures from real Chrome for QA, docs, or launch collateral.
When should I use full-page-screenshot?
Use it in Ship/testing for visual regression before release, in Launch/distribution for store or social previews, and in Build/frontend when reviewing long marketing or dashboard pages on localhost.
Is full-page-screenshot safe to install?
It executes local Node scripts and controls Chrome; review the Security Audits panel on this page and only point it at URLs and tabs you trust.
SKILL.md
READMESKILL.md - Full Page Screenshot
#!/usr/bin/env node // full-page-screenshot.mjs — Standalone full-page screenshot tool via Chrome CDP // // Modes: // --check Check environment (Node.js 22+, Chrome debugging port) // --list List open browser tabs as JSON // --url <URL> [output] [options] Screenshot a URL (create tab → wait → capture → close) // <targetId> [output] [options] Screenshot an existing tab by target ID // // Options: // --width N Viewport width in CSS pixels (default: 1200) // --dpr N Device pixel ratio (default: 1) // --wait N Page load timeout in ms, --url mode only (default: 15000) // // Requires: Node.js 22+, Chrome with remote debugging enabled import fs from 'fs'; import net from 'net'; import path from 'path'; import { platform, homedir } from 'os'; // ─── Argument parsing ─────────────────────────────────────────────────────── const args = process.argv.slice(2); const flags = {}; const positional = []; const boolFlags = new Set(['check', 'list']); for (let i = 0; i < args.length; i++) { if (args[i].startsWith('--')) { const key = args[i].slice(2); if (boolFlags.has(key)) { flags[key] = true; } else if (i + 1 < args.length) { flags[key] = args[++i]; } } else { positional.push(args[i]); } } const vpWidth = parseInt(flags.width || '1200', 10); const dpr = parseInt(flags.dpr || '1', 10); const loadTimeout = parseInt(flags.wait || '15000', 10); const customCss = flags.css || ''; // ─── Chrome discovery ─────────────────────────────────────────────────────── function getDevToolsActivePortPaths() { const home = homedir(); if (platform() === 'darwin') { return [ path.join(home, 'Library/Application Support/Google/Chrome/DevToolsActivePort'), path.join(home, 'Library/Application Support/Google/Chrome Canary/DevToolsActivePort'), path.join(home, 'Library/Application Support/Chromium/DevToolsActivePort'), ]; } else if (platform() === 'linux') { return [ path.join(home, '.config/google-chrome/DevToolsActivePort'), path.join(home, '.config/chromium/DevToolsActivePort'), ]; } else if (platform() === 'win32') { const local = process.env.LOCALAPPDATA || ''; return [ path.join(local, 'Google/Chrome/User Data/DevToolsActivePort'), path.join(local, 'Chromium/User Data/DevToolsActivePort'), ]; } return []; } function checkPort(port) { return new Promise((resolve) => { const socket = net.createConnection(port, '127.0.0.1'); const timer = setTimeout(() => { socket.destroy(); resolve(false); }, 2000); socket.once('connect', () => { clearTimeout(timer); socket.destroy(); resolve(true); }); socket.once('error', () => { clearTimeout(timer); resolve(false); }); }); } async function discoverChrome() { // 1. Try DevToolsActivePort file for (const p of getDevToolsActivePortPaths()) { try { const lines = fs.readFileSync(p, 'utf-8').trim().split('\n'); const port = parseInt(lines[0], 10); if (port > 0 && port < 65536) { const ok = await checkPort(port); if (ok) { const wsPath = lines[1] || '/devtools/browser'; return { port, wsUrl: `ws://127.0.0.1:${port}${wsPath}` }; } } } catch { /* try next */ } } // 2. Fallback: probe common debugging ports for (const port of [9222, 9229, 9333]) { const ok = await checkPort(port); if (ok) { return { port, wsUrl: `ws://127.0.0.1:${port}/devtools/browser` }; } } return null; } // ─── CDP WebSocket helpers ────────────────────────────────────────────────── let msgId = 0; const pending = new Map(); let ws; function send(method, params = {}, sessionId = null, timeoutMs = 60000) { return new Promise((resolve, reject) => { const id = ++msgId; const msg = { id, method, params }; if (sessionId) msg.sessionId = sessionId; pending.set(id, resolve); ws.send(JSON.stringify(msg)); setT