Using Playwright with Scrnify for Advanced Screenshot Workflows

4/1/2025

Hey there, Laura and Heidi here from SCRNIFY! šŸ‘‹

If you're a developer working with web automation, you've likely faced the challenges of capturing high-quality screenshots at scale. Maybe you're running Playwright tests across multiple browsers, generating visual documentation, or creating automated reports with website previews. Whatever your use case, managing the infrastructure for reliable screenshot capture can quickly become a headache. šŸ˜…

We've been there! That's why we built SCRNIFY ā€” to handle the complex infrastructure so you can focus on creating awesome screenshot workflows. In this tutorial, we'll show you how to combine the power of Playwright's local automation capabilities with SCRNIFY's scalable screenshot API for advanced use cases.

By the end of this article, you'll know how to:

  • Set up a local Playwright project for complex browser interactions
  • Use SCRNIFY's API to offload screenshot generation
  • Create advanced workflows combining both technologies
  • Save time and resources by letting SCRNIFY handle the heavy lifting

Let's dive in! ā˜•

Prerequisites

Before we get started, make sure you have:

Setting Up Your Project

Let's start by creating a new Node.js project and installing the necessary dependencies. Open your terminal and run the following commands:

# Create a new directory and navigate into it
mkdir playwright-scrnify-demo
cd playwright-scrnify-demo

# Initialize a new Node.js project
npm init -y

# Install Playwright and other dependencies
npm install playwright axios dotenv

Next, let's create a .env file to store your SCRNIFY API key:

# .env file
SCRNIFY_API_KEY=your_api_key_here

Make sure to replace your_api_key_here with your actual SCRNIFY API key.

Now, let's create a basic project structure:

playwright-scrnify-demo/
ā”œā”€ā”€ .env                # Environment variables
ā”œā”€ā”€ package.json        # Project configuration
ā”œā”€ā”€ src/                # Source code directory
ā”‚   ā”œā”€ā”€ index.js        # Main script
ā”‚   ā”œā”€ā”€ local.js        # Local Playwright functions
ā”‚   ā””ā”€ā”€ scrnify.js      # SCRNIFY API functions
ā””ā”€ā”€ screenshots/        # Directory for saved screenshots

Let's create these directories and files:

mkdir src screenshots
touch src/index.js src/local.js src/scrnify.js

Local Setup: Configuring Playwright

First, let's set up the local Playwright configuration in src/local.js. This file will contain functions for taking screenshots using Playwright locally:

// src/local.js
const { chromium } = require('playwright');

/**
 * Takes a screenshot of a webpage using local Playwright
 * @param {string} url - The URL to capture
 * @param {Object} options - Screenshot options
 * @returns {Promise<Buffer>} - Screenshot buffer
 */
async function takeLocalScreenshot(url, options = {}) {
  // Set default options
  const defaultOptions = {
    fullPage: false,
    type: 'png',
    path: null, // If provided, save to this path
    viewport: { width: 1280, height: 720 },
    waitUntil: 'networkidle'
  };
  
  const screenshotOptions = { ...defaultOptions, ...options };
  let browser = null;
  
  try {
    // Launch a browser
    console.log('Launching browser...');
    browser = await chromium.launch();
    const context = await browser.newContext({
      viewport: screenshotOptions.viewport
    });
    
    // Create a new page and navigate to URL
    const page = await context.newPage();
    console.log(`Navigating to ${url}...`);
    await page.goto(url, { waitUntil: screenshotOptions.waitUntil });
    
    // Take screenshot
    console.log('Taking screenshot...');
    const screenshotBuffer = await page.screenshot({
      fullPage: screenshotOptions.fullPage,
      type: screenshotOptions.type,
      path: screenshotOptions.path
    });
    
    return screenshotBuffer;
  } catch (error) {
    console.error('Error taking local screenshot:', error);
    throw error;
  } finally {
    // Always close the browser
    if (browser) {
      await browser.close();
      console.log('Browser closed.');
    }
  }
}

