|
| 1 | +--- |
| 2 | +layout: post |
| 3 | +title: "Web-Perf Wednesday 011 – SpeedCurve’s INP Observer Missed Its Threshold" |
| 4 | +date: 2026-09-30 12:00:00 +0100 |
| 5 | +categories: |
| 6 | + - Web Performance |
| 7 | +tags: |
| 8 | + - RUM |
| 9 | + - INP |
| 10 | + - Tooling |
| 11 | +show_taxonomy: true |
| 12 | +main: "" |
| 13 | +meta: "A long-standing lux.js bug left Event Timing on its 104 ms default, while browser releases improve testing and rendering performance." |
| 14 | +--- |
| 15 | + |
| 16 | +This week has been quieter than [last |
| 17 | +Wednesday](/2026/09/web-perf-wednesday-010-safari-keeps-scrolled-content-in-place/), |
| 18 | +but the lead is exactly the sort of measurement bug worth stopping for. I found |
| 19 | +that SpeedCurve’s public lux.js code wasn’t passing all of its |
| 20 | +`PerformanceObserver` options to the browser correctly, which left Event Timing |
| 21 | +using its default 104 ms threshold. The fix was merged quickly and now appears |
| 22 | +in an open-source release. Safari Technology Preview and Firefox also shipped |
| 23 | +smaller changes worth testing. Together, they’re a reminder to test the |
| 24 | +measurement system as carefully as the site. |
| 25 | + |
| 26 | +## SpeedCurve’s INP Observer Missed Its Threshold |
| 27 | + |
| 28 | +While investigating an odd distribution in SpeedCurve RUM data, I found and |
| 29 | +[reported a bug in |
| 30 | +lux.js](https://github.com/SpeedCurve-Metrics/lux.js/issues/85). The library has |
| 31 | +a small wrapper around `PerformanceObserver` that accepts an entry type, a |
| 32 | +callback, and any extra observer options. The wrapper was putting those extra |
| 33 | +options inside a nested `options` property instead of passing them at the top |
| 34 | +level of the object sent to the browser. |
| 35 | + |
| 36 | +That sounds like a minor object-shape error, but it changes what the browser |
| 37 | +records. lux.js asks for Event Timing entries with `durationThreshold: 0`; the |
| 38 | +browser never received that property where it expected it, so it fell back to |
| 39 | +the [`PerformanceObserver.observe()` default of 104 |
| 40 | +ms](https://developer.mozilla.org/en-US/docs/Web/API/PerformanceObserver/observe). |
| 41 | +Interactions below that threshold weren’t delivered through the ordinary |
| 42 | +Event Timing observer, which could change the INP value and attribution that |
| 43 | +SpeedCurve calculated for a page view. |
| 44 | + |
| 45 | +The faulty option forwarding had been there for some time. The wrapper began |
| 46 | +with the correct spread syntax in March 2023, then [an unrelated change in May |
| 47 | +2023](https://github.com/SpeedCurve-Metrics/lux.js/commit/b5adb3e966cf699df5dc2a92c2422cc54be5dcfc) |
| 48 | +introduced the nested object. lux.js began explicitly requesting a zero |
| 49 | +threshold for INP in May 2024, but the wrapper still prevented that value from |
| 50 | +reaching the browser. By the time I reported it, the bad forwarding code had |
| 51 | +been present for more than three years. |
| 52 | + |
| 53 | +SpeedCurve’s Joseph Wynn opened a patch a little over an hour after the report, |
| 54 | +and [the fix was merged on 24 |
| 55 | +September](https://github.com/SpeedCurve-Metrics/lux.js/pull/86). It removes the |
| 56 | +extra object wrapper so that `durationThreshold`, and any future |
| 57 | +`PerformanceObserverInit` option, is passed through at the correct level. The |
| 58 | +patch also adds a browser integration test that checks interactions below 104 |
| 59 | +ms, which is the right place to catch a Web API wiring problem like this. |
| 60 | + |
| 61 | +For SpeedCurve users, the immediate task is to preserve context. Record the |
| 62 | +lux.js version alongside the relevant RUM period, annotate the eventual rollout, |
| 63 | +and compare the shape and attribution of INP before and after it. A change in |
| 64 | +the distribution may come from the observer seeing interactions that were |
| 65 | +previously absent rather than from an application deployment. A careful [RUM |
| 66 | +measurement review](/consultancy/) should establish what the collector could |
| 67 | +see before anyone uses the graph to judge a release, set a target, or explain a |
| 68 | +commercial result. |
| 69 | + |
| 70 | +## lux.js 4.5.3 Contains the Fix |
| 71 | + |
| 72 | +[lux.js 4.5.3](https://github.com/SpeedCurve-Metrics/lux.js/releases/tag/v4.5.3) |
| 73 | +was published early on 30 September, and its tag contains the merged fix. The |
| 74 | +public release note describes an INP correction that mostly affected soft |
| 75 | +navigations. That establishes the fix in a published open-source version rather |
| 76 | +than only on the repository’s main branch. |
| 77 | + |
| 78 | +That still doesn’t prove which version SpeedCurve is serving to customer sites. |
| 79 | +Its [public product |
| 80 | +changelog](https://support.speedcurve.com/changelog) hadn’t announced a 4.5.3 |
| 81 | +rollout by the 09:00 cutoff, and a repository release isn’t evidence of a hosted |
| 82 | +deployment. The merged fix may therefore not yet be in the customer SDK. If |
| 83 | +you’re planning a before-and-after comparison, confirm the deployed version |
| 84 | +first and keep the rollout time with the data. |
| 85 | + |
| 86 | +## Safari Adds Network Throttling and Performance Fixes |
| 87 | + |
| 88 | +[Safari Technology Preview |
| 89 | +253](https://webkit.org/blog/18357/release-notes-for-safari-technology-preview-253/) |
| 90 | +adds network throttling to Web Inspector and fixes several browser-side |
| 91 | +performance problems. WebKit lists excessive CPU and power use from pages with |
| 92 | +many `IntersectionObserver` targets, multi-second main-thread blocking around |
| 93 | +shared `scroll-timeline` names, redundant parsing of very long URLs, and |
| 94 | +unnecessary full-layer repaints among the resolved issues. |
| 95 | + |
| 96 | +These fixes are in Technology Preview, so they don’t describe current stable |
| 97 | +Safari for every user. They do give teams useful test cases: repeat an expensive |
| 98 | +Safari journey in 253, use the new network controls to make the conditions |
| 99 | +reproducible, and check whether the application or the browser owns the work. A |
| 100 | +[journey-level performance test](/performance-audits/) is far more useful when |
| 101 | +it records both sides of that comparison. |
| 102 | + |
| 103 | +## Firefox Cuts WebGPU Attachment Overhead |
| 104 | + |
| 105 | +[Firefox 157](https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Releases/157) |
| 106 | +shipped on 29 September with support for WebGPU’s `TRANSIENT_ATTACHMENT` texture |
| 107 | +usage. It allows render-pass attachments to remain in tile memory, avoiding |
| 108 | +VRAM traffic and potentially avoiding a VRAM allocation for textures whose |
| 109 | +contents aren’t needed afterwards. |
| 110 | + |
| 111 | +This is a specialised improvement, but it can be useful for graphics-heavy |
| 112 | +applications on tile-based GPUs. Teams using WebGPU can now test the transient |
| 113 | +path in stable Firefox, compare memory and frame behaviour on representative |
| 114 | +hardware, and keep the existing path until those results justify a change. The |
| 115 | +release note says the flag _can_ avoid traffic and allocation, so measure the |
| 116 | +effect on the actual workload rather than treating it as a guaranteed saving. |
| 117 | + |
| 118 | +## Need Help Checking Your SpeedCurve Data? |
| 119 | + |
| 120 | +Once the fixed collector goes live for customers, I’d be very happy to provide |
| 121 | +a second pair of eyes on an existing SpeedCurve instance. You might want to |
| 122 | +check whether an INP distribution moved, whether attribution now points |
| 123 | +somewhere different, or simply whether the data still supports the conclusion |
| 124 | +the team had already reached. |
| 125 | + |
| 126 | +This invitation is open to any SpeedCurve user, whether we already work |
| 127 | +together or you’re completely new to me. You don’t need a finished diagnosis; |
| 128 | +a dashboard, an odd pattern, or a loose question is plenty to begin with. If |
| 129 | +you’d like me to take a look once the fix reaches the customer SDK, [get in |
| 130 | +touch](/contact/). |
| 131 | + |
| 132 | +{% include web-perf-wednesdays.md %} |
0 commit comments