Skip to content

Repository files navigation

lcp-hunter

An in-page performance overlay that hunts down your Largest Contentful Paint element, render-blocking resources, and long tasks — on any page — and tells you what to do about each one.

lcp-hunter drops a small, draggable panel onto the page. It outlines the LCP element in place, lists the scripts and stylesheets blocking the first paint, counts main-thread long tasks, and pairs every finding with a one-line fix and a "Learn more →" deep-link. It ships two ways from one codebase:

  • a Manifest V3 browser extension (Chrome / Edge / Firefox-compatible) with a toolbar button that toggles the overlay on the active tab, and
  • a bookmarklet — a single javascript: URL — for when you don't want to install anything.

No build server, no account, no data leaves the page. It reads the same PerformanceObserver entries the browser already exposes.


What it shows

A compact panel, top-right, in three sections (rendered in a Shadow DOM so the page's own CSS can't touch it):

┌─────────────────────────────────────┐
│ lcp-hunter                    –  ×  │
├─────────────────────────────────────┤
│ LARGEST CONTENTFUL PAINT            │
│ Time      1.24 s          [ Good ]  │
│ Element   img#hero [hero.avif]      │
│ LCP is an image — set               │
│ fetchpriority="high", preload it…   │
│ Measuring LCP with DevTools →       │
│ Image CDNs & fetchpriority →        │
├─────────────────────────────────────┤
│ RENDER-BLOCKING RESOURCES           │
│ Found     2        [ 1 JS · 1 CSS ] │
│ JS   vendor.js                      │
│ CSS  app.css                        │
│ Add defer/async or split & …        │
│ Dynamic imports & code-splitting →  │
├─────────────────────────────────────┤
│ LONG TASKS (MAIN-THREAD)            │
│ Count     3      [ 240 ms blocking ]│
│ Longest   220 ms                    │
│ 220 ms  @ 1.10 s · self             │
│ Break up long tasks and yield …     │
│ Profiling event handlers for INP →  │
├─────────────────────────────────────┤
│ lcp-hunter · frontend-performance…  │
└─────────────────────────────────────┘

