The hidden pane: why a loader reading needs a visible window
What happened
On 9 October at 09:07 one of our reviewers read a client review site at desktop width (1440 pixels) and recorded it as not ready: the site's loading screen was still showing "Loading 50%" after eight to ten seconds, with a checkerboard of squares over the page behind it (R2). Every asset had loaded and the console showed no errors, which made the stall look like a defect in the build.
Six minutes later a second reading of the same live build, in a visible Chrome window on a laptop with a real graphics chip, found no stall at all. The loader left at 482 milliseconds with a cold cache and 308 milliseconds warm, with no console errors and nothing waiting on the network (R3). A separate instrument, run four times on the same address, saw the loader leave between 997 and 1530 milliseconds, never resting on "Loading 50%" for more than 0.63 seconds (R5).
The difference was the window. When the same page was opened in a background tab, the loader sat at 50% for as long as the tab stayed hidden, and finished 23 milliseconds after the tab was brought forward (33 milliseconds on a warm cache) (R4). Our reviewer then checked its own browser pane by script and found that it had been hidden during the desktop reads (R6). The not ready verdict was withdrawn at 09:16 as a reading error.
Why a hidden page looks broken
The loader on that site waits for exactly two things: the fonts, and the main picture reporting that it is decoded and ready to draw. The second is a standard browser call, decode(), which returns a promise that resolves once the image is ready to be shown1. In our runs Chrome 155 did not resolve that promise while the document was hidden, even though the picture itself had fully downloaded. Fonts resolved, so the loader showed 50%, and then it waited.
The frozen pattern behind it has a documented cause. Browsers stop calling requestAnimationFrame, the function that drives on-screen animation, for pages in background tabs or hidden frames, to save power2; Chrome states that it does not call it when a page is in the background3. A page can ask whether it is visible through document.visibilityState4, part of the Page Visibility API5, and be told when that changes through the visibilitychange event6.
None of this is a fault in the browser. Holding back work nobody can see is the right call for battery and speed. It only becomes a problem when the person reading the page is a tester, a reviewer or an automated tool whose window is not in front.
It was not a one-off
The day before, a public speed test7 opened in a hidden pane had stalled at "Running analysis" and was logged as needing a visible window (R1). Later that morning the same reviewer read a second client site and this time checked its visibility at every read: 390 pixels was visible for one read, 1440 was hidden, and the loader held at 75 or 100 percent. Those readings were declared void before any verdict (R7). A headed run in a visible, focused window then saw the loader leave at 1.99 seconds at 1440 and 1.88 seconds at 390, with no percentage held for more than 0.48 seconds (R8).
On 10 October a reviewer captured a museum archive page black, twice, at phone width, although the page's text and colours were all in place (R9). Nine minutes later frames taken in a visible Chrome window on a real graphics chip, at 0, 300, 1200 and 3000 milliseconds after load, all showed the page, with a mean luminance of 30 in the dark theme and 219 in the light theme, where a black frame would read close to 0 (R10). The black captures were most likely the same artefact; we did not reproduce them in a hidden pane, so we do not claim it.
What we changed in how we read
Since 9 October our loader readings record the page's visibility with every sample (R8). A reading taken while the document is hidden is marked void and repeated in a visible window; it is never counted as a pass or a fail. Reviewers who work through a side pane state in their verdict whether the pane was visible. Loader timings that decide whether a site ships are taken in a headed browser, in a visible and focused window, on a real graphics chip.
For the sites themselves we proposed one robustness change: a loader should never wait without limit on something a hidden page will not finish. Either it skips itself when the page starts hidden, or it caps its wait (about 1.5 to 2.5 seconds) so a visitor who opens the link in a background tab never meets a stuck percentage when they switch to it. That change is queued in our shared site kit and has not shipped, so we claim no result for it here. A real visitor was never affected in the cases above: the loader finished within 33 milliseconds of the tab being shown.
What we do better next
Three things, none of them measured yet. First, the loader fallback goes into the shared kit once, so every site built on it inherits it, and its proof includes a background-tab run: the page opened hidden, then shown, with the time to a clear screen recorded. Second, our reading instruments refuse to report a timing or a screenshot without the page's visibility beside it, so a hidden reading cannot be mistaken for a result. Third, a reviewer who sees a stuck or blank page checks visibility before writing a verdict, not after.
The rule, for anyone checking a site
If you are judging whether a page is slow, blank or stuck, look at it in a window that is on screen and in front. Background tabs, minimised windows, side panes that are collapsed and automated browsers without a screen can all hold back drawing and some loading work. A reading from such a window tells you about the window, not the site.
Limits
All readings are from Chrome 155 and the Chromium-based browser panes of our own tools, on two laptops; we did not test other browsers. The decode() behaviour is what we measured in Chrome 155, not a claim about the specification. The loader times are single runs or runs of four, not large samples. The museum page's black captures (R9) were not reproduced in a hidden pane. The kit change and the three steps in "What we do better next" are plans and are unmeasured. Client sites are not named; each reading is listed in the record below with its date, time and file.
Appendix: the record, 8 to 10 October 2026
| Run | When (IST) | What was read | As recorded | Kept in |
|---|---|---|---|---|
| R1 | 08 Oct 2026 05:04 | Public speed test opened in a hidden browser pane | stalled at "Running analysis"; logged as needing a visible pane | studio log entry, 8 Oct 2026 05:04 |
| R2 | 09 Oct 2026 09:07 | Our reviewer reads a client review site at 1440 in a pane | loader on screen after 8 to 10 s at "Loading 50%" (100% once), a checkerboard over the page; recorded as not ready | studio log entry, 9 Oct 2026 09:07 |
| R3 | 09 Oct 2026 09:13 | Same live build, headed Chrome 155, 1440 x 900, visible tab | loader gone at 482 ms cold, 308 ms warm; 0 console errors; 0 pending requests | reading notes with two timing files (visible cold, visible warm), 9 Oct 2026 |
| R4 | 09 Oct 2026 09:13 | Same build, tab opened in the background | held at "Loading 50%" while hidden; gone 23 ms (cold) and 33 ms (warm) after the tab was shown | the same reading notes with the background-tab probe script, 9 Oct 2026 |
| R5 | 09 Oct 2026 09:12 to 09:14 | Separate instrument, same address, visible window, 4 cold runs | loader exit 1530, 1423, 997, 1465 ms; time on "Loading 50%" 0 to 0.63 s; 0 console errors | timing script and its log entry, 9 Oct 2026 09:14 |
| R6 | 09 Oct 2026 09:2x | Reviewer checks its own pane by script | document.visibilityState = hidden during the 1440 reads; NOT OK withdrawn at 09:16 | studio log entry, 9 Oct 2026 09:16 |
| R7 | 09 Oct 2026 09:33 | Reviewer reads a second client site, checking visibility at each read | 390 visible for one read, 1440 hidden; loader held at 75 or 100%; those readings declared void before judging | studio log entry, 9 Oct 2026 09:33 |
| R8 | 09 Oct 2026 09:46 | Second site, headed Chrome 155, visible and focused, cold cache | 1440 gone 1.99 s; 390 gone 1.88 s; no percentage held more than 0.48 s; 0 failed requests | studio log entry, 9 Oct 2026 09:46 |
| R9 | 10 Oct 2026 22:44 | A reviewer captures a museum archive page in a pane at 390 | captured black twice although the page was complete, opacity 1, text on a dark ground | studio log entry, 10 Oct 2026 22:44 |
| R10 | 10 Oct 2026 22:53 | Same page, headed Chrome on a real GPU, 390 x 844, frames at 0, 300, 1200 and 3000 ms | every frame shows the page; mean luminance 30 (dark theme) and 219 (light theme) | eight frames (dark and light at four times) and a contact sheet, 10 Oct 2026 |
Sources
- https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/decode
- https://developer.mozilla.org/en-US/docs/Web/API/Window/requestAnimationFrame
- https://developer.chrome.com/blog/background_tabs
- https://developer.mozilla.org/en-US/docs/Web/API/Document/visibilityState
- https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API
- https://developer.mozilla.org/en-US/docs/Web/API/Document/visibilitychange_event
- https://pagespeed.web.dev/