/**
 * Takes a screenshot of a specific element
 * @param {string} url - The URL to capture
 * @param {string} selector - CSS selector for the element
 * @param {Object} options - Screenshot options
 * @returns {Promise<Buffer>} - Screenshot buffer
 */
async function takeElementScreenshot(url, selector, options = {}) {
  let browser = null;
  
  try {
    // Launch a browser
    console.log('Launching browser...');
    browser = await chromium.launch();
    const context = await browser.newContext({
      viewport: options.viewport || { width: 1280, height: 720 }
    });
    
    // Create a new page and navigate to URL
    const page = await context.newPage();
    console.log(`Navigating to ${url}...`);
    await page.goto(url, { waitUntil: options.waitUntil || 'networkidle' });
    
    // Wait for the element to be visible
    console.log(`Waiting for selector: ${selector}`);
    await page.waitForSelector(selector, { state: 'visible' });
    
    // Take screenshot of the element
    const element = await page.$(selector);
    if (!element) {
      throw new Error(`Element with selector "${selector}" not found`);
    }
    
    console.log('Taking element screenshot...');
    const screenshotBuffer = await element.screenshot({
      type: options.type || 'png',
      path: options.path
    });
    
    return screenshotBuffer;
  } catch (error) {
    console.error('Error taking element screenshot:', error);
    throw error;
  } finally {
    // Always close the browser
    if (browser) {
      await browser.close();
      console.log('Browser closed.');
    }
  }
}

module.exports = {
  takeLocalScreenshot,
  takeElementScreenshot
};

Integrating with SCRNIFY API

Now, let's create the SCRNIFY integration in src/scrnify.js. This file will handle communication with the SCRNIFY API:

// src/scrnify.js
const axios = require('axios');
const fs = require('fs').promises;
const path = require('path');
require('dotenv').config();

// Get API key from environment variables
const SCRNIFY_API_KEY = process.env.SCRNIFY_API_KEY;
const SCRNIFY_API_URL = 'https://api.scrnify.com/capture';

/**
 * Takes a screenshot using SCRNIFY API
 * @param {string} url - The URL to capture
 * @param {Object} options - Screenshot options
 * @returns {Promise<Buffer>} - Screenshot buffer
 */
async function takeScrnifyScreenshot(url, options = {}) {
  // Set default options
  const defaultOptions = {
    type: 'image',
    format: 'png',
    width: 1280,
    height: 720,
    fullPage: false,
    waitUntil: 'firstMeaningfulPaint',
    savePath: null // If provided, save to this path
  };
  
  const screenshotOptions = { ...defaultOptions, ...options };
  
  // Prepare API request parameters
  const params = new URLSearchParams({
    key: SCRNIFY_API_KEY,
    url: url,
    type: screenshotOptions.type,
    format: screenshotOptions.format,
    width: screenshotOptions.width.toString(),
    waitUntil: screenshotOptions.waitUntil
  });
  
  // Add height parameter only if fullPage is false
  if (!screenshotOptions.fullPage) {
    params.append('height', screenshotOptions.height.toString());
  }
  
  // Add fullPage parameter if true
  if (screenshotOptions.fullPage) {
    params.append('fullPage', 'true');
  }
  
  try {
    console.log('Requesting screenshot from SCRNIFY API...');
    const response = await axios({
      method: 'get',
      url: `${SCRNIFY_API_URL}?${params.toString()}`,
      responseType: 'arraybuffer'
    });
    
    const buffer = Buffer.from(response.data, 'binary');
    
    // Save the screenshot if a path is provided
    if (screenshotOptions.savePath) {
      await fs.mkdir(path.dirname(screenshotOptions.savePath), { recursive: true });
      await fs.writeFile(screenshotOptions.savePath, buffer);
      console.log(`Screenshot saved to: ${screenshotOptions.savePath}`);
    }
    
    return buffer;
  } catch (error) {
    if (error.response) {
      console.error(`API Error (${error.response.status}):`, 
        Buffer.from(error.response.data).toString());
    } else {
      console.error('Error taking SCRNIFY screenshot:', error.message);
    }
    throw error;
  }
}

