@zsoltcsaszti/lens
v0.2.1
Published
Reader intelligence for publishers. One script tag. Zero tracking cookies.
Maintainers
Readme
Lens SDK
Reader intelligence for publishers. One script tag. Zero tracking cookies.
Install
Via CDN (recommended for most publishers):
<script
src="https://unpkg.com/@zsoltcsaszti/lens/dist/lens.umd.js"
data-site-id="YOUR_SITE_ID"
data-api="https://your-lens-server.com"
data-mode="emphasis">
</script>Paste before </body> on every page. That's it — no API keys for readers, no accounts, no configuration files.
Via npm (for bundler-based setups):
npm install @zsoltcsaszti/lensimport '@zsoltcsaszti/lens' // side-effect import — auto-initialises from data-* attributesWordPress: the plugin is included in the npm package. After npm install @zsoltcsaszti/lens, copy node_modules/@zsoltcsaszti/lens/wordpress/lens-analytics/ into wp-content/plugins/, activate, and configure under Settings → Lens Analytics.
What it does
Lens silently observes how each visitor reads — which sections they dwell on, re-read, copy — and builds an anonymous interest profile in their browser. After a couple of visits, it subtly adapts section emphasis so each reader sees what matters most to them first.
What Lens never does:
- Never rewrites a word of your content
- Never sends browsing history or personal data anywhere
- Never requires readers to sign up or install anything
The visitor profile lives entirely in localStorage. Only anonymised engagement metrics (section dwell times, scroll depth) are sent to the analytics server.
Live demos
Run npm start then open:
| URL | What you see |
|-----|-------------|
| http://localhost:3000/ | Demo landing page |
| http://localhost:3000/demo/article-tech.html | Tech deep-dive article |
| http://localhost:3000/demo/article-breaking-news.html | Breaking news style |
| http://localhost:3000/demo/article-magazine.html | Long-form magazine essay |
| http://localhost:3000/demo/article-tutorial.html | Step-by-step tutorial |
| http://localhost:3000/dashboard?site=demo | Publisher analytics dashboard |
| http://localhost:3000/public/index.html | Privacy positioning page |
Run the analytics server
cd lens-sdk
npm install
npm start # production
npm run dev # auto-restart on file changeServer starts at http://localhost:3000. Set the PORT env variable to change it.
Script tag options
| Attribute | Values | Default | Description |
|-----------|--------|---------|-------------|
| data-site-id | any string | demo | Your unique publisher identifier |
| data-api | URL | — | Your Lens server endpoint for analytics |
| data-mode | emphasis · collapse · both · off | emphasis | How to adapt presentation |
| data-debug | true · false | false | Show relevance scores on each section |
Modes:
emphasis— subtle left accent on highly relevant sectionscollapse— folds low-relevance sections to 2 lines with "Show more"both— emphasis on high, collapse on lowoff— analytics only, no visual changes
Dashboard
The dashboard shows what your readers actually engage with — not just pageviews.
http://localhost:3000/dashboard?site=YOUR_SITE_IDWhat's included:
- Section engagement ranked by average dwell time
- Section interest heatmap — sections in article order, coloured cold→hot by attention
- The gap between word count and actual attention (the insight that drives editorial decisions)
- Scroll depth distribution
- Top pages by traffic
How the interest profile builds
The profile is entirely local — it never leaves the visitor's browser.
| Signal | Weight | Why | |--------|--------|-----| | Re-read (scrolled back) | 1.0 | Strongest signal — deliberate | | Clipboard copy | 1.0 | User found it worth saving | | Dwell > 5s | 0.5 | Moderate — attention held | | Slow scroll velocity | 0.2 | Weak — ambiguous |
Keywords from engaged sections are weighted using an exponential moving average (EWMA). The top 120 keywords form the profile. No topic categories are pre-defined — the profile emerges from what each person reads.
Adaptation doesn't start until visit 2. The first visit is silent — building the profile — so there's no false adaptation before the system has signal.
Privacy model
| Data | Where it lives | Sent to server? |
|------|----------------|-----------------|
| Visitor interest profile | localStorage only | Never |
| Section dwell times | Analytics server (aggregate) | Yes — anonymous, no user identity |
| Scroll depth | Analytics server | Yes — anonymous |
| Page path | Server stores path only | Yes — no query string, no user linkage |
| IP address | Not recorded by Lens | Arrives as normal HTTP traffic |
| Device fingerprint | Never collected | — |
| Cross-site tracking | Impossible — localStorage is same-origin | — |
GDPR-compatible for analytics use without a consent banner (no personal data collected). If you enable visual adaptation, check your jurisdiction's requirements for disclosures about personalised content.
Build the npm package
npm run build # produces dist/lens.umd.js + dist/lens.esm.js
npm publish --dry-run # verify what's included
npm publish --access publicDeploy to production
Standard Node.js/Express. Deploy to Railway, Render, Fly.io, or any VPS:
PORT=3000 node server/index.jsThen point data-api in your script tag to your deployed URL.
Roadmap
- [x] Section interest heatmap in dashboard
- [x] WordPress plugin
- [x] Multi-format demo (tech, news, magazine, tutorial)
- [x] npm package (
@zsoltcsaszti/lens) - [ ] Multi-page interest graph (track topics across your whole publication)
- [ ] A/B mode — adapted vs. non-adapted to 50/50 of visitors, measure engagement delta
- [ ] Weekly publisher digest — "what your readers actually cared about this week"
- [ ] Ghost / Webflow plugins
- [ ] Optional AI-generated per-reader summary (publisher provides API key)
