Title: AnGo Scroll Analytics
Author: andrewgolubev
Published: <strong>Settember 20, 2026</strong>
Last modified: October 14, 2026

---

Search plugins

![](https://ps.w.org/ango-scroll-analytics/assets/icon.svg?rev=3704194)

# AnGo Scroll Analytics

 By [andrewgolubev](https://profiles.wordpress.org/andrewgolubev/)

[Telechargiada](https://downloads.wordpress.org/plugin/ango-scroll-analytics.1.12.0.zip)

 * [Details](https://roh.wordpress.org/plugins/ango-scroll-analytics/#description)
 * [Reviews](https://roh.wordpress.org/plugins/ango-scroll-analytics/#reviews)
 *  [Installation](https://roh.wordpress.org/plugins/ango-scroll-analytics/#installation)
 * [Development](https://roh.wordpress.org/plugins/ango-scroll-analytics/#developers)

 [Support](https://wordpress.org/support/plugin/ango-scroll-analytics/)

## Descripziun

AnGo Scroll Analytics records how far visitors scroll through your posts and pages
and shows the result on a „Readability“ screen in wp-admin. It answers the question
a pageview count cannot: did anyone actually read past the first screen?

The tracking script is loaded on the single page of every public post type — posts,
pages, WooCommerce products, custom post types from any plugin (media attachments
are left out) — and measures how far the page was scrolled during a visit. That 
is depth through the scrollable height of the document, not the share of the article
visible on screen: a page that fits on one screen records 100%.

A visit is registered as soon as the page loads — a visitor who scrolls nowhere 
still gets a row, with 0%. After that, data is sent to the server only when reading
progress actually advances (checked every 5 seconds), and always when the visitor
leaves the page or switches tabs (via navigator.sendBeacon, or fetch with keepalive
where it is unavailable). A failed send is retried at the next check.

One visit is one row in the table, updated as the reader gets further down. A new
tab or a new session is a new visit.

#### What the dashboard shows

 * The period, chosen by hand (the last 30 days by default), with an „Apply“ button
   and grouping by day or by month.
 * A summary for the selected period: number of visits, average scroll %, median.
 * Percentiles 5/10/25/50/75/90/100, phrased as „X% of visitors scrolled at least
   to Y% of the article“. A „Percentile mode“ switch (Reach / Classical) feeds both
   this list and the table columns from either the reach computation — how far readers
   actually got — or the classical one: the standard PERCENTILE.INC percentiles,„
   X% of visitors reached no further than Y%“.
 * An „Exclude load-and-die visits“ checkbox (on by default): visits whose only 
   beacon fired at page load — on a live site mostly bots and link previews — are
   left out of the statistics, and the Visits card shows how many were excluded.
   Uncheck it to see the raw population.
 * A trend chart: average scroll as a line, visits as bars.
 * A table per item: title, type (the content type’s own name — Post, Page, Product),
   visits, average scroll, and a „Percentiles“ group — seven sortable columns (5/
   10/25/50/75/90/100) showing the scroll depth each share of that article’s visitors
   reached. Every column header is clickable and sorts the table (first click ascending,
   second descending; numbers as numbers, titles alphabetically), so „most visited“
   or „worst-median depth“ is one click away — the 50% column is the median. A „
   Search articles“ box above the table narrows the rows as you type, matching titles
   case-insensitively; it composes with the active sort and survives an Apply.
 * A „Readability brief“ widget on the admin Dashboard home screen: the same numbers
   for the last 7 days at a glance, with a link to the full report. It is a standard
   dashboard widget — visible to administrators, and movable, collapsible or hideable
   through Screen Options like every other box there.

#### Performance at scale

The plugin computes the statistics on the fly with database queries. At very large
volumes (hundreds of thousands of visits) caching (transients) or cron-based aggregation
is worth adding. A typical blog does not need it.

#### Rate limiting and the database

Without a persistent object cache, each beacon writes the throttle counter to the
options table, so the limit costs two queries per beacon. That is cheap at the scale
this plugin targets — it is a spam brake, not a firewall.

## Maletgs dal visur

[⌊The Readability dashboard: the filter bar, visits / average scroll / median cards,
the percentile list ("X% of visitors scrolled at least to Y% of the article"), and
the visits-plus-average-scroll trend chart.⌉⌊The Readability dashboard: the filter
bar, visits / average scroll / median cards, the percentile list ("X% of visitors
scrolled at least to Y% of the article"), and the visits-plus-average-scroll trend
chart.⌉[

The Readability dashboard: the filter bar, visits / average scroll / median cards,
the percentile list („X% of visitors scrolled at least to Y% of the article“), and
the visits-plus-average-scroll trend chart.

## Installaziun

 1. In wp-admin go to Plugins  Add New  Upload Plugin.
 2. Choose the downloaded archive (ango-scroll-analytics-1.12.0.zip), click „Install
    Now“, then „Activate“.
 3. On activation the plugin creates its table in the database, {prefix}_ango_visits(
    wp_ango_visits with the default prefix). If the schema changes in a later version,
    the table is brought up to date the next time wp-admin is opened (dbDelta).
 4. A „Readability“ entry appears in the left-hand menu — the dashboard lives there.

## FAQ

### Do visits with no scrolling really count?

Yes. Every arrival is a row, including the ones with 0% scroll: the percentiles 
describe all arrivals, not only those who read to the end. That is a deliberate 
decision, not lost data.

The dashboard does distinguish two kinds of zero-scroll visits. A visit that sent
nothing after the initial page-load beacon (max_scroll 0 and time on page 0) is „
load-and-die“ — on a live site those are usually bots, crawlers and link previews,
not people. They are excluded from the statistics by default (the Visits card shows
how many were left out), while a person who loaded the page and stayed without scrolling—
time on page above zero — always counts. The checkbox in the filter bar turns the
exclusion off.

### Is the tracking endpoint public?

Yes — it has to be, because visitors are not logged in. POST /wp-json/ango/v1/depth
is limited to 60 requests per minute per IP; the limit is changed through the ango_rate_limit
filter, and a value below 1 disables the check. The IP itself is not stored, only
a hash of it, for the duration of the window. Behind a proxy (Cloudflare and similar)
REMOTE_ADDR is the proxy address, so the limit is shared by all visitors — raise
it in that setup.

### Why do the numbers differ from my analytics tool?

This plugin measures depth through the scrollable height of the page, not the share
of the article that was on screen: a short page that fits on one screen records 
100%. And it counts visits, not unique readers — a new tab or a new session is a
new visit.

### What timezone are the numbers in?

Timestamps in the table (created_at) are stored in UTC, while the dashboard computes
day boundaries in the site timezone. Keep that in mind when writing manual SQL against{
prefix}_ango_visits.

### Can I track custom post types?

They are tracked by default: the script loads on the single page of every public
post type, so a WooCommerce product, a forum thread or a knowledge-base entry all
get measured the moment they exist. Media attachments are the one exception — they
are public but are media-library artifacts, not pages a reader scrolls. If a plugin
registers a public type you do not want counted (a page-builder’s template library,
a popup type), remove it with the ango_trackable_post_types filter:

    ```
    add_filter( 'ango_trackable_post_types', function ( $types ) {
        unset( $types['my_popup'] );
        return $types;
    } );
    ```

### Does the admin screen load anything from outside my site?

No. The chart is drawn by Chart.js 4.5.1, bundled with the plugin (assets/vendor/
chart.umd.min.js, MIT license), so wp-admin makes no external request and the chart
works offline and behind a filtering proxy.

### Why does the bundled Chart.js ship minified?

Chart.js 4.5.1 (MIT) ships here as the minified UMD build that upstream publishes—
assets/vendor/chart.umd.min.js, licence banner intact. Upstream has no unminified
UMD to use instead: dist/chart.umd.js and dist/chart.umd.min.js in its package are
both minified. The readable code is published, just in another build: the unminified
ESM bundle is dist/chart.js (https://unpkg.com/chart.js@4.5.1/dist/chart.js), and
the sources it is built from are at https://github.com/chartjs/Chart.js/tree/v4.5.1/
src.

### Why are the file names so plain?

They are deliberately neutral: the tracking script is assets/js/scroll-depth.js 
and the public endpoint is /wp-json/ango/v1/depth, with no tracker/track in either.
Those words are a common target for filter lists, and a blocked script or beacon
loses data silently. Neither EasyList/EasyPrivacy nor the uBlock Origin and AdGuard
filters matched these URLs (checked 2026-09-16 under the plugin’s previous name;
the endpoint paths did not change in the rename), but the lists update daily — so
do not rename them back „for clarity“.

### What happens to my data when I uninstall the plugin?

Deleting the plugin removes everything it stored: the {prefix}_ango_visits table,
the ango_db_version option and the throttle transients. Deactivating does not — 
the data survives a deactivate and reactivate cycle, so an upgrade or a temporary
switch-off costs nothing. Clicking Delete on the Plugins screen shows a confirmation
naming the statistics that will be lost. If you need the numbers after the plugin
is gone, export them before deleting, or hold the cleanup back from a must-use plugin
with add_filter( ‚ango_remove_data_on_uninstall‘, ‚__return_false‘ ).

## Reviews

There are no reviews for this plugin.

## Contributors & Developers

“AnGo Scroll Analytics” is open source software. The following people have contributed
to this plugin.

Contributors

 *   [ andrewgolubev ](https://profiles.wordpress.org/andrewgolubev/)

[Translate “AnGo Scroll Analytics” into your language.](https://translate.wordpress.org/projects/wp-plugins/ango-scroll-analytics)

### Interested in development?

[Browse the code](https://plugins.trac.wordpress.org/browser/ango-scroll-analytics/),
check out the [SVN repository](https://plugins.svn.wordpress.org/ango-scroll-analytics/),
or subscribe to the [development log](https://plugins.trac.wordpress.org/log/ango-scroll-analytics/)
by [RSS](https://plugins.trac.wordpress.org/log/ango-scroll-analytics/?limit=100&mode=stop_on_copy&format=rss).

## Changelog

#### 1.12.0

 * New: a „Percentile mode“ switch on the dashboard (Reach / Classical). Reach percentiles
   answer „how far did readers get“ — at least X% of visitors reached this depth.
   Classical percentiles are the textbook statistical view, computed exactly like
   Excel’s PERCENTILE.INC — X% of visitors reached this depth or less. The switch
   changes the summary list, the table columns and their tooltips at once, needs
   no reload and is remembered per browser. Reach stays the default.

#### 1.11.0

 * New: a „Search articles“ box above the „By article“ table. It filters the rows
   live as you type — case-insensitive title matching, no Apply click, no extra 
   requests — composes with any active sort, and survives a re-render. When nothing
   matches, the table says so instead of showing a stale row set.

#### 1.10.0

 * New: the „By article“ table replaces the single „90% scrolled at least to“ column
   with a full „Percentiles“ group — seven sortable sub-columns (5/10/25/50/75/90/
   100) showing the depth each share of that article’s visitors reached. The separate
   Median column is gone; the 50% sub-column IS the median, computed the same way
   from the same rows. Wide screens scroll the table horizontally instead of squeezing
   it.

#### 1.9.0

 * New: the tracker covers every public post type, not only posts and pages — WooCommerce
   products and any plugin’s custom post types are now measured and appear in the„
   By article“ table. Media attachments stay untracked, and the new ango_trackable_post_types
   filter removes a type a site does not want counted.
 * New: the „By article“ table names each row’s content type — a product reads „
   Product“, not „Post“.

#### 1.8.1

 * New: article titles in the „By article“ table are clickable links to the article
   itself, opening in a new tab. Posts that no longer exist stay plain text.
 * Fix: the sortable column headers no longer show their label twice („ArticleArticle“).
   The 1.8.0 headers printed the column name both outside and inside the sort button;
   it now appears once.

#### 1.8.0

 * New: the „By article“ table is sortable. Click a column header to sort by it —
   first click ascending, second descending, with a direction arrow on the active
   column. Numbers sort numerically, titles and types alphabetically; finding the
   most visited or worst-median article no longer means eyeballing the rows.

#### 1.7.0

 * New: a „Readability brief“ widget on the admin Dashboard home screen. Visits,
   average scroll, median and the „90% scrolled at least to X%“ line for the last
   7 days, plus a link to the full report. Server-rendered — no extra scripts on
   the Dashboard — and counts visits exactly like the Readability page, load-and-
   die exclusion included.

#### 1.6.0

 * New: the dashboard percentile list gains the low percentiles 5/10/25 alongside
   50/75/90/100. On a bounce-heavy audience the classic lines read 0 and say nothing,
   while „25% of visitors scrolled at least to X%“ stays informative.

#### 1.5.0

 * New: logged-in visitors are not tracked — the site owner’s own views no longer
   pollute the percentiles. The tracking script is not loaded for logged-in users,
   and the endpoint refuses their beacons. Multi-author sites that want member visits
   counted can turn the exclusion off with the ango_track_logged_in filter.

#### 1.4.0

 * Fix: the tracker no longer records 100% depth when the page layout is still unsettled
   at load time (scrollable height not yet known) — such loads were miscounted as
   full-depth reads.

#### 1.3.0

 * New „Exclude load-and-die visits“ filter (on by default): page loads whose only
   beacon fired at page load — mostly bots and link previews — are excluded from
   all dashboard statistics. The Visits card shows how many were excluded, and a
   checkbox in the filter bar turns it off.

#### 1.2.1

 * Fix: the author is shown as Andrew Golubev, the Details link points to the WordPress.
   org page, and „Details“ is translated in Russian.

#### 1.2.0

 * Renamed to AnGo Scroll Analytics per the WordPress.org review; all code symbols,
   options and the REST namespace moved to the ango_ prefix.
 * Translation files no longer ship in the package — translations come from translate.
   wordpress.org.

#### 1.1.8

 * Fix: cleared all Plugin Check errors (inline prepare() calls, %i identifier placeholder,
   sanitized REMOTE_ADDR).

#### 1.1.7

 * Readme discloses the bundled Chart.js 4.5.1 build (MIT).

#### 1.1.6

 * Uninstall cleanup: deleting the plugin removes its table, options and transients,
   with a warning on the Plugins screen naming what is lost.
 * Added the WordPress.org readme metadata block and plugin header fields.

#### 1.1.5

 * Fix: rebuilt the Russian translation file with a parseable header (the previous
   build shipped a malformed one).

#### 1.1.4

 * Fix: removed a doubled percent sign in the percentile lines (50%% -> 50%).

#### 1.1.3

 * Plugin attributed to Adviko Inc.

#### 1.1.2

 * Neutral asset and endpoint names (scroll-depth.js, /wp-json/…/depth) so ad-blocker
   filter lists don’t silently break tracking.

#### 1.1.1

 * Fix: the Apply button sits on the controls‘ line in the filter row.

#### 1.1.0

 * The admin interface is translatable (English source, Russian catalogue).

#### 1.0.1

 * Fix: Chart.js is bundled with the plugin instead of loaded from a CDN URL that
   returned 404.

#### 1.0.0

 * Initial release: scroll-depth tracking on posts and pages, the Readability dashboard(
   visits, averages, percentiles, trend chart, per-item table), UTC storage with
   site-timezone day grouping.

## Meta

 *  Version **1.12.0**
 *  Last updated **avant 6 uras**
 *  Active installations **10+**
 *  WordPress version ** 6.8 or higher **
 *  Tested up to **7.1.3**
 *  PHP version ** 7.4 or higher **
 *  Language
 * [English (US)](https://wordpress.org/plugins/ango-scroll-analytics/)
 * Tags
 * [content engagement](https://roh.wordpress.org/plugins/tags/content-engagement/)
   [scroll depth](https://roh.wordpress.org/plugins/tags/scroll-depth/)
 *  [Advanced View](https://roh.wordpress.org/plugins/ango-scroll-analytics/advanced/)

## Ratings

No reviews have been submitted yet.

[Your review](https://wordpress.org/support/plugin/ango-scroll-analytics/reviews/#new-post)

[See all reviews](https://wordpress.org/support/plugin/ango-scroll-analytics/reviews/)

## Contributors

 *   [ andrewgolubev ](https://profiles.wordpress.org/andrewgolubev/)

## Support

Got something to say? Need help?

 [View support forum](https://wordpress.org/support/plugin/ango-scroll-analytics/)