module.exports = {
  takeScrnifyScreenshot
};

Creating Advanced Workflows

Now, let's create our main script in src/index.js that demonstrates advanced screenshot workflows by combining local Playwright capabilities with SCRNIFY API:

// src/index.js
const { takeLocalScreenshot, takeElementScreenshot } = require('./local');
const { takeScrnifyScreenshot } = require('./scrnify');
const { chromium } = require('playwright');
const path = require('path');

/**
 * Example 1: Compare local and SCRNIFY screenshots
 */
async function compareScreenshots(url) {
  try {
    console.log(`\nšŸ“ø Comparing screenshots for: ${url}`);
    
    // Take screenshots using both methods
    const localScreenshot = await takeLocalScreenshot(url, {
      path: path.join(__dirname, '../screenshots/local.png')
    });
    
    const scrnifyScreenshot = await takeScrnifyScreenshot(url, {
      savePath: path.join(__dirname, '../screenshots/scrnify.png')
    });
    
    console.log('āœ… Screenshots captured successfully!');
    console.log('Local screenshot size:', localScreenshot.length, 'bytes');
    console.log('SCRNIFY screenshot size:', scrnifyScreenshot.length, 'bytes');
  } catch (error) {
    console.error('āŒ Error comparing screenshots:', error.message);
  }
}

/**
 * Example 2: Capture screenshots after complex interactions
 */
async function captureAfterInteraction(url) {
  let browser = null;
  
  try {
    console.log(`\nšŸ”Ø Capturing screenshots after interaction: ${url}`);
    
    // Launch a browser for interaction
    browser = await chromium.launch();
    const context = await browser.newContext();
    const page = await context.newPage();
    
    // Navigate to the page
    console.log('Navigating to URL...');
    await page.goto(url, { waitUntil: 'networkidle' });
    
    // Perform some interactions (example: filling a form)
    console.log('Performing interactions...');
    await page.fill('input[name="search"]', 'playwright tips');
    await page.click('button[type="submit"]');
    await page.waitForLoadState('networkidle');
    
    // Now use SCRNIFY to capture the results
    console.log('Capturing results with SCRNIFY...');
    const currentUrl = page.url();
    await takeScrnifyScreenshot(currentUrl, {
      fullPage: true,
      savePath: path.join(__dirname, '../screenshots/search-results.png')
    });
    
    console.log('āœ… Interaction and screenshot complete!');
  } catch (error) {
    console.error('āŒ Error in interaction workflow:', error.message);
  } finally {
    // Always close the browser
    if (browser) {
      await browser.close();
    }
  }
}

/**
 * Example 3: Batch screenshot using SCRNIFY for efficiency
 */
async function batchScreenshots(urls) {
  try {
    console.log('\nšŸ› ļø Starting batch screenshot process...');
    
    const promises = urls.map((url, index) => {
      return takeScrnifyScreenshot(url, {
        savePath: path.join(__dirname, `../screenshots/batch-${index + 1}.png`)
      });
    });
    
    await Promise.all(promises);
    console.log('āœ… Batch screenshots completed successfully!');
  } catch (error) {
    console.error('āŒ Error in batch screenshot process:', error.message);
  }
}

/**
 * Example 4: Advanced responsive testing workflow
 */
async function responsiveTesting(url) {
  try {
    console.log(`\nšŸ“± Testing responsive designs for: ${url}`);
    
    // Define device viewports
    const viewports = [
      { name: 'mobile', width: 375, height: 667 },
      { name: 'tablet', width: 768, height: 1024 },
      { name: 'desktop', width: 1440, height: 900 }
    ];
    
    // Capture screenshots for each viewport
    for (const viewport of viewports) {
      console.log(`Capturing for ${viewport.name} (${viewport.width}x${viewport.height})...`);
      
      await takeScrnifyScreenshot(url, {
        width: viewport.width,
        height: viewport.height,
        savePath: path.join(__dirname, `../screenshots/${viewport.name}.png`)
      });
    }
    
    console.log('āœ… Responsive testing completed!');
  } catch (error) {
    console.error('āŒ Error in responsive testing:', error.message);
  }
}

