> ## Documentation Index
> Fetch the complete documentation index at: https://docs.grainql.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Attention quality

> Lists the rules that decide when Analytics Web counts a section view or a scroll as attention.

Analytics Web sends a section view event and a heatmap scroll event while a user reads a page.
Four rules stop those events when the user is not looking. Each rule has a threshold. The
thresholds are fixed and are not part of `GrainConfig`.

## 1. Page visibility

Rule: tracking stops while the page is hidden.

Reason: a page in a background tab or a minimized window receives no attention. Events sent
during that time inflate time on section.

How it is applied: the SDK reads `document.visibilityState` through the Page Visibility API.
Tracking pauses at the moment the page becomes hidden. When the page becomes visible again,
tracking resumes and every section timer starts from zero.

## 2. User activity

Rule: tracking stops after 30 seconds without user input.

Reason: a user who has not moved the mouse, touched the screen, pressed a key, or scrolled for
30 seconds is not reading the page.

How it is applied: the SDK listens for `mousemove`, `mousedown`, `keydown`, `scroll`,
`touchstart`, and `click`. It records the time of the last input, debounced to one update per
500 milliseconds. Before it sends an event, the SDK compares the current time with that
record. If the gap is more than 30 seconds, the event is not sent. Tracking resumes on the
next input.

## 3. Section duration cap

Rule: a section collects at most 9 seconds of attention before a scroll is required.

Reason: a user who stays on one section without a scroll is either reading it, or has stopped
looking. The cap keeps the first case and drops the second. A user who reads scrolls to the
next part.

How it is applied: the SDK sends a section view event every 3 seconds while a section is
visible and adds the duration to a total for that section. When the total reaches 9 seconds,
the SDK stops sending events for that section. The total returns to zero when the user scrolls
100 pixels or more, when the user moves to another section, or when the page becomes visible
again.

## 4. Scroll distance

Rule: a scroll counts only when it moves 100 pixels or more.

Reason: a small movement, such as a touchpad drift or a view adjustment, is not a sign that
the user reads on. 100 pixels is about 13 percent of a small mobile screen and about 9 percent
of a desktop screen.

How it is applied: the SDK stores the scroll position of each section at the last reset.
Before it sends an event, the SDK computes the distance from that position. If the distance
is 100 pixels or more, the section total returns to zero and tracking continues. If the
distance is less than 100 pixels and the total has reached 9 seconds, the event is not sent.

## Thresholds

| Threshold                                       | Value            | Where                            |
| ----------------------------------------------- | ---------------- | -------------------------------- |
| Idle time before tracking stops                 | 30 seconds       | Rule 2, `idleThreshold`          |
| Activity debounce                               | 500 milliseconds | Rule 2, the input listeners      |
| Attention per section without a scroll          | 9 seconds        | Rule 3, `maxSectionDuration`     |
| Section view event interval                     | 3 seconds        | Rule 3, section tracking         |
| Scroll distance that resets a section           | 100 pixels       | Rule 4, `minScrollDistance`      |
| Memory per tracked section                      | about 100 bytes  | the section state                |
| Expected drop in section view and scroll events | 20 to 40 percent | varies by site and user behavior |

The values in the SDK are `maxSectionDuration: 9000`, `minScrollDistance: 100`, and
`idleThreshold: 30000`, in milliseconds and pixels. They are the same for every tenant.

## Events

The rules filter two events. An event is sent only when all four rules pass.

| Event                   | Property                    | Holds                                                                              |
| ----------------------- | --------------------------- | ---------------------------------------------------------------------------------- |
| `_grain_section_view`   | `section_name`              | The section identifier.                                                            |
|                         | `section_type`              | The section type, for example `hero`, `content`, or `footer`.                      |
|                         | `duration_ms`               | The length of this segment.                                                        |
|                         | `is_split`                  | `true` for a 3-second segment. `false` for the final segment on exit.              |
|                         | viewport and scroll metrics | The viewport size and scroll depth at the time of the event.                       |
| `_grain_heatmap_scroll` | `viewport_section`          | The screen-height slice of the page: `0` for the first screen, `1` for the second. |
|                         | `scroll_depth_px`           | The scroll depth in pixels.                                                        |
|                         | `duration_ms`               | The time in this viewport section.                                                 |
|                         | `is_split`                  | `true` for a periodic segment. `false` for a change of viewport section.           |

For `_grain_heatmap_scroll`, the viewport section takes the place of the section name in rule
3\. A change of viewport section resets the total.

Read [Automatic tracking](/core/automatic-tracking) for the other events that the SDK sends
without code.

## One reading session

1. The user opens the page. Section tracking starts.
2. After 3 seconds, the SDK sends the first segment. After 6 and 9 seconds, the second and
   third. The total is 9 seconds.
3. The user stays without a scroll. The fourth segment is not sent.
4. The user scrolls 150 pixels. The total returns to zero.
5. After 3 seconds on the new section, the SDK sends a segment.
6. The user switches tabs. Tracking pauses.
7. The user returns after 5 minutes. Tracking resumes and every total returns to zero.
8. The user leaves the desk for 35 seconds. Tracking pauses after 30.
9. The user moves the mouse. Tracking resumes.

## Debug output

Set `debug: true` to write each rule decision to the console.

```typescript theme={null}
const grain = createGrainAnalytics({
  tenantId: 'your-tenant-id',
  debug: true
});
```

| Prefix               | Holds                                                     |
| -------------------- | --------------------------------------------------------- |
| `[AttentionQuality]` | Rule decisions and state changes.                         |
| `[SectionTracking]`  | Section view events and the reason an event was filtered. |
| `[Heatmap Tracking]` | Scroll events and the reason an event was filtered.       |
