Skip to content

Headless Chromium screenshots - CLI and Node.js SDK.

License

Notifications You must be signed in to change notification settings

benoitpetit/screencut

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

7 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

screencut

Logo AgentCode

Headless Chromium screenshots — CLI and Node.js SDK.

npm version license node


Install

npm install screencut

Chromium is downloaded automatically by Puppeteer on install. If you already have Chromium on your system (Docker, CI), skip the download and point to your binary instead:

PUPPETEER_SKIP_DOWNLOAD=true npm install screencut
PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium screencut https://example.com

CLI

npx screencut <url> [output] [options]

output defaults to screenshot.png. The image format is inferred from the file extension when --type is not set.

Flag Default Description
--width 1280 Viewport width in pixels
--height 720 Viewport height in pixels
--type png Image format: png, jpeg, webp
--quality 80 Compression quality for jpeg / webp (0–100)
--scale 1 Device pixel ratio for HiDPI output (2 = retina)
--full-page off Capture the full scrollable page
--wait-for CSS selector to wait for before capturing
--wait-until networkidle2 Navigation strategy: load, domcontentloaded, networkidle0, networkidle2
--timeout 30000 Navigation timeout in ms
--user-agent Override the browser user-agent string
--stdout off Write image bytes to stdout instead of a file
-v, --version Print version and exit
-h, --help Show help
# Save to a file
screencut https://example.com
screencut https://example.com shot.png

# Full-page JPEG
screencut https://example.com shot.jpeg --full-page --quality 90

# Retina screenshot
screencut https://example.com retina.png --scale 2

# Faster capture — skip waiting for network idle
screencut https://example.com --wait-until domcontentloaded

# Wait for a specific element to appear
screencut https://example.com --wait-for "#hero" --timeout 10000

# Mobile user-agent
screencut https://example.com --user-agent "Mozilla/5.0 (iPhone; CPU iPhone OS 17_0)"

# Pipe bytes to another tool
screencut https://example.com --stdout | convert - -resize 400x thumb.jpg
screencut https://example.com --stdout > shot.png

SDK

const screencut = require('screencut');

require('screencut') returns a ready-to-use instance. No setup needed.


capture(url, options?)Promise<Buffer>

const buf = await screencut.capture('https://example.com');

const buf = await screencut.capture('https://example.com', {
  width: 1920,
  height: 1080,
  type: 'jpeg',
  quality: 90,
  fullPage: true,
});

captureAsStream(url, options?)Readable

Returns a stream synchronously — the capture runs in the background and data is pushed when ready. Pipe it anywhere without awaiting.

// Stream to an HTTP response
res.setHeader('Content-Type', 'image/png');
screencut.captureAsStream('https://example.com').pipe(res);

// Stream to a file
screencut.captureAsStream('https://example.com').pipe(fs.createWriteStream('shot.png'));

captureAsBase64(url, options?)Promise<string>

const b64 = await screencut.captureAsBase64('https://example.com');
// "iVBORw0KGgoAAAANSUhEUgAA..."

captureAsDataURL(url, options?)Promise<string>

const dataUrl = await screencut.captureAsDataURL('https://example.com', {
  type: 'jpeg',
  quality: 85,
});
// "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQ..."

const html = `<img src="${dataUrl}" />`;

captureToFile(url, outputPath, options?)Promise<void>

await screencut.captureToFile('https://example.com', './shot.png');

await screencut.captureToFile('https://example.com', './shot.jpeg', {
  type: 'jpeg',
  quality: 80,
  fullPage: true,
});

close()Promise<void>

Closes the underlying Chromium instance. The default instance closes itself automatically on SIGINT, SIGTERM, and beforeExit. Call close() manually when using a custom instance.

await screencut.close();

Options

All capture methods accept the same options object. Every field is optional.

Option Type Default Description
width number 1280 Viewport width in pixels
height number 720 Viewport height in pixels
type string 'png' Image format: 'png', 'jpeg', 'webp'
quality number 80 Compression quality for jpeg and webp (0–100)
fullPage boolean false Capture the full scrollable page
waitFor string CSS selector to wait for before capturing
waitUntil string 'networkidle2' Navigation strategy: 'load', 'domcontentloaded', 'networkidle0', 'networkidle2'
timeout number 30000 Navigation timeout in milliseconds
userAgent string Override the browser user-agent string
deviceScaleFactor number 1 Device pixel ratio for HiDPI / retina output
clip object Restrict capture to a region: { x, y, width, height }. Ignored when fullPage is true.

Custom instance

Use new Screencut(defaults?) when you need an isolated browser with its own defaults. You are responsible for calling close().

const { Screencut } = require('screencut');

const mobile = new Screencut({
  width: 390,
  height: 844,
  userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15',
});

const buf = await mobile.capture('https://example.com');
await mobile.close();

LaunchDefaults extends all capture options and adds two browser-level fields:

Option Type Default Description
headless boolean true Set to false to debug captures visually
args string[] [...] Additional Chromium launch flags

Recipes

Express screenshot endpoint

const express = require('express');
const screencut = require('screencut');

const app = express();

app.get('/screenshot', (req, res) => {
  const { url } = req.query;
  if (!url) return res.status(400).send('Missing ?url=');

  res.setHeader('Content-Type', 'image/png');
  screencut.captureAsStream(url).pipe(res);
});

app.listen(3000);

Thumbnail generation with sharp

const sharp = require('sharp');
const screencut = require('screencut');

const buf = await screencut.capture('https://example.com');
const thumb = await sharp(buf).resize(400, 225).toBuffer();

Batch captures

One instance handles multiple concurrent captures — no need to spawn a browser per URL.

const screencut = require('screencut');

const urls = ['https://example.com', 'https://github.com', 'https://nodejs.org'];

const buffers = await Promise.all(urls.map(url => screencut.capture(url)));

If you need truly isolated browser processes (different proxies, separate sessions), create one instance per process:

const { Screencut } = require('screencut');

const a = new Screencut({ args: ['--proxy-server=socks5://proxy1:1080'] });
const b = new Screencut({ args: ['--proxy-server=socks5://proxy2:1080'] });

const [bufA, bufB] = await Promise.all([
  a.capture('https://example.com'),
  b.capture('https://example.com'),
]);

await Promise.all([a.close(), b.close()]);

Clip a region

const buf = await screencut.capture('https://example.com', {
  clip: { x: 0, y: 0, width: 600, height: 400 },
});

TypeScript

Types are bundled. No @types package needed.

import screencut, { Screencut, CaptureOptions, LaunchDefaults } from 'screencut';

const options: CaptureOptions = {
  width: 1440,
  type: 'webp',
  quality: 90,
  fullPage: true,
  waitUntil: 'domcontentloaded',
};

const buf: Buffer = await screencut.capture('https://example.com', options);
import { Screencut, LaunchDefaults } from 'screencut';

const defaults: LaunchDefaults = {
  width: 390,
  height: 844,
  headless: true,
};

const instance = new Screencut(defaults);
const buf = await instance.capture('https://example.com');
await instance.close();

Requirements

  • Node.js >= 18.0.0
  • Chromium — downloaded automatically by Puppeteer on npm install

License

MIT

About

Headless Chromium screenshots - CLI and Node.js SDK.

Topics

Resources

License

Stars

Watchers

Forks

Sponsor this project