// Run all examples
async function runExamples() {
  try {
    // Example 1: Compare local and SCRNIFY screenshots
    await compareScreenshots('https://example.com');
    
    // Example 2: Capture after interaction
    await captureAfterInteraction('https://google.com');
    
    // Example 3: Batch screenshots
    await batchScreenshots([
      'https://example.com',
      'https://playwright.dev',
      'https://nodejs.org'
    ]);
    
    // Example 4: Responsive testing
    await responsiveTesting('https://example.com');
    
    console.log('\nšŸŽ‰ All examples completed successfully!');
  } catch (error) {
    console.error('āŒ Error running examples:', error);
  }
}

// Run the examples
runExamples();

Running the Project

Now you can run your project with:

node src/index.js

This will execute all the examples, demonstrating the advanced screenshot workflows combining Playwright and SCRNIFY.

Understanding the Advanced Use Cases

Let's break down the advanced workflows we've implemented:

1. Comparative Analysis

The compareScreenshots function takes screenshots of the same URL using both local Playwright and SCRNIFY API. This is useful for:

  • Quality comparison between local and cloud-based rendering
  • Validating that your cloud screenshots match local expectations
  • Performance benchmarking (timing and file size comparisons)

2. Complex Interaction Workflows

The captureAfterInteraction function showcases how to:

  1. Use Playwright locally to perform complex interactions (form filling, clicking)
  2. Offload the screenshot capture to SCRNIFY once the page is in the desired state
  3. Save resources by handling only the automation locally while delegating the resource-intensive rendering

This approach is ideal for capturing screenshots after complex user journeys, authenticated sessions, or dynamic content interactions.

3. Batch Screenshot Processing

The batchScreenshots function demonstrates how to efficiently capture multiple screenshots in parallel using SCRNIFY. This approach:

  • Avoids the overhead of launching multiple browser instances locally
  • Leverages SCRNIFY's scalable architecture for concurrent processing
  • Reduces execution time for large batches of screenshots

4. Responsive Design Testing

The responsiveTesting function shows how to validate website appearance across different device sizes. Using SCRNIFY for this workflow:

  • Eliminates the need to configure and maintain multiple device emulations
  • Ensures consistent rendering across viewport sizes
  • Enables quick visual verification of responsive behavior

Conclusion

By combining Playwright's automation capabilities with SCRNIFY's screenshot API, you can create powerful, efficient, and scalable screenshot workflows. This hybrid approach gives you the best of both worlds:

  • Local Playwright for complex interactions, authentication, and precise control over user flows
  • SCRNIFY API for reliable, high-quality screenshot generation without infrastructure overhead

This approach is particularly valuable when:

  • Scaling your screenshot processes without scaling your infrastructure
  • Working with resource-intensive screenshots (high resolution, full page)
  • Running parallel screenshot jobs without overwhelming local resources
  • Ensuring consistent rendering across environments

Remember, SCRNIFY handles all the complex infrastructure, browser management, and scaling challenges for you. This means you can focus on creating valuable automation workflows rather than maintaining screenshot generation infrastructure.

Ready to supercharge your screenshot workflows with SCRNIFY? Sign up for our free beta and start exploring these advanced use cases today!

For more information on getting started with SCRNIFY, check out our introductory post.

Cheers, Laura & Heidi šŸ‡¦šŸ‡¹

P.S. We'd love to hear about your specific screenshot automation challenges! Tweet us at @scrnify and let us know how you're using Playwright and SCRNIFY together. šŸš€

Additional Resources

Ready to Get Started?

Sign up now and start capturing stunning screenshots in minutes.

Sign Up Now