Skip to content
academia.sh

Lesson 01 / 27

User-Centric Metrics

Measuring a published interface's speed by the events the user perceives; the definitions of loading, interactivity, and visual stability metrics, computing a layout shift's score, and summarizing a distribution with percentiles.

Contents

The North Slope Measurement Station application gets built, bundled, and published. The build output ships to a hosting target, releases can be rolled back, and environment variables stay separated. The deployment pipeline answered one question — does the code reach the user — and the answer was yes.

The unanswered question is: how well does what arrives work for the user? This lesson builds the measurable form of that question. The metrics tie to events the user perceives; tying them to a budget is left to this topic’s last lesson.

Technical Milestones Answer the Wrong Question

A page loading is a process with events defined for the browser: document parsing finishes, subresources arrive, the load event fires. These events are measurable and recordable. The problem is not measurability — it is what they represent.

Consider the station list page. The list arrives inside the HTML from the server and appears immediately; a map component further down the page downloads a large script and becomes ready two seconds later. The load event fires when the map is ready. The user, meanwhile, saw the list in one second and has already tapped the station they were looking for.

The reverse case exists too: the document finishes early, but nothing readable is on screen because the content gets produced by script. The load event is early; what the user sees is a blank screen.

In both cases the technical milestone is accurate and describes the experience wrong. A metric’s subject should not be the browser’s internal flow — it should be what the user sees on screen and does.

Three Questions, Five Metrics

User-centric metrics separate three questions.

When did I see something? This question has two measures. Time to first byte is the time between sending the request and the response’s first byte arriving; it is the first segment of the timing that the Network Panel Diagnostics lesson breaks into stages. It measures the server side and sets the ceiling for any optimization done on the client: every later metric stacks on top of this time. First contentful paint is the moment any content from the document — text, an image, a canvas drawing — first gets painted on screen. It reports that the blank screen has ended, not that the page is ready.

When did I see the real content? Largest contentful paint is the moment the largest content element in the viewport gets painted. The metric’s idea is this: a page’s feeling of being “loaded” comes from the arrival of the element that occupies the most space. On the station list page, this element is the list itself; on the measurement detail page, it is the station photo. The metric keeps updating through loading; if a larger element gets painted later, the candidate changes and the metric moves forward.

When did I get a response to my touch? Interaction to next paint is the time from a user interaction — a click, a tap, a key — to the screen’s next paint that reflects it. What gets measured is not the handler’s own duration but the user’s entire perceived delay: the time the input waits to be processed, the handler’s duration, and producing the frame. Many interactions happen over the course of a page; the metric gives a single value representing the worst of them.

Did the screen shift under my feet? Cumulative layout shift is the sum of unexpected displacements. It is the quantitative counterpart of the phenomenon introduced as content shift in the Web Fundamentals and HTML course, and unlike the other metrics it gives not a duration but a unitless score.

The Score for Visual Stability

A shift’s score is the product of two fractions. The impact fraction is the ratio of the union of the shifting element’s visible areas across two consecutive frames to the viewport area: how much of the screen got affected? The distance fraction is the ratio of the largest distance the element traveled to the viewport’s larger dimension: how far did it go?

The following computation assumes the shifting elements span the full width of the viewport; under this assumption the impact region reduces from a two-dimensional area to a single vertical interval. For narrower elements, the same product scales by the width ratio.

// shift.mjs — a single layout shift's score and the session window total

const VIEWPORT = { width: 390, height: 844 }; // viewport size, CSS pixels

// Each shift: the unstable element's vertical range before and after the shift.
const shifts = [
  { name: "announcement banner opened", before: [96, 700], after: [196, 800] },
  { name: "station image settled", before: [200, 444], after: [224, 468] },
  { name: "measurement badge arrived", before: [512, 560], after: [520, 568] },
];

