ScreenshotNeo

BlogScreenshots on your device

How to Capture a Page Area and Display It on Another Page

Use getDisplayMedia for live sharing, Region Capture for a tab area, canvas for stills, or ScreenshotNeo for automated URL screenshots.

By the ScreenshotNeo team1 October 20267 min read

Direct answer: For a live capture, call navigator.mediaDevices.getDisplayMedia() from a user action, then assign the returned MediaStream to a <video> element on the page that should display it. The browser asks the user to choose a screen, window, or tab. For a still image, draw a video frame to a canvas and export it. If you need only a rectangular area of the captured tab, use Region Capture where supported.

A browser cannot silently capture another page. The user must approve each capture, and the API is available only in a secure context (HTTPS or localhost) in browsers that support it. MDN describes the flow in its Screen Capture guide and getDisplayMedia() reference.

1. Display a selected screen, window, or tab on another page

The simplest reliable design is a destination page with a Start button. The user opens that page, clicks Start, selects the source page in the browser picker, and sees the live stream in the destination video element.

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Screen preview</title>
  <style>
    video { max-width: 100%; background: #111; }
    button { margin-right: .5rem; }
    #status { min-height: 1.5rem; }
  </style>
</head>
<body>
  <button id="start">Choose page to display</button>
  <button id="stop" disabled>Stop</button>
  <p id="status" role="status"></p>
  <video id="preview" autoplay playsinline muted></video>

  <script>
    const start = document.querySelector('#start');
    const stop = document.querySelector('#stop');
    const preview = document.querySelector('#preview');
    const status = document.querySelector('#status');
    let stream;

    start.addEventListener('click', async () => {
      if (!navigator.mediaDevices?.getDisplayMedia) {
        status.textContent = 'Screen capture is not supported in this browser.';
        return;
      }

      try {
        stream = await navigator.mediaDevices.getDisplayMedia({
          video: {
            frameRate: { ideal: 30, max: 60 }
          },
          audio: false
        });

        preview.srcObject = stream;
        start.disabled = true;
        stop.disabled = false;
        status.textContent = 'Capture is running.';

        const [track] = stream.getVideoTracks();
        track.addEventListener('ended', stopCapture, { once: true });
      } catch (error) {
        if (error.name === 'NotAllowedError') {
          status.textContent = 'The capture request was cancelled or denied.';
        } else if (error.name === 'NotFoundError') {
          status.textContent = 'No display source was selected.';
        } else {
          status.textContent = `Capture failed: ${error.name}`;
          console.error(error);
        }
      }
    });

    stop.addEventListener('click', stopCapture);

    function stopCapture() {
      stream?.getTracks().forEach(track => track.stop());
      stream = undefined;
      preview.srcObject = null;
      start.disabled = false;
      stop.disabled = true;
      status.textContent = 'Capture stopped.';
    }
  </script>
</body>
</html>

How the two-page flow works

  1. Serve the destination page over HTTPS or from localhost.
  2. Start the call directly from a click or other transient user activation.
  3. In the picker, select the tab, window, or display containing the page you want to show.
  4. Set the returned stream as video.srcObject.
  5. Stop every track when the user clicks Stop or when the browser ends sharing.

The browser controls source selection. Constraints such as frame rate are applied after the user chooses a source; they cannot silently select or narrow the picker in advance. A user can deny the prompt, and permission is requested for each capture.

2. Capture only an area of the selected tab

Region Capture crops a captured tab to an element’s bounding rectangle. Content layered over that rectangle can still appear. The API sequence is: capture the tab, create a CropTarget from the element, then call cropTo() on the video track.

const stream = await navigator.mediaDevices.getDisplayMedia({ video: true });
const video = document.querySelector('#preview');
video.srcObject = stream;
await video.play();

const card = document.querySelector('#card');
const [track] = stream.getVideoTracks();

if (window.CropTarget?.fromElement && track.cropTo) {
  const target = await CropTarget.fromElement(card);
  await track.cropTo(target);
} else {
  console.warn('Region Capture is unavailable; showing the full selected tab.');
}

See MDN’s Region and Element Capture guide for current API details and restrictions.

Region Capture versus Element Capture

Method Output Use it when
Region Capture A rectangular crop of the tab You need the pixels inside an element’s bounding box, even if overlapping content remains visible.
Element Capture The target DOM tree and descendants You need to exclude surrounding or overlapping DOM content, subject to browser support and documented restrictions.

3. Turn the live capture into a still image

getDisplayMedia() returns a live stream, not an image file. Draw a frame to a canvas and export it with toBlob().

async function saveFrame(video, filename = 'capture.png') {
  if (video.readyState < HTMLMediaElement.HAVE_CURRENT_DATA) {
    throw new Error('The video has not produced a frame yet.');
  }

  const canvas = document.createElement('canvas');
  canvas.width = video.videoWidth;
  canvas.height = video.videoHeight;
  canvas.getContext('2d').drawImage(video, 0, 0);

  const blob = await new Promise(resolve => canvas.toBlob(resolve, 'image/png'));
  if (!blob) throw new Error('The browser could not encode the frame.');

  const link = document.createElement('a');
  link.download = filename;
  link.href = URL.createObjectURL(blob);
  link.click();
  URL.revokeObjectURL(link.href);
}

document.querySelector('#save').addEventListener('click', () =>
  saveFrame(document.querySelector('#preview'))
);

For JPEG, replace the MIME type with image/jpeg and provide a quality value such as 0.9. Wait for loadedmetadata or a nonzero videoWidth before exporting.

4. Compatibility, privacy, and security constraints

  • Secure context: use HTTPS or localhost. Insecure pages generally cannot access the API.
  • User activation: call getDisplayMedia() from a recent click or equivalent interaction.
  • Permission: the user must confirm sharing and choose a source. Do not design around silent reuse.
  • Limited availability: MDN marks Screen Capture as not Baseline. Check support for your target browsers and for Region or Element Capture separately.
  • Privacy obscuring: browsers may blur or replace portions of a logical display surface that are hidden from the user.
  • Lifecycle: listen for the video track’s ended event; users can stop sharing from browser controls at any time.

5. Performance and reliability checklist

  • Request only video unless audio is required; audio adds another permission and processing path.
  • Use a reasonable frame rate such as 30 fps for previews. Higher rates increase CPU, memory, and network use.
  • Render at the video’s intrinsic videoWidth and videoHeight to avoid accidental scaling during canvas export.
  • Throttle still-image exports instead of encoding every animation frame.
  • Stop tracks when the destination page is hidden or the user finishes, so the browser indicator clears promptly.
  • Test picker cancellation, browser-level Stop, sleep or display changes, and a source tab that navigates.
  • For remote display, send encoded frames or a WebRTC stream from the capture page to your server; a MediaStream is local to the browser context unless you build that transport.

6. Automated screenshots when the input is a URL

If your requirement is to render a known URL or HTML document rather than capture a visitor’s screen, server-side browser automation is a different workflow. Cloudflare documents a Browser Rendering screenshot endpoint that accepts a URL or HTML and supports full-page and CSS-selector screenshots. See the Cloudflare screenshot endpoint documentation. It does not capture a person’s display and does not replace the permission-based flow above.

Or skip the browser setup

ScreenshotNeo renders a URL and returns a PNG, JPEG, WebP, or PDF from one request. It removes cookie banners, newsletter popups, and chat widgets before the shot; bot checks, blank pages, timeouts, failed loads, and cache hits are not billed. Responses identify the result with X-Page-Verdict and X-Billed headers. Its MCP server provides take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients.

Read the ScreenshotNeo API documentation for all options, then try this one-call example:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

ScreenshotNeo supports full-page and element capture, device presets and custom viewports, retina scale, dark mode, custom CSS and JavaScript, waits, request blocking, headers, cookies, user agents, geolocation, caching, signed links, asynchronous webhooks, bulk capture, PDFs, and HTML-to-image. One thousand screenshots per month are free with no card; paid plans start at $5 for 3,000.

Create a free ScreenshotNeo account and start with 1,000 screenshots each month at no charge.

Troubleshooting

Error or symptom Cause Fix
NotAllowedError The user denied or cancelled the picker, or the call was not made from a user gesture. Call from a click handler and show a clear retry control.
NotFoundError No display source was selected or available. Ask the user to select a tab, window, or display and retry.
SecurityError or undefined API The page is not in a secure context or the browser lacks support. Use HTTPS or localhost and check browser compatibility.
Black or blank video The video metadata has not loaded, the track ended, or the source is protected. Wait for loadedmetadata, verify videoWidth, and handle the track’s ended event.
Crop call fails Region Capture is unsupported or the target is invalid. Feature-detect CropTarget.fromElement and track.cropTo; fall back to full-tab capture.
Captured area includes overlays Region Capture is rectangular, so overlapping content remains visible. Use Element Capture where supported, or redesign the source page to hide the overlay.
Exported image is low quality The canvas was sized with CSS pixels rather than the video’s intrinsic dimensions. Set canvas dimensions from video.videoWidth and video.videoHeight.

FAQ

Can a page capture another tab without asking?

No. The browser requires a visible user choice and approval for each getDisplayMedia() request.

Does the API return a PNG?

No. It returns a live MediaStream. Use a video element for live display or canvas plus toBlob() for a still.

Can I capture just one DOM element?

Region Capture crops to an element’s rectangle. Element Capture can restrict output to the element’s DOM tree where supported.

Should I use screen capture for generated website thumbnails?

Usually no. If you have a URL or HTML to render, use a server-side screenshot service such as ScreenshotNeo; screen capture is intended for a user’s selected display surface.