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.