My day job is performance and reliability engineering, so I spend a lot of time looking at slow pages. The tools I reach for are good at telling me a page is slow and bad at telling me what to change. Perflex is my attempt at the missing half. It watches a page while you use it, works out which script and which function held the main thread, and writes the fix down next to the problem. It runs in the browser, and nothing is uploaded.
Fig. 00The same session in two tabs: the score with Core Web Vitals on the left, and the ranked list of things to fix on the right.
Lighthouse hands me a score from a lab run on a machine that isn't mine. The Performance panel in DevTools hands me a flame chart, and with an hour to spare I can usually find the answer inside it. RUM tells me the p75 moved last Tuesday. All three are worth having. None of them has ever told me the name of the function I need to open.
Chrome's Long Animation Frames API reports which script blocked a frame and roughly where. Perflex adds those up per function, so a long task stops being an anonymous block of scripting and starts having a name and a line.
Everything it finds comes with the before and after code, how risky the change is, and what to check once you have made it. I got tired of advice that stops at "consider deferring this".
Collecting the events, working out the numbers and checking them all happen inside the tab. There is no account and no telemetry, and nothing is uploaded, because there is no server on my end to upload it to.
Open the side panel and use your site the way somebody actually would. Perflex builds the picture behind you and sorts it by what is costing you the most.
A score out of 100 with a letter grade, your Core Web Vitals as they happen, and an estimate of what Lighthouse would give you, worked out locally without running Lighthouse.
Fig. 01A news site I profiled: 15.89 seconds of blocking time across 537 requests. It reckons the third-party scripts on their own are worth 18 points.
Every script on the page sorted by how long it held the main thread, along with its long tasks, transfer size, share of layout shift and memory growth. Open a row and you get the functions inside it that cost the most, with their source positions.
Fig. 02107 scripts on one page, and 12.57 seconds at the top of the list. The coloured bar down the left marks first-party against third-party.
Perflex knows 37 ways a page goes slow, across loading, running, rendering, network, third parties and frameworks. Layout thrashing, scripts that block rendering, synchronous XHR, the same fetch twice, payloads sent uncompressed, lists rendered without a limit, timers firing far more often than anyone intended. It checks your session against all of them.
Fig. 03Nineteen problems, worst first. Layout thrashing with 144 forced reflows is the sort of thing a score out of 100 will never name.
A timeline drawn with D3 that you can zoom into and drag around, with a lane each for interactions, long tasks, network, layout shifts, dropped frames and the heap.
Click an interaction and it walks back through what caused it: the click that started the long task that fired the fetch that changed the DOM that forced a reflow that moved the layout. Breaking that chain anywhere is usually cheaper than making any one link faster.
Fig. 04Six lanes against the same clock. The red marks in Frame Drops sit directly under the long-task clusters, which is usually the first thing worth noticing.
A waterfall of its own, drawn with virtual scrolling so it stays usable past 500 requests. Each bar is split into DNS, TCP, TLS, waiting and download, so you can see where in a request the time went rather than only how long the whole thing took.
Fig. 05612 requests on one page load. The colours are the point: a slow request and a request that waited a long time to start need completely different fixes.
A chat tab that can see everything Perflex captured on the page. You can ask why the page is slow, or what to fix first, and get an answer that refers to your actual numbers instead of general advice.
It needs an API key, either Claude or Google Gemini, whose free tier is enough. It does nothing until you add one. What gets sent is a summary with the identifying parts taken out: file names, function names and numbers. URLs are cut back to the site and the path, and page content and request bodies never go anywhere.
Fig. 06The empty state says what leaves your machine before you have typed anything, which felt like the right place to put it.
A session is not much use if it dies in your tab. Perflex writes out JSON, HAR with its own extra fields, OpenTelemetry traces you can open in Jaeger or Tempo, and a PDF with the gauges, the vitals, a small waterfall and the code.
Sharing works the same way as the rest of it. A permalink compresses the whole session into the part of the URL after the hash, which browsers never send to a server, and the person you send it to decodes it locally. Or you can download one HTML file with the session inside it, which opens offline with no extension and no server, and is the one to attach to a ticket.
Fig. 07The experimental collectors are off when you install it, because each one costs a little main thread and most sessions do not need them.
Six pieces, arranged so the expensive work never runs on the page's main thread. The collector sits in the page itself with a buffer that never grows, and hashes stack traces rather than keeping them as strings, because building strings in a hot loop is one of the things it is there to catch. Everything after that runs in a worker.
Sits in the page and records long tasks, network, timers, forced reflows, layout shifts and memory into a buffer with a fixed size. Stacks are hashed with FNV-1a instead of being kept as strings.
A content script passes events over chrome.runtime to a per-tab buffer in the service worker. webNavigation tells it when you have gone to a new page, so the collector goes back in and the session starts clean.
Turns the raw stream of events into per-script and per-function numbers, Core Web Vitals and interactions. This is the step that turns "a long task happened" into "this function, in this file, cost this much".
Runs the 37 checks and works out the Lighthouse estimate. Both sit in a worker, so the analysis never shows up in the thing being analysed.
A written fix for every pattern, with before and after code, a risk level and what to check afterwards, plus the optional Claude or Gemini path for questions about your own session.
The React panel, the overlay you can drop onto any page with Ctrl+Shift+X, and the six ways of getting a session out.
A tool that costs performance to measure performance is not much use, so Perflex keeps an eye on its own overhead against the frame budget and throttles collection if it goes past 2%. You can watch that happen in the overlay, which shows the frame rate, the heap, long tasks and whether it is currently throttling, on any page you like.
Perflex is not trying to take over production monitoring, and it would be bad at it. It is for the part of the job where you already know the page is slow and need to work out which line to change.
| Perflex | Lighthouse | DevTools Perf | Sentry / Datadog RUM | |
|---|---|---|---|---|
| Runs on the real page, live | Yes | Lab run | Yes | Yes |
| Names the function | Yes | No | Manual | No |
| Checks for known problems | 37 | Subset | No | Subset |
| Gives you the code to change | Yes | Generic | No | No |
| Answers questions about your session | Yes | No | No | Paid |
| Shows what caused what | Yes | No | Manual | Partial |
| Free & open source | MIT | Yes | Yes | No |
| Runs entirely on your machine | Yes | Yes | Yes | No |
| Exports HAR, OTLP and PDF | All three | JSON | Partial | Yes |
Knowing the LCP is 9.59 seconds tells you something is wrong. It does not tell you which of the 107 scripts on the page held the thread for twelve and a half seconds, and until you know that, every fix is a guess that costs a deploy to test.
On a lot of sites the scripts eating the main thread are ones nobody on the team wrote. You cannot fix code you do not own, but you can measure exactly what it costs and take that number to whoever signed off on the tag.
Performance advice that might break a checkout does not get merged. That is why every fix comes with a risk level, the steps to check it afterwards, and a note on why it leaves the behaviour alone.
URLs carry tokens and request bodies carry personal data. That is why nothing leaves the browser on its own, why permalinks live in the part of the URL that is never sent, and why the AI features do nothing until you turn them on.
The Performance panel gives you the raw picture and leaves the joining up to you. If you know what you are looking for it is excellent, and I still open it most weeks. Perflex does the joining up: it puts long tasks, network, layout shifts and memory on one timeline, works out the cost per script and per function, checks the result against 37 known problems, and gives you a sorted list with a fix attached to each one.
Chrome's Long Animation Frames API reports scripts that blocked a frame, along with where they came from. Perflex takes those entries, hashes the stacks with FNV-1a, and adds them up per script and per function inside the correlator worker. That is what turns a long task into a named function at a known position.
A bit, unavoidably, so Perflex measures how much. It watches its own cost against the frame budget and throttles itself if it passes 2%. The heavy work, meaning the correlation, the checks and the Lighthouse estimate, runs in workers rather than on the main thread, so it does not end up in the numbers it is producing.
The experimental collectors are off by default for the same reason.
By default, nothing at all. No account, no telemetry, no server. Sessions live in the tab and are cleared when you navigate away.
The exception is the AI features, which do nothing until you add your own Claude or Gemini key. When you use them, what goes out is a summary with the identifying parts taken out: file names, function names and numbers. URLs are cut back to site.com/path. Request bodies and page content are never included. Permalinks are local too, since the session goes in the part of the URL after the hash, which browsers do not send to servers.
Chrome, Edge, Brave, Arc and the other Chromium browsers. It is a Manifest V3 extension. On Arc the dashboard opens in its own window, because Arc does not support the side panel API yet.
No. Everything that matters works without one, and without any network access at all: the attribution, the script list, the timeline, all 37 checks, the written fixes with their code, the Lighthouse estimate, the waterfall and every export.
A key only adds the chat tab and the per-problem AI answers. Gemini's free tier covers it if you would rather not pay for one.
Two ways, both offline. Copy permalink puts the whole session into the part of the URL after the hash and points it at a static viewer, so the person you send it to decodes it in their own browser and nothing is uploaded on the way. Download shareable HTML gives you one file that opens in any browser with no extension and no server, which is the one to attach to a ticket.
There is also a PDF with the gauges, the vitals, a small waterfall, the worst interaction and the code, for when somebody wants a document.
Manifest V3 · TypeScript (strict) · React 18 · Vite + CRXJS · Zustand · Web Workers · D3 · @tanstack/react-virtual · Tailwind CSS · Vitest · Claude API · Google Gemini API