Skip to main content

headless-screenshots

Use this skill when asked to take browser screenshots of web pages or web-based tools in a headless/automated way — especially when the page uses HTML canvas (e.g. Cytoscape.js, WebGL, Chart.js) and the screenshots need to show canvas-rendered content like graph nodes, edge labels, or drawn shapes. Also use when asked to automate multi-step browser interactions (clicks, drags, form fills) before screenshotting.

Source facts

Repository
NVIDIA/TensorRT
Last source activity
September 22, 2026 at 19:37
Detected SKILL.md language
English
Stars
13,405
Forks
2,414

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
headless-screenshots
description
Use this skill when asked to take browser screenshots of web pages or web-based tools in a headless/automated way — especially when the page uses HTML canvas (e.g. Cytoscape.js, WebGL, Chart.js) and the screenshots need to show canvas-rendered content like graph nodes, edge labels, or drawn shapes. Also use when asked to automate multi-step browser interactions (clicks, drags, form fills) before screenshotting.
# Headless Browser Screenshots with Playwright ## Stack - **Playwright** (Python) — `pip install playwright && python3 -m playwright install chromium` - **Chromium headless shell** — installed by the command above ## Critical: Canvas rendering requires software rendering Headless Chromium GPU-composites canvas off-screen by default. `canvas.getContext('2d').getImageData()` returns all-zero pixels, and canvas-rendered content (text labels, shapes drawn via `fillText`/`strokeRect`, etc.) does **not** appear in `page.screenshot()`. **Fix: always launch with `--use-gl=swiftshader`** ```python browser = pw.chromium.launch( headless=True, args=["--disable-gpu", "--use-gl=swiftshader"], ) ``` This forces software rendering so canvas content is fully composited into screenshots. ### How to tell you need this - DOM-rendered content (divs, HTML overlays) shows up fine in screenshots - Canvas-drawn content (graph edges, IO node labels, chart axes) is missing - `canvas.getContext('2d').getImageData(x, y, 1, 1).data` returns `[0, 0, 0, 0]` everywhere ## Serving pages Serve HTML via a local HTTP server — snap-confined browsers and sandboxed environments block `file://` paths: ```python srv = subprocess.Popen( ["python3", "-m", "http.server", "8765"], cwd="/tmp/my_assets", stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) # wait for it to be ready import socket, time def wait_for_port(port, timeout=10): deadline = time.time() + timeout while time.time() < deadline: try: socket.create_connection(("127.0.0.1", port), 0.5).close(); return True except OSError: time.sleep(0.2) ``` ## Wait times for JS-heavy pages After `wait_until="networkidle"`, add extra sleep for pages that run layout algorithms (e.g. Cytoscape dagre, D3 force-directed): ```python page.goto(url, wait_until="networkidle") time.sleep(3.5) # let JS layout finish rendering ``` ## Accessing Cytoscape.js instances `window.cy` is shadowed by the `#cy` DOM element (browser auto-creates globals from element IDs). Use the internal registry instead: ```python GET_CY = "document.getElementById('cy')._cyreg.cy" # Get rendered node positions positions = page.evaluate(f""" () => {{ var cy = {GET_CY}; var result = []; cy.nodes('.op').forEach(function(n) {{ var pos = n.renderedPosition(); result.push({{x: pos.x, y: pos.y}}); }}); return result; }} """) ``` `renderedPosition()` returns pixel coordinates relative to the canvas container div (not the page). ## Box-select / drag interactions ```python canvas_box = page.locator("#cy").bounding_box() cx, cy_y = canvas_box["x"], canvas_box["y"] page.mouse.move(cx + x1, cy_y + y1) page.mouse.down() page.mouse.move(cx + x2, cy_y + y2, steps=12) # steps for smooth drag page.mouse.up() time.sleep(1.5) # let selection + panel update ``` ## Avoiding identifying paths in screenshots If a page displays the model/file path in its UI (e.g. in a command preview), copy the file to a neutral temp location before serving: ```python import shutil, pathlib shutil.copy(src_model, "/tmp/polygraphy_docs/model.onnx") # serve from /tmp/polygraphy_docs so displayed path is non-identifying ``` ## Full boilerplate ```python import subprocess, time, socket, glob, os from pathlib import Path from playwright.sync_api import sync_playwright CHROMIUM_ARGS = ["--disable-gpu", "--use-gl=swiftshader"] VIEWPORT = {"width": 1600, "height": 900} def wait_for_server(port, timeout=15): deadline = time.time() + timeout while time.time() < deadline: try: socket.create_connection(("127.0.0.1", port), 0.5).close(); return True except OSError: time.sleep(0.2) return False with sync_playwright() as pw: browser = pw.chromium.launch(headless=True, args=CHROMIUM_ARGS) page = browser.new_page(viewport=VIEWPORT) page.goto("http://127.0.0.1:PORT/", wait_until="networkidle") time.sleep(3.5) page.screenshot(path="output.png") browser.close() ```
View on GitHub