Boloop-made explanatory illustration: only reported foreground-visible segments are counted after consent; background time is excluded.
When adding analytics to Boloop, I wanted to know whether readers spent some time on a page. A page view tells me that a page opened. It does not distinguish a quick close, careful reading, or a tab left in the background.
We settled on a narrower measurement: after a visitor accepts optional analytics, record how long an eligible public page was visible in the foreground. Our site records do not store IP addresses or build a visitor identity across pages. “Effective” here means meeting the timing and reporting rules, not understanding the content.
Decide what you do not need to know
Comparing whether pages received some attention does not require a lasting profile of each person. Boloop generates an independent random UUID for each timing event and reports it with the page path and visible milliseconds. The identifier supports short-term deduplication, not recognizing the same person on their next page.
The record is therefore small: a public path, a visible-time segment, and its event ID. It contains no account identity, raw search text or persistent identifier for linking activity across pages.
“Our site records do not store IP addresses” describes the fields and purpose of this analytics data. The network connection between browser and server still requires address information. The statement does not mean that IP addresses are never processed on the internet or that every infrastructure provider has no logs.
Consent remains an ongoing condition
Timing reuses Boloop’s existing optional analytics consent. It requires valid consent that can be stored durably, and runs only on the production origin and allowed public pages. Local previews, the admin area and ineligible paths do not take part.
In the deployed three-language version discussed here, the entry points cover public English, Simplified Chinese and Traditional Chinese paths. Adding languages cannot be declared fully tested by extending one regular expression; measurement needs to be checked alongside page availability and the new language flows.
Withdrawing consent discards timing that has not yet been reported. Requests already sent cannot be recalled by the browser, and both the interface and privacy explanation need to state that boundary accurately.
I do not want an analytics error to stop someone reading an article. If storage is unavailable, secure randomness is missing, or a request fails, measurement can drop the event while the page remains usable.
Stop timing when the page is hidden
The implementation uses page-visibility events to accumulate visible time, ending the current record when the page is hidden, left, or replaced by another site route. Background time after hiding does not enter that segment. In the current implementation, becoming visible again does not automatically create a new record; page logic must re-enter the timing entry point. Each record therefore represents one reported visible segment, not a complete, continuous duration for the whole visit. Re-entering the timing flow uses a new event and still does not reconstruct an individual reading history.
This avoids a common overstatement: opening an article, doing something else for ten minutes, then returning to close it should not turn all ten minutes into article time.
Visibility is not attention, however. A visible window may have nobody watching or may be held open by an automated browser. A reader may also take a screenshot, copy text or continue offline, outside the measurement. A dashboard label such as “visible time” is more accurate than “actual reading time.”
Restrained reporting still needs server validation
Boloop drops records shorter than one second and clamps accumulated client time above thirty minutes. The server separately validates the path, UUID, exact field set and integer duration range. It cannot assume every request came from our own page.
A repeated event ID is accepted only once. Deduplication rows are retained briefly and then cleaned up; daily aggregates use UTC. Administrator sessions and identifiable bot requests are excluded, and minute and daily admission budgets also apply.
These limits make data more manageable, not impossible to forge. Budgets may drop events, and reporting before a connection closes can fail. We accept bounded gaps rather than retrying until measurement interferes with visiting the site.
If you reuse a similar approach, keep client filtering and server validation together. Merely hiding fields in the front end does not prevent an external request from submitting extra data to the endpoint.
Test the timing mechanism, then interpret averages carefully
Boloop’s regressions cover specific scenarios: four visible seconds followed by one minute in the background must not count the background minute; a route change must settle the old page’s time against the old path; a five-hundred-millisecond record must not be sent; a long record must not exceed the cap; rejected consent or a preview origin must produce no report.
Those checks establish the corresponding mechanisms. They do not prove that every browser, window-close network condition, or real person’s attention has been measured accurately.
Operations provided another reminder: in a small sample, one long automated page visit accounted for most of the total time. A higher average in that situation cannot show that recommended content kept readers engaged for longer. Look first at sample count, total duration, page distribution and possible operational visits before deciding whether a change has a defensible explanation.
For a personal site, this measurement can help raise useful questions: does a page deserve closer examination, and is there a continuing observation on the same basis after a change? It does not need to identify people, assess understanding or prove growth. Narrowing the data and its interpretation together makes the measurement easier to maintain.