# How to take a screenshot in a cloud sandbox Start the sandbox's desktop and call `sbx.desktop.screenshot()` for PNG bytes, or run Playwright's `page.screenshot()` for a full page. **On Runtime a screenshot is one call, taken inside a microVM that is thrown away afterwards.** The page you capture runs in its own Firecracker machine, so a hostile site or a broken app never touches your server, and each capture can start from a clean browser. A new sandbox ran its first command 351 ms after the create request at the median on 24 September 2026 ([speed](/docs/speed)), and one that waits on a page load costs $0.03125 an hour at 2 vCPU and 4 GiB, because Runtime bills the CPU it uses ([pricing](/docs/pricing)). ## Capture the screen ```ts check import { writeFile } from "node:fs/promises"; import { Sandbox } from "withruntime"; await using sbx = await Sandbox.create({ diskMiB: 8192 }); await sbx.desktop.start({ width: 1440, height: 900 }); // the first start installs it await sbx.desktop.open("https://example.com"); await writeFile("example.png", await sbx.desktop.screenshot()); ``` ```python check from withruntime import Sandbox with Sandbox.create(disk_mib=8192) as sbx: sbx.desktop.start(width=1440, height=900) sbx.desktop.open("https://example.com") with open("example.png", "wb") as file: file.write(sbx.desktop.screenshot()) ``` ```bash no-run runtime sandbox desktop "${id}" start runtime sandbox desktop "${id}" open https://example.com runtime sandbox desktop "${id}" screenshot example.png ``` The screenshot is the whole screen at the size you started it with: the browser window, its address bar and anything else open. The first start in a sandbox installs the desktop (about 90 seconds and 1 GB of disk, once), and Firefox follows in the background; `open` waits for it. ## Capture an app running in the same sandbox An app an agent just built can be photographed without exposing it to the internet. Start it with `spawn`, which keeps it running, and point the desktop's browser at `localhost`: ```ts check import { writeFile } from "node:fs/promises"; import { Sandbox } from "withruntime"; await using sbx = await Sandbox.create({ diskMiB: 8192, timeoutSeconds: 1800 }); await sbx.files.upload("./site", "/workspace/site"); await sbx.spawn("python3 -m http.server 3000", { cwd: "/workspace/site" }); await sbx.desktop.start({ width: 1280, height: 800 }); await sbx.desktop.open("http://localhost:3000"); await writeFile("home.png", await sbx.desktop.screenshot()); ``` ```python check from withruntime import Sandbox with Sandbox.create(disk_mib=8192, timeout_seconds=1800) as sbx: sbx.files.upload("./site", "/workspace/site") sbx.spawn("python3 -m http.server 3000", cwd="/workspace/site") sbx.desktop.start(width=1280, height=800) sbx.desktop.open("http://localhost:3000") with open("home.png", "wb") as file: file.write(sbx.desktop.screenshot()) ``` To look at the app yourself as well, share port 3000 as a private HTTPS [preview](/docs/javascript#share-a-port). ## A full-page capture with Playwright The desktop captures what fits on the screen. For the whole length of a page, or for thousands of pages from a script, use a headless browser. With an image named `playwright` built as in the [Playwright guide](/integrations/playwright), a capture is one script: ```ts check import { writeFile } from "node:fs/promises"; import { Sandbox } from "withruntime"; const script = ` import { chromium } from "playwright"; const browser = await chromium.launch(); const page = await browser.newPage({ viewport: { width: 1280, height: 800 } }); await page.goto(process.argv[2], { waitUntil: "networkidle" }); await page.screenshot({ path: "full.png", fullPage: true }); await browser.close(); `; await using sbx = await Sandbox.create({ image: "playwright" }); await sbx.files.write("/workspace/full.mjs", script); await sbx.exec(["node", "full.mjs", "https://example.com"], { check: true, timeoutMs: 120_000 }); await writeFile("full.png", await sbx.files.read("/workspace/full.png")); ``` ## Many pages at once Each capture can have a sandbox of its own, so no two pages share cookies, storage or a crashed browser. A paid account runs 100 sandboxes at once to start with, and the free trial eight: ```ts check import { writeFile } from "node:fs/promises"; import { Sandbox } from "withruntime"; const urls = ["https://example.com", "https://example.org", "https://example.net"]; await Promise.all( urls.map(async (url, i) => { await using sbx = await Sandbox.create({ diskMiB: 8192 }); await sbx.desktop.start(); await sbx.desktop.open(url); await writeFile(`page-${i}.png`, await sbx.desktop.screenshot()); }), ); ``` A create past the account's limit waits for room for up to two minutes rather than failing, so a batch larger than the limit queues ([errors and retries](/docs/javascript#errors-and-retries)). ## Which way to take it | Way | Captures | Best for | | ------------------------------------------------ | ------------------------------------- | ----------------------------------- | | `sbx.desktop.screenshot()` | The whole screen, as PNG | Agents that act on what they see | | `runtime sandbox desktop screenshot ` | The same, from a terminal | Scripts and quick checks | | `runtime_sandbox_desktop_screenshot` | The same, as an MCP tool result | Agents connected to Runtime's MCP | | Playwright `page.screenshot()` | One page, visible area or full length | Pages in bulk, visual tests | | `sbx.desktop.recordings` | An MP4 of the screen over time | Showing what happened between shots | ## Mistakes and how Runtime handles them - **A disk too small for a browser.** The default 4 GiB sandbox had about 2.5 GiB free on 24 September 2026, and the desktop and Firefox take about 1.5 GB. Ask for `diskMiB: 8192`. - **Capturing before the page draws.** `open` starts Firefox on the address and returns; it does not wait for a slow page to load. Wait for a known element with Playwright, or take another screenshot after a pause. - **A server started with `exec`.** It ends with its command, and the browser then shows an error page. `spawn` keeps it running. - **Trial ports.** A trial sandbox reaches ports 80 and 443 only, which covers ordinary websites; a paid sandbox reaches any port. ## Related - [Control a desktop](/how-to/control-a-desktop) for clicks, keys and video. - [A browser automation agent](/use-cases/browser-automation-agent). - [Preview agent-built apps](/use-cases/preview-agent-built-apps). ## Start ```bash no-run npx withruntime sandbox run --trial --keep -- echo ready ``` New accounts get 50 free sandbox hours, no card. Facts on this page were checked on 25 September 2026.