CLS Layout Shift Score Calculator

Enter the before and after bounding rectangles of every unstable element and get the real numbers: impact fraction, distance fraction, per-frame shift values, session windows, and the maximum-window CLS. Not a threshold grader — the actual Layout Instability formula. Runs entirely in your browser.

1. Viewport

The impact fraction divides by the viewport area. The distance fraction divides by the larger single dimension. These are two different denominators — that is why a tall phone punishes vertical shifts less than you would expect.

2. Unstable elements — bounding rect before and after the shift, in viewport coordinates

ElementFrame Before xywh After xywh

Elements sharing a Frame number shifted in the same animation frame and are merged into one shift record, exactly as the browser does. Coordinates are relative to the viewport origin; a rect may extend outside the viewport and will be clipped.

3. Frame timestamps — when each shift record was reported, in ms since navigation start

FrameTimestamp (ms)Elements in frameShift value (sum)Spec-exact union

A real browser computes one value per frame: the union of every unstable element's before and after rects, times the single largest move distance in that frame. Summing per-element scores is the attribution view used by most debugging tools — it is always greater than or equal to the spec value when several elements move together. Both are shown so you can see the gap.

4. Result

0.000
CLS (max window)
0.000
Naive lifetime total
0
Session windows
Worst element
Good
00.10 good0.25 poor0.50+

5. Per-element breakdown

ElementFrameUnion ∩ viewport (px²) Impact fractionMove dist (px)Distance fraction Shift valueShare of CLS

6. Session windows — gap < 1000 ms, window capped at 5000 ms, CLS = max sum

WindowFirst shiftLast shiftSpan (ms)FramesWindow sumStatus

The highlighted row is the winning window. Every other shift on the page is discarded from the reported CLS.

7. To-scale overlay

Viewport Before rect After rect Impact region (union ∩ viewport) Move distance

8. Displacement budget — how far this element may move before CLS crosses 0.10

0 px
Current displacement
0 px
Max affordable
0 px
Headroom
0.000
CLS at that limit

The method, exactly

The browser does not score layout shifts with a heuristic. The Layout Instability API defines a closed-form value per frame, and this page implements it literally.

Step 1 — impact fraction

For every unstable element the browser takes the union of the rectangle it occupied in the previous frame and the rectangle it occupies now, intersects that region with the viewport, and divides by the viewport area. When the before and after rects overlap, the union is smaller than the two areas added together; when the element jumps clear of its old position, the union is the two areas added together.

impactRegion = (rectBefore ∪ rectAfter) ∩ viewport impactFraction = area(impactRegion) / (viewportWidth × viewportHeight)

Step 2 — distance fraction

The move distance is the greatest movement along a single axis, not the diagonal. It is divided by the greater of the two viewport dimensions, so on a 390 x 844 phone the denominator is 844 for both vertical and horizontal movement.

moveDistance = max( |x₂ − x₁| , |y₂ − y₁| ) distanceFraction = moveDistance / max(viewportWidth, viewportHeight)

Step 3 — layout shift value

The two fractions are multiplied. Both are bounded by 1, so a single shift record can never exceed 1.0. An element occupying the whole viewport and jumping a full screen height scores close to 1.0; a small button nudged 8 px scores in the thousandths.

layoutShiftValue = impactFraction × distanceFraction

Step 4 — session windows

CLS has not been the lifetime sum since Chrome 91. Shift records are bucketed into session windows, and only the heaviest window is reported. The rule, taken from the reference implementation in Google's web-vitals library, is:

This is why a page with twenty small shifts spread across a long session can report a better CLS than a page with three shifts fired back to back. Fixing the burst matters far more than fixing the count.

Thresholds

Google's Core Web Vitals cut-offs for CLS are 0.10 and 0.25, evaluated at the 75th percentile of real page loads, segmented separately for mobile and desktop:

What this calculator deliberately does not model

How to get the numbers to type in here

Open DevTools, run the snippet below, then reload the page. Each logged entry gives you the value, the timestamp, and the before and after rects of every source element, which map one to one onto the fields above.

new PerformanceObserver((list) => { for (const e of list.getEntries()) { if (e.hadRecentInput) continue; console.log(e.startTime.toFixed(0) + 'ms', e.value.toFixed(4), e.sources.map(s => ({ node: s.node, before: s.previousRect, after: s.currentRect }))); } }).observe({type: 'layout-shift', buffered: true});

Frequently asked questions

Why does my per-element sum differ from the browser's frame value?
Because the browser unions the regions of all elements moving in that frame before dividing by the viewport area, and applies one distance fraction — the largest move in the frame — to that single union. When three stacked elements all slide down together their regions overlap, so the true union is smaller than the sum of the individual unions. Toggle the frame model in section 3 to compare the two figures on your own data.
Can a single layout shift value be greater than 1?
No. Both the impact fraction and the distance fraction are clamped to the range 0 to 1 by construction: the impact region is intersected with the viewport before division, and the move distance is divided by the larger viewport dimension. CLS as a whole can exceed 1 only by accumulating many shifts inside one session window.
Is the move distance the diagonal?
No. It is the greatest single-axis movement, that is max(|dx|, |dy|). An element moving 100 px right and 100 px down has a move distance of 100 px, not 141 px.
Does a taller viewport make CLS better or worse?
Both denominators grow, but not at the same rate. A taller viewport increases the area, shrinking the impact fraction of a fixed-size element, and increases max(width, height), shrinking the distance fraction of a fixed vertical jump. So the same absolute shift generally scores lower on a taller screen — which is exactly why you must test at the viewport your real users have.
Why is my CLS lower than the sum of everything I entered?
That is the session window algorithm working as intended. Only the single heaviest burst counts. Section 6 shows which window won and which shifts were discarded.

About the author. Built by , a solo developer shipping performance and developer tooling across the Zovo network. This calculator implements the Layout Instability specification and the session window logic from Google's web-vitals reference implementation. Everything runs client side; nothing you type leaves the page.

Last updated 29 July 2026

By the same builder: GitHub — theluckystrike BeLikeNative — Grammar AI EarlyThunder — Dev Blog Zovo — AI Dev Tools