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), 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).
Capture the screen
TypeScriptimport { 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 itawait sbx.desktop.open("https://example.com");await writeFile("example.png", await sbx.desktop.screenshot());Pythonfrom withruntime import Sandboxwith 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())Terminalruntime sandbox desktop "${id}" startruntime sandbox desktop "${id}" open https://example.comruntime sandbox desktop "${id}" screenshot example.pngThe 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:
TypeScriptimport { 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());Pythonfrom withruntime import Sandboxwith 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.
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,
a capture is one script:
TypeScriptimport { 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:
TypeScriptimport { 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).
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 <id> screenshot <file> |
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.
openstarts 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.spawnkeeps 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 for clicks, keys and video.
- A browser automation agent.
- Preview agent-built apps.
Start
Terminalnpx withruntime sandbox run --trial --keep -- echo readyNew accounts get 50 free sandbox hours, no card.
Facts on this page were checked on 25 September 2026.