At the same time, a labelled purple box is drawn directly over the LCP element (LCP · 1.24 s), and it re-anchors as you scroll or resize the window.

  • LCP is rated Good / Needs work / Poor against the standard 2500 ms / 4000 ms thresholds. If the LCP element is an image you get an image-specific tip (fetchpriority, preloading, don't lazy-load it).
  • Render-blocking counts parser-blocking <script src> in <head> (no async / defer / type="module") and blocking <link rel="stylesheet"> (ignoring the media="print" swap trick and disabled sheets).
  • Long tasks are collected via the longtask observer. The badge shows a total-blocking-time estimate (Σ of each task's time over the 50 ms budget) — a good proxy for what will hurt your INP.

Drag the header to move it, to minimise, × to remove it.


Install & use

Not published to any store or registry. You load it unpacked, straight from source. That's the whole install.

Option A — the browser extension

git clone https://github.com/frontend-performance/lcp-hunter.git
cd lcp-hunter
npm install
npm run build      # bundles src/ → extension/overlay.js + extension/popup.js

Then load the extension/ folder unpacked:

Chrome / Edge / Brave

  1. Open chrome://extensions (or edge://extensions).
  2. Turn on Developer mode (top-right).
  3. Click Load unpacked and select the extension/ folder.
  4. Pin lcp-hunter, open any http(s) page, click the icon, hit Toggle overlay.

Firefox (uses the same MV3 manifest)

  1. Open about:debugging#/runtime/this-firefox.
  2. Click Load Temporary Add-on… and pick extension/manifest.json.
  3. Use the toolbar button as above. (Temporary add-ons are removed on restart.)

Clicking the button again toggles the overlay off — the popup re-injects the same bundle, which flips visibility via window.__lcpHunter.

The extension ships without icon PNGs to keep the repo binary-free, so the toolbar shows a default placeholder icon. That's expected; drop PNGs into extension/ and add an "icons" block to manifest.json if you want your own.

Option B — the bookmarklet (no install)

npm install
npm run build      # writes dist/bookmarklet.txt
  1. Open dist/bookmarklet.txt and copy its entire contents (it's one long javascript: line).
  2. Create a new bookmark (bookmark any page, then edit it).
  3. Replace the bookmark's URL with the copied text. Name it "LCP Hunter".
  4. Navigate to any page and click the bookmark. Click it again to toggle off.

The bookmarklet is the same overlay core, self-contained in the URL — no network request, nothing to trust but the code you can read in src/overlay.ts.

Some sites with a strict script-src Content-Security-Policy will block bookmarklets. On those pages, use the extension (it injects via the scripting API and isn't subject to the page CSP).


How it works

One small codebase, bundled by esbuild into a single IIFE:

  • src/analysis.ts — pure, DOM-free logic: rateLcp, isRenderBlockingScript, isRenderBlockingStylesheet, describeElement, summarizeLongTasks, formatMs. No window, no document. This is the part that's unit-tested in Node.
  • src/overlay.ts — the DOM shell. It wires up PerformanceObservers for largest-contentful-paint and longtask (both buffered: true), scans the document for blocking resources, and renders the panel + element outline into a Shadow DOM. It exposes window.__lcpHunter = { toggle, destroy }; running the bundle a second time toggles the overlay instead of stacking a new one.
  • build.mjs — esbuild bundles overlay.ts to a minified IIFE and writes:
    • dist/overlay.js and extension/overlay.js (injected by the extension),
    • dist/bookmarklet.txt = javascript: + URI-encoded overlay, and
    • extension/popup.js (from extension/popup.ts).
  • The extension (extension/manifest.json, popup.html, popup.ts) uses activeTab + scripting only. The popup calls chrome.scripting.executeScript({ files: ['overlay.js'] }) on the active tab. Repeated injections share the tab's isolated world, so the second click finds the existing window.__lcpHunter and toggles.

Because the two entry points (extension injection and bookmarklet) run the exact same bundle, there's a single source of truth for the overlay behaviour.

Project layout

lcp-hunter/
├── src/
│   ├── analysis.ts          # pure, DOM-free, unit-tested logic
│   └── overlay.ts           # the overlay (DOM shell around analysis.ts)
├── extension/
│   ├── manifest.json        # MV3: action popup, activeTab + scripting
│   ├── popup.html
│   ├── popup.ts             # bundled → popup.js
│   ├── popup.js             # (generated by npm run build)
│   └── overlay.js           # (generated by npm run build)
├── test/
│   └── analysis.test.ts     # Node built-in test runner (via tsx)
├── dist/                    # (generated) overlay.js + bookmarklet.txt
├── build.mjs                # esbuild build script
├── tsconfig.json            # type-checks src/
├── tsconfig.extension.json  # type-checks extension/popup.ts with @types/chrome
├── package.json
├── LICENSE
└── README.md

Scripts

Command What it does
npm run build Type-checks, then bundles the overlay, bookmarklet, and popup.
npm run typecheck tsc --noEmit over src/ and the extension popup (no emit).
npm test Runs the unit tests (node --test --import tsx test/*.test.ts).

Browser support notes

  • LCP and long-task observers require a Chromium-based browser or a recent Firefox. Where an entry type isn't supported, that section degrades gracefully ("waiting…" / "no long tasks yet") instead of erroring.
  • The overlay guards on typeof PerformanceObserver and wraps each observe() in a try/catch, so it never throws on an unsupported page.

Requirements

  • Node.js >= 20 to build (the overlay itself has no runtime dependencies).

Further reading

Deep-dives on frontend-performance.com behind each finding:

License

MIT © frontend-performance

About

Browser overlay (MV3 extension + bookmarklet) that highlights the LCP element, render-blocking resources, and long tasks on any page, with a fix tip and deep-link for each finding. Docs on frontend-performance.com.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages