How full-page screenshots actually work

Notes from building an open-source capture extension.

A full-page screenshot looks like it should be trivial: the page is a rectangle, take a picture of the rectangle. In practice a browser can only ever photograph the part of the page currently on screen, so every "full page" capture is several photographs glued together — and almost all of the interesting bugs live in the glue.

The basic loop

The naive version is four steps repeated until you reach the bottom:

scrollTo(0, y)
capture visible tab      -> slice
y += viewportHeight
stitch slices onto one canvas

This works on a plain document and fails on almost everything else. Here is why.

Sticky and fixed elements

An element with position: sticky or position: fixed stays glued to the viewport. It is therefore present in every slice. Stitch them and the navigation bar marches down the finished image like a flipbook.

The fix is to detach or hide those elements for the duration of the capture and restore them afterwards. It sounds obvious; a surprising number of tools skip it. The same applies to cookie banners, chat bubbles and back-to-top buttons.

Lazy-loaded content

Images that load when they enter the viewport need a moment to arrive. Scroll, capture and scroll again with no pause and you photograph the placeholder instead of the picture.

The page looked fine while you were reading it because you scrolled at human speed. A correct implementation scrolls, waits for the network and layout to settle, and only then captures. That is slower, which is exactly why not everything does it.

Device pixel ratio

This is the one that produces the strangest artefacts. On a HiDPI display the browser renders at 2x or 3x. Scroll offsets are expressed in CSS pixels; the captured bitmap is in device pixels. Mix the two anywhere in the arithmetic and every slice lands a fraction off.

The result is a hairline of duplicated or missing pixels at each seam. You usually notice it first as text that looks slightly wrong — a line of type carrying a sliver of the line above it. Zoom to 400% at a boundary and it is unmistakable.

// slice N should start at, in device pixels:
Math.round(n * viewportHeightCss * devicePixelRatio)

// not:
n * viewportHeightCss

Inner scroll containers

A page can contain its own scrolling regions: a data table with a fixed height, a code block, a chat panel, a sidebar with overflow: auto. Those are separate scroll contexts. "Full page" capture scrolls the document; it does not scroll them.

So the outer page is captured completely while the inner region shows only whatever happened to be visible. There is no universal fix — a tool has to detect scrollable descendants and decide what to do about them, and very few do. Worth knowing so you can spot it rather than assume the capture is complete.

Canvas size limits

Browsers cap how large a single canvas surface can be. The exact limit varies by browser and platform, but somewhere past a few tens of thousands of pixels of height a capture will silently come back truncated rather than failing loudly. Anything that captures genuinely long pages has to tile the output rather than allocating one enormous surface.

A one-minute test for any tool

Find a page with a sticky header, lazy images, an inner scroll region and roughly 8000 pixels of height — most documentation sites qualify. Capture it, then check four things:

CheckWhat a failure looks like
Header countThe nav bar appears more than once
ImagesGrey placeholder boxes below the fold
Inner regionsA table that shows only the rows that were on screen
Seams at 400%Thin repeated or missing lines at slice boundaries

A tool that survives that will survive your actual work.

Where the built-in browser feature sits

Both Chrome and Firefox can already do this without an extension. In Chrome, open DevTools, press Ctrl+Shift+P, type screenshot and choose Capture full size screenshot. In Firefox, right-click the page and choose Take Screenshot → Save full page.

It gives you a PNG and nothing else — no annotation, no redaction, no PDF — and it is not especially careful about the failure modes above. But for a lot of people that is genuinely enough, and it is worth knowing before installing anything.