Loading...

Capturing Advanced Format Screenshots with Playwright

Want to save screenshots in GIF, JP2, TIFF or other formats? Learn how to capture screenshots in advanced formats using Playwright and Sharp.

Back

During my recent project work, I encountered an interesting challenge - a client needed screenshots in formats beyond Playwright's default PNG and JPEG outputs. This led me down a fascinating path of discovery, and today I want to share what I learned about capturing screenshots in formats like GIF, JP2, TIFF, AVIF, HEIF, and even SVG.

The Challenge with Default Formats

When I first started working with Playwright, I quickly realized its limitation with image formats. While Playwright is incredibly powerful for browser automation and testing, it primarily supports PNG and JPEG for screenshots. This became a hurdle when my client needed TIFF format for their print design workflow, and I had another project requiring JPEG 2000 for its superior compression capabilities.

My Solution: Combining Playwright with Sharp

After some experimentation, I discovered that pairing Playwright with the Sharp image processing library opened up a world of possibilities. Here's the approach I developed:

import { chromium } from 'playwright';
import sharp from 'sharp';
 
async function captureAdvancedScreenshot() {
    const browser = await chromium.launch();
    try {
        const context = await browser.newContext();
        const page = await context.newPage();
        await page.goto('https://example.com');
 
        // First, capture in PNG format (highest quality)
        const screenshot = await page.screenshot({
            type: 'png'
        });
 
        // Now we can use Sharp to convert to any format we need
        const image = sharp(screenshot);
 
        // Convert to AVIF
        await image.clone().avif().toFile('screenshot.avif');
 
        // Convert to TIFF
        await image.clone().tiff().toFile('screenshot.tiff');
 
        // Convert to JPEG 2000
        await image.clone().jp2().toFile('screenshot.jp2');
 
        // Convert to HEIF
        await image.clone().heif().toFile('screenshot.heif');
 
        // For GIF conversion (with optimization)
        await image.clone()
            .gif({
                quality: 80,
                effort: 7  // Higher effort means better optimization
            })
            .toFile('screenshot.gif');
 
    } finally {
        await browser.close();
    }
}

The Magic Behind Format Conversion

Let me share some insights I gained while working with each format:

1. AVIF (AV1 Image Format) I found AVIF particularly impressive for web optimization. It consistently produced smaller file sizes than JPEG while maintaining better quality. Here's how I fine-tuned AVIF conversion:

await sharp(screenshot)
    .avif({
        quality: 80,
        effort: 8,  // I found 8 to be the sweet spot
        chromaSubsampling: '4:4:4'  // For best color accuracy
    })
    .toFile('optimized.avif');

2. TIFF for Print Workflows For my client's print requirements, TIFF was crucial. I learned to maintain color fidelity with this approach:

await sharp(screenshot)
    .tiff({
        compression: 'lzw',
        quality: 100,
        pyramid: true,  // Helps with large images
        tile: true
    })
    .toFile('print-ready.tiff');

3. Creating Animated GIFs One interesting challenge was creating animated GIFs from dynamic content. I developed this solution:

async function captureAnimatedScreenshot() {
    const browser = await chromium.launch();
    const page = await browser.newPage();
    
    const frames = [];
    
    // Capture multiple frames
    for (let i = 0; i < 5; i++) {
        await page.goto(`https://example.com/state${i}`);
        const frame = await page.screenshot();
        frames.push(frame);
    }
 
    // Combine frames into an animated GIF
    const animation = sharp(frames[0]);
    animation.gif({
        delay: 100,  // 100ms between frames
        frames: frames.length
    });
    
    await animation.toFile('animation.gif');
    await browser.close();
}

Handling Different Screen Sizes

During my experiments, I realized that different formats work better with different screen sizes. Here's my adaptive approach:

async function adaptiveScreenshot(url: string, format: 'avif' | 'jp2' | 'tiff') {
    const browser = await chromium.launch();
    const context = await browser.newContext({
        viewport: { width: 1920, height: 1080 }
    });
    
    const page = await context.newPage();
    await page.goto(url);
 
    const screenshot = await page.screenshot();
    const processor = sharp(screenshot);
 
    // Adjust processing based on image dimensions
    const metadata = await processor.metadata();
    
    if (metadata.width && metadata.width > 2000) {
        // For large screenshots, optimize differently
        processor.resize(2000, null, {
            withoutEnlargement: true,
            fit: 'inside'
        });
    }
 
    await processor[format]().toFile(`screenshot.${format}`);
    await browser.close();
}

Performance Considerations

Through trial and error, I discovered some important performance optimizations:

  1. Memory Management: When dealing with large screenshots or multiple formats, I learned to process them sequentially rather than in parallel to avoid memory issues.

  2. Viewport Optimization: Setting an appropriate viewport size before capturing screenshots can significantly impact both processing time and output quality.

  3. Format-Specific Tweaks: Each format has its sweet spots for quality vs. size trade-offs. I created this handy configuration object:

const formatConfigs = {
    avif: { quality: 80, effort: 8 },
    jp2: { quality: 85, lossless: false },
    tiff: { compression: 'lzw', quality: 100 },
    heif: { quality: 75, compression: 'hevc' },
    gif: { quality: 80, effort: 7 }
};

Conclusion

This journey taught me that while Playwright's built-in screenshot capabilities are somewhat limited in terms of formats, combining it with Sharp opens up endless possibilities. The key is understanding each format's strengths and use cases.

For those interested in implementing this in their own projects, I've open-sourced a small utility library that wraps these functionalities. You can find it on my GitHub profile.

Remember, the best format choice depends on your specific needs - AVIF for modern web delivery, TIFF for print work, JP2 for archival purposes, or GIF for simple animations. Don't be afraid to experiment and find what works best for your use case.

Note: If you're looking for a managed solution, screenshotsapi.dev offers professional screenshot services with support for multiple formats, without the complexity of managing your own infrastructure.

Want to learn more about Playwright? Check out our other guides:

External Resources

Written by

Durgaprasad Budhwani

At

Tue Jan 02 2024