function shiftScore(shift) {
  const [beforeTop, beforeBottom] = shift.before;
  const [afterTop, afterBottom] = shift.after;

  // Impact region: the union of the visible areas across two frames.
  const unionTop = Math.max(0, Math.min(beforeTop, afterTop));
  const unionBottom = Math.min(VIEWPORT.height, Math.max(beforeBottom, afterBottom));
  const impactFraction = (unionBottom - unionTop) / VIEWPORT.height;

  // Distance fraction: the largest distance the element traveled / the viewport's larger dimension.
  const distance = Math.abs(afterTop - beforeTop);
  const distanceFraction = distance / Math.max(VIEWPORT.width, VIEWPORT.height);

  return { impactFraction, distanceFraction, distance, score: impactFraction * distanceFraction };
}

console.log("shift".padEnd(28) + "moved".padStart(8) + "impact".padStart(8) +
  "distance".padStart(10) + "score".padStart(8));
let total = 0;
for (const s of shifts) {
  const r = shiftScore(s);
  total += r.score;
  console.log(
    s.name.padEnd(28) + `${r.distance}px`.padStart(8) +
    r.impactFraction.toFixed(3).padStart(8) + r.distanceFraction.toFixed(3).padStart(10) +
    r.score.toFixed(4).padStart(8));
}
console.log("-".repeat(62));
console.log("session window total".padEnd(28) + total.toFixed(4).padStart(34));
console.log(`threshold rating: ${total <= 0.1 ? "good" : total <= 0.25 ? "needs improvement" : "poor"}`);
shift                          moved  impact  distance   score
announcement banner opened     100px   0.834     0.118  0.0988
station image settled           24px   0.318     0.028  0.0090
measurement badge arrived        8px   0.066     0.009  0.0006
--------------------------------------------------------------
session window total                                    0.1085
threshold rating: needs improvement

The three shifts have very different impact. The hundred-pixel announcement banner that opens above the page pushes down everything below it, so it affects eighty-three percent of the screen and alone produces nearly the entire score. The measurement badge’s eight-pixel jitter, by contrast, amounts to six-tenths of a percent.

Two rules follow from this. A shift’s cost is proportional not to the shifting element’s size but to how much content sits beneath it: a small element at the very top of the page is more expensive than a large one at the very bottom. And because the score is a product, when either fraction approaches zero the result approaches zero — a shift outside the viewport counts for nothing at all.

The summing rule is also part of the definition: shifts get summed within a session window, and the page’s score is the largest of these windows. A window groups shifts that have no long gap between them; a page left open for hours does not get a poor score from small shifts accumulating over that time.

Shifts that start with user movement do not enter the metric. A panel that opens on a button press and pushes down what is below it is an expected outcome; what gets measured is the unexpected shift.

Field Data and Lab Data

The same metric gets collected two separate ways, and the two answer different questions.

Lab data is a measurement produced under controlled conditions: a fixed network throttle, a fixed device profile, a browser with no extensions, a cold cache. Because it is reproducible, it suits comparison; it is the only reliable way to measure the difference between two builds. The question it does not answer is what real users actually experience.

Field data is a measurement collected from real visits. It carries device diversity, network conditions, and user behavior. The question it does not answer is why: it reports that a metric got worse, not which resource caused the delay.

Neither substitutes for the other. Field data shows that a problem exists; lab data shows where. Metrics like interaction to next paint arise only through user interaction, so they cannot be produced directly under lab conditions; measuring them requires simulating the interaction, and a simulation does not represent a real usage pattern.

Summarizing a Distribution

Field data is not a single number but a distribution, and which number summarizes that distribution changes the conclusion.

// distribution.mjs — two different summaries of the same page: the mean and percentiles

// The distribution is a generated sample, not measured field data:
// a deterministic generator produces 500 visits.
let seed = 20260728;
const random = () => (seed = (seed * 1103515245 + 12345) % 2147483648) / 2147483648;

const visits = Array.from({ length: 500 }, () => {
  // Roughly a third of visits are in the slow condition (distant network, slow device).
  const slow = random() < 0.3;
  return slow
    ? Math.round(3400 + random() * 2100)
    : Math.round(1200 + random() * 800);
});

