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
| Element | Frame | Before x | y | w | h | After x | y | w | h |
|---|
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
| Frame | Timestamp (ms) | Elements in frame | Shift 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
5. Per-element breakdown
| Element | Frame | Union ∩ viewport (px²) | Impact fraction | Move dist (px) | Distance fraction | Shift value | Share of CLS |
|---|
6. Session windows — gap < 1000 ms, window capped at 5000 ms, CLS = max sum
| Window | First shift | Last shift | Span (ms) | Frames | Window sum | Status |
|---|
The highlighted row is the winning window. Every other shift on the page is discarded from the reported CLS.
7. To-scale overlay
8. Displacement budget — how far this element may move before CLS crosses 0.10
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.
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.
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.
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:
- The first shift opens a window.
- A new shift joins the current window only if it is less than 1000 ms after the previous shift and less than 5000 ms after the first shift in that window.
- Otherwise it closes the window and opens a new one.
CLS = max(window sums), neversum(all shifts).
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:
- Good — CLS at or below 0.10
- Needs improvement — above 0.10, at or below 0.25
- Poor — above 0.25
What this calculator deliberately does not model
- Excluded shifts. Real entries carry
hadRecentInput; anything within 500 ms of a discrete user input is dropped by the browser before CLS is computed. Only enter shifts that were not input-driven. - Transforms. Movement produced by
transform: translate()never generates a layout shift entry, because layout position is unchanged. That is the whole reason animations should use transforms. - Sub-pixel and scroll. The browser works in physical pixels after device pixel ratio scaling, ignores shifts smaller than the implementation threshold, and does not count movement caused by scrolling.
- Newly inserted content. An element appearing for the first time is not itself unstable; the elements it pushes down are.
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.
Frequently asked questions
max(|dx|, |dy|). An element moving 100 px right and 100 px down has a move distance of 100 px, not 141 px.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.Last updated 29 July 2026