When we measure the page loading speed from the user’s perspective, we pay attention to the appearance of subsequent elements on the screen. Metrics such as First Contentful Paint, First Meaningful Paint and Visually Complete directly reflect what the user sees and when. But what if the page is invisible, when it loads in the background, for example in a different tab? Should we consider such views interesting for us? Don’t the collected metrics distort the results?

## Methodology

To answer these questions, we first need information about whether the page was visible when it was loading. For this purpose, we decided to send new pieces of information from the browsers, collected by the Page Visibility API:

• Initial page visibility state
• Change of page visibility

Each time the tab is switched, hidden or the browser window is displayed (including switching the desktop in the operating system), it is recorded.

By comparing time of visual metrics with time of visibility changes, we can split page views into two buckets:

• Visible all the time until Visually Complete

## Results

After splitting metrics in the way described above, we can compare the number of page views of each type and the values of the main metrics collected for them. We did this for three Allegro pages: home page, offers listing and the offer page. The one loaded in the background the most was the offer page, then homepage and last — listing page. This seems to correspond to the scenarios of using these pages — e.g. when browsing search results, users open multiple offers in the background to compare them with each other.

Many more views took place in the background for desktops than for smartphones. This data is not surprising, either, since on the phone it is much less convenient to use websites in many tabs.

For smartphones, First Contentful Paint time turned out to be several hundred seconds (!) higher for pages loading in the background than for visible pages. For desktops, it was much less, but still over 100 seconds.

When we excluded an error in the aggregation algorithm, we looked at how we collect metrics. For better approximation of the moment when content appears on the screen, we used to use the requestAnimationFrame API, which has been available for a long time in all major browsers. This function allows plugging in custom code into the browser rendering process, just before the work of drawing the content. At this very moment — when the interesting part of HTML was parsed, but before showing it on the screen — we send information to the server. Unfortunately, currently there is no browser API that would allow code to be executed right after the visible content has been updated.

<div id="meaningul-content-here"></div>
<script>
function sendMark() {
window.performance.mark('FirstMeaningfulPaint');
}

requestAnimationFrame(sendMark);
</script>


It turned out that in order to optimize the use of hardware resources, the browser does not render anything for tabs in the background. Even though it still downloads stylesheets and parses HTML, it omits calculation of elements’ dimensions (so called Layout) and drawing them. There is no animation frame rendered, thus the code passed to requestAnimationFrame is not executed. This stage of work is postponed until the tab is activated. The reporting function starts almost at the same time as sending information about the change of visibility of the page. We have experimentally proved this hypothesis, and it also found confirmation in the collected data. First Paint — a metric reported by the browser itself — is also very high for hidden tabs. This means the first frame render occurs when a tab becomes active.

In order not to bias the results, we decided to conditionally — for invisible pages — collect metrics related to drawing at the time of parsing the HTML code of elements. We are aware that this is a distortion in the opposite direction (measured time is smaller than it should be), but the expected difference is much smaller than when using requestAnimationFrame.

<div id="meaningul-content-here"></div>
<script>
function sendMark() {
window.performance.mark('FirstMeaningfulPaint');
}

if (document.visibilityState != 'visible') sendMark();
else requestAnimationFrame(sendMark);
</script>


After implementing the patch and re-checking the data, the picture changed significantly, but the trend remained — invisible pages load noticeably slower and their higher times affect the overall result.