Blog / Puppeteer Screenshot Too Slow? 7 Ways to...

Puppeteer Screenshot Too Slow? 7 Ways to Fix It

Speed up your Puppeteer screenshots by 10x with these optimization techniques. Learn buffer usage, viewport reduction, and when to consider alternatives.

ScreenCraft Team
| | 6 min read

TL;DR: Speed up Puppeteer screenshots by: 1) Using buffers instead of files, 2) Reducing viewport size, 3) Blocking ads/trackers, 4) Using JPG format, 5) Setting optimizeForSpeed: true. For high-volume needs, consider a Screenshot API instead.

Why Are Puppeteer Screenshots Slow?

Puppeteer launches a full Chromium browser instance for each screenshot operation. This includes browser startup time, page rendering, JavaScript execution, and image encoding. Without optimization, a single screenshot can take 3-10 seconds.

Let's explore seven proven techniques to dramatically reduce screenshot capture time.

1. Use Buffers Instead of Files

Writing to disk is significantly slower than keeping data in memory. Return the screenshot as a buffer instead of saving to a file.

// Slow: Writing to disk
await page.screenshot({ path: 'screenshot.png' });

// Fast: Return as buffer
const buffer = await page.screenshot();
// Process buffer in memory or stream to storage

This alone can save 100-500ms per screenshot, especially on slower storage systems.

2. Reduce Viewport Size

Larger viewports mean more pixels to render and encode. Use the minimum viewport size that meets your requirements.

// Large viewport (slow)
await page.setViewport({ width: 1920, height: 1080 });

// Optimized viewport (faster)
await page.setViewport({ width: 1280, height: 720 });

// Mobile viewport (fastest)
await page.setViewport({ width: 375, height: 667 });

A 1280x720 screenshot is 44% smaller than 1920x1080, which translates to significant speed improvements.

3. Block Ads and Trackers

Third-party scripts, ads, and trackers can add seconds to page load time. Block these resources to speed up rendering.

await page.setRequestInterception(true);

page.on('request', (request) => {
  const blockedDomains = [
    'googleads', 'doubleclick', 'facebook',
    'analytics', 'tracking', 'adservice'
  ];

  const url = request.url();
  const shouldBlock = blockedDomains.some(domain =>
    url.includes(domain)
  );

  if (shouldBlock) {
    request.abort();
  } else {
    request.continue();
  }
});

This can reduce page load time by 1-5 seconds on ad-heavy websites.

4. Use JPG Format Instead of PNG

JPG encoding is faster than PNG, and the file size is smaller. Use JPG when you don't need transparency.

// Slower (PNG)
await page.screenshot({ type: 'png' });

// Faster (JPG with quality setting)
await page.screenshot({
  type: 'jpeg',
  quality: 80  // 80% quality is usually sufficient
});

JPG screenshots are typically 2-5x faster to encode than PNG.

5. Enable optimizeForSpeed Flag

Puppeteer 19+ includes an optimizeForSpeed option that uses a faster encoding algorithm.

await page.screenshot({
  type: 'png',
  optimizeForSpeed: true
});

This can reduce encoding time by 30-50% for PNG screenshots.

6. Reuse Browser Instances

Browser startup takes 1-3 seconds. Reuse browser instances across multiple screenshots instead of launching a new browser each time.

// Bad: New browser for each screenshot
async function captureScreenshot(url) {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto(url);
  const screenshot = await page.screenshot();
  await browser.close();
  return screenshot;
}

// Good: Reuse browser instance
let browser;

async function getBrowser() {
  if (!browser) {
    browser = await puppeteer.launch();
  }
  return browser;
}

async function captureScreenshot(url) {
  const browser = await getBrowser();
  const page = await browser.newPage();
  await page.goto(url);
  const screenshot = await page.screenshot();
  await page.close();  // Close page, not browser
  return screenshot;
}

7. Use Faster Wait Strategies

networkidle0 waits for zero network connections, which can be slow. Use faster alternatives when appropriate.

// Slowest: Wait for zero network activity
await page.goto(url, { waitUntil: 'networkidle0' });

// Medium: Wait for max 2 connections
await page.goto(url, { waitUntil: 'networkidle2' });

// Faster: Wait for DOM content
await page.goto(url, { waitUntil: 'domcontentloaded' });

// Fastest: Just wait for load event
await page.goto(url, { waitUntil: 'load' });

Performance Comparison

Optimization Time Saved Effort
Buffer instead of file 100-500ms Low
Smaller viewport 200-800ms Low
Block ads/trackers 1-5s Medium
JPG format 100-300ms Low
optimizeForSpeed 50-200ms Low
Reuse browser 1-3s Medium
Faster wait strategy 500ms-2s Low

When to Consider Alternatives

Even with all optimizations, self-hosted Puppeteer has limitations:

  • Each screenshot still takes 1-3 seconds minimum
  • Scaling requires managing multiple browser instances
  • Memory usage is high (200-500MB per browser)
  • Browser crashes require restart logic

For high-volume or production use cases, a Screenshot API eliminates these concerns with sub-second response times, automatic scaling, and zero maintenance.

Conclusion

By implementing these optimizations, you can reduce Puppeteer screenshot time from 5-10 seconds to 1-2 seconds. For the fastest results with zero infrastructure overhead, consider using a dedicated Screenshot API.

Try ScreenCraft's API - get 100 free screenshots per month with sub-2-second response times.

Ready to Try ScreenCraft?

Start capturing screenshots and generating PDFs in minutes. Free tier includes 100 screenshots/month.

Stop wrestling with Puppeteer.
Start shipping screenshots.

Join 2,000+ developers who chose the easy path.

Free forever tier • No credit card • Setup in 2 minutes