function percentile(arr, p) {
  const sorted = [...arr].sort((a, b) => a - b);
  const position = (p / 100) * (sorted.length - 1);
  const lower = Math.floor(position);
  const upper = Math.ceil(position);
  return sorted[lower] + (sorted[upper] - sorted[lower]) * (position - lower);
}

const mean = visits.reduce((a, b) => a + b, 0) / visits.length;
console.log("largest contentful paint summary (generated sample distribution, 500 visits)");
for (const [label, value] of [
  ["mean", mean],
  ["median (50th)", percentile(visits, 50)],
  ["75th percentile", percentile(visits, 75)],
  ["95th percentile", percentile(visits, 95)],
]) {
  const rating = value <= 2500 ? "good" : value <= 4000 ? "needs improvement" : "poor";
  console.log(`${label.padEnd(16)}${(value / 1000).toFixed(2)} s   -> ${rating}`);
}

const goodRatio = visits.filter((v) => v <= 2500).length / visits.length;
console.log(`share of visits under the threshold: ${(goodRatio * 100).toFixed(1)}%`);
largest contentful paint summary (generated sample distribution, 500 visits)
mean            2.38 s   -> good
median (50th)   1.73 s   -> good
75th percentile 3.56 s   -> needs improvement
95th percentile 5.15 s   -> poor
share of visits under the threshold: 72.4%

The distribution is built inside the program with a deterministic generator; it is not measured field data but a sample generated to show the difference between two summarization methods. A third of the visits are assumed to be in the slow condition, the rest in the fast condition.

The result gives the same data two conflicting answers. The mean is 2.38 seconds and falls in the good rating; the 75th percentile is 3.56 seconds and falls in the needs-improvement rating. The gap is not a computation error — it is how the mean works: fast visits hide the weight of the slow ones.

A percentile cannot do that. The 75th percentile means “three-quarters of users saw better than this value,” and it corresponds directly to a share of users. The share of visits under the threshold gives the same information: in this distribution, 72 percent of visits fall under the good threshold.

The metrics’ standard thresholds are also defined over a distribution. For largest contentful paint, up to 2.5 seconds counts as good and up to 4 seconds as needs improvement; for cumulative layout shift the limits are 0.1 and 0.25; for interaction to next paint they are 200 and 500 milliseconds. The classification applies not to a single visit but to the chosen percentile.

Choosing Metrics for the Station Pages

The application’s two pages are not judged by the same metrics.

The station list page is a navigation starting point. The user comes here to see the list and immediately taps a row. The decisive metrics are largest contentful paint and cumulative layout shift: if the list arrives late, the user waits; if it shifts late, the user taps the wrong row.

The measurement detail page is a working surface. The user changes the date range, filters the chart, converts units. The decisive metric is interaction to next paint; loading happens once, interaction happens dozens of times.

Metric selection therefore depends on the page’s job. Tracking every metric with the same weight on every page hides which job broke.

Summary

  • Technical milestones measure the browser’s internal flow; user-centric metrics measure what the user sees and does, and the two can degrade independently of each other.
  • The three questions map to five metrics: time to first byte and first contentful paint measure the blank screen ending, largest contentful paint measures the real content’s arrival, interaction to next paint measures response time, cumulative layout shift measures visual stability.
  • A layout shift’s score is the product of the impact fraction and the distance fraction; what determines the cost is not the shifting element’s size but how much content sits beneath it.
  • Field data shows that a problem exists, lab data shows where; one is reproducible, the other is representative.
  • When a distribution is summarized with the mean, slow visits get hidden; because a percentile corresponds directly to a share of users, metric thresholds get applied to a percentile.

Next Step

The metrics are defined, and which number to read them by is settled. The next question is where the measured time gets spent. If largest contentful paint happens late, the delay sits in one of three places: the server producing a response, the response being transported, or the resources the browser has to wait for before it can paint anything. The third is entirely under the developer’s control and is often the largest share. The next lesson addresses which resources the first paint waits for, and computes how many round trips that wait costs, through a calculable model.

To keep your progress and take notes, Log in

My notes

Log in to take notes.

Start typing to search.

↑↓ Esc navigate · open · close