Every studio says it builds fast sites. Almost none publish a number you can check, and the ones that do publish their best run.
This is our record. Five sites (ours and four award-winning studios) measured on one machine, in one session, with one configuration. Four consecutive runs each. The worst of the four is the number printed. No run discarded, no site given an easier profile than any other, including us.
| Site | Perf | A11y | Best Prac | SEO | LCP | CLS | TBT |
|---|---|---|---|---|---|---|---|
| aristidebenoist.com Awwwards, multiple | 100 | 91 | 100 | 100 | 1.06 s | 0.014 | 0 ms |
| saffrondesigns.io Our own site · re-measured 1 Sep | 98 | 100 | 100 | 100 | 2.34 s | 0.000 | 0 ms |
| unseen.co Awwwards-winning studio | 88 | 93 | 73 | 100 | 3.36 s | 0.000 | 42 ms |
| obys.agency Awwwards Studio of the Year 2023 | 60 | 94 | 100 | 92 | 6.71 s | 0.279 | 72 ms |
| basement.studio FWA / Awwwards | 51 | 100 | 100 | 100 | 7.51 s | 0.727 | 51 ms |
We are not top of our own table
Our site returns 98 on performance on this profile (up from 93 when this page first went up), and one of the studios above still beats it. Largest Contentful Paint is 2.34 seconds against their 1.06. That is the honest reading and we are not going to bury it three scrolls down.
Two things are true at once. The 95+ guarantee applies to work we deliver, and it holds: every build ships against Lighthouse 95+, WCAG 2.2 AA and zero accessibility violations, measured on the live URL before handover. Our own site is now at 98 on a four-times-throttled mobile CPU, which clears that bar too. The remaining gap is still LCP, and it is still ours.
Re-measured and republished here on 15 September 2026, whatever it says.
The layout shift was never the fonts
Two pages on this site were still moving under the reader after they had loaded. The obvious explanation was the display font swapping in late, and it is written down here because it was wrong. The trace says so plainly: the fonts finish loading between 183 and 365 milliseconds, and the page does not paint until somewhere between 274 and 1,304. The fonts are already there when the page appears. There is no swap.
What was actually happening: the navigation script measures the fixed header and then sets a top padding on the page after the first paint. On a phone that drops the entire document by about 85 pixels once it is already on screen. It also explains why the fault came and went: when the paint happened late, the script had already finished and nothing moved; when the paint happened early, the padding landed on top of it.
The fix is to reserve that space in the stylesheet, where it exists before anything is drawn. Eight runs on the treated page against eight on an untouched one, same batch:
| Page | Published (worst of eight) | Layout shift |
|---|---|---|
| Control untouched | 97 | 0.103 in three runs |
| Treatment space reserved | 99 | zero, all eight |
Seven of the eight treatment runs painted early (the exact condition that breaks the control), and not one of them moved.
A smaller shift survived on two pages and not on the others, and the difference between them was a single script: it swaps a second <header> for a <nav> almost a second after load, to stop two elements claiming the same landmark. But the page styles that element by its tag, so changing the tag threw away its padding and margin and everything underneath jumped. A role does the same accessibility job without rewriting anything. That page now returns 99 with zero shift, and its accessibility score never moved off 100.
Where it stands: the blog index went from 91 to 99, this page from 92 to 98, the four posts from 97 to 99, all with no measurable layout shift. Every page on this site now clears the 95+ standard it sells. One page, /rates, still shifts in about one run in eight; its content sits lower on the page so the same reservation would open a visible gap, and it is left alone rather than approximated.
Two wrong guesses in a row here were caught by the same habit: read the trace, not the symptom. The fonts had already loaded, so it was never a font problem. The page carried an inline style no stylesheet had written, so it was always a script problem.
Then the same fix, on nine more pages
Once the controlled test below settled it, the rest was rollout rather than investigation. Nine pages requested the byte-identical font string. Each was checked first: the self-hosted set does not carry Cormorant Garamond 600, and the Google request did fetch it, so the question was whether any page actually renders a heading at that weight. None does: every element at 600 or above resolves to Jost, at a weight Google was never serving these pages either. Headings are Cormorant 500. The heavy face was dead weight in the request.
| Page | Before (worst of four) | After (worst of four) |
|---|---|---|
| /work | 86 | 97 |
| /audit | 87 | 98 |
| /canon | 89 | 98 |
| /7‑day | – | 98 |
| /guarantee | – | 98 |
| /contact | – | 98 |
| /terms | – | 99 |
| /privacy | – | 99 |
The swing is gone as well as the floor: runs now cluster within a point or two instead of jumping eight. Six pages are left on the old arrangement and are staying there for now: two of them ask for a weight the self-hosted set does not carry, and four need typefaces we do not host. Converting those would change how they look, so they wait.
What this section is really recording: the pages had been shipping below our own 95+ guarantee for as long as they had existed, and nobody had noticed because nobody had ever run them four times. The measurement found it, not a redesign.
The blog was failing our own guarantee, and nobody had checked
On 2 September the blog pages were measured four times each for the first time. They published a worst-of-four of 90. The guarantee on this site is 95+. The pages that carry that promise were not meeting it, and had not been for as long as the blog has existed, not because anything regressed, but because nobody had ever run them four times.
The trace said something odd. Everything (document, stylesheets, script, both fonts) finished loading in about 360 milliseconds, and then the browser painted nothing for a further 930. Network was identical between fast and slow runs. Server response was 40 ms both ways. Main-thread work was identical. Total blocking time was zero. The one long task in the slow run started after first paint.
Two guesses had already been wrong on this site, so this one got a control. Fonts were self-hosted on one post; a second post was left untouched and measured in the same batch.
| Post | Run 1 | Run 2 | Run 3 | Run 4 | Published (worst) |
|---|---|---|---|---|---|
| Control Google Fonts, untouched | 91 | 99 | 99 | 99 | 91 |
| Treatment self-hosted, preloaded | 99 | 100 | 100 | 99 | 99 |
The hypothesis was half wrong, and the correction is the interesting part. The paint stall still happened on the treated post: two of its four runs stalled exactly as before. So the stall is an artefact of the measurement environment, not a font problem. What changed was what the stall cost. With the third-party font host out of the critical chain, a stalled run lands at 1.84 seconds instead of 2.76, because the simulation prices a third-party DNS, TLS and request hop at a 150 ms round trip and prices same-origin preloads at nothing.
Rolled out to all four posts and the index. Clean runs are about 80 milliseconds slower than before: seven same-origin preloads against one cached third-party stylesheet, which costs zero points and buys eight on the bad ones. That trade is the whole result.
The post published that morning was first recorded here at 99, on a batch of four. Eight runs later the worst is 97, and 97 is the number that stands: the rule on this page is the worst run, and a batch that happened to miss the bad case does not get to set the figure. It still clears the 95+ guarantee, which it had never done before that morning.
Correction, 2 September, same day: the sentence that stood here said the blog index was the page with a layout-shift problem and that the posts sat at 0.008. A verification pass with more runs shows that was wrong. Layout shift on every blog-family page is bimodal (a run returns either 0 or roughly 0.08–0.19, never in between), and the posts do it too. Across eight runs the post published that morning shifted 0.103 in three of them. Four runs is not enough to pin a number that behaves this way, and the figures below are the worst across every run recorded, not the worst of one lucky batch.
The second attempt worked. The first two guesses did not.
Three render-blocking stylesheets sat in front of first paint. Two were ordinary page styles and are now inlined; the third is the brand token file and was deliberately left alone, because tokens belong in an import. Two round trips removed, roughly six kilobytes.
Measured the same way as everything else here, four runs, worst published:
| saffrondesigns.io | Published (worst of four) | LCP | FCP |
|---|---|---|---|
| 31 Aug before | 93 | 2.79 s | 1.59 s |
| 1 Sep after | 98 | 2.34 s | 1.04 s |
Two other explanations were tested first and both were wrong. The largest element on the page fades in under JavaScript, so forcing it visible from the start looked like the obvious answer: it moved Largest Contentful Paint by one millisecond. There is also a full-screen loading overlay held for two and a half seconds after the page has finished loading; cutting that to six hundred milliseconds moved it by two. Neither was kept.
Three hypotheses for one improvement is the ordinary ratio. The point of this page is that the two failures are as easy to look up as the success.
The first attempt to fix it failed
Hours after this page went up we tried to close the gap. The homepage was preloading seven WOFF2 font files. Only three are used above the fold. The other four are roughly 143 KB competing for a throttled connection during the exact window that decides Largest Contentful Paint. Removing them from the preload list is a textbook LCP fix: the fonts still load, just without jumping the queue.
It shipped. Then it was measured, the same way as everything else on this page: four consecutive runs, worst published, no run discarded.
| Homepage state | Run 1 | Run 2 | Run 3 | Run 4 | Published (worst) | Steady LCP |
|---|---|---|---|---|---|---|
| 7 preloads before | 93 | 96 | 96 | 96 | 93 | 2.73 s |
| 3 preloads after the “fix” | 91 | 100 | 96 | 96 | 91 | 2.73 s |
One run hit 100 and it would have been very easy to publish that number. Under this page’s own rule the figure is the worst of the four, and the worst of the four got worse. Steady-state LCP did not move at all. The commit that made the change said, before the result was known, that it would be reverted rather than explained if the number did not move. It was reverted the same hour. The site is back to seven preloads and back to 93.
The diagnosis was wrong, and the measurement says where the real cost is: of the 2.79 seconds, 147 ms is server response and 1,171 ms is element render delay: time spent after the bytes have arrived. Fonts were never the bottleneck. That is the next thing to attack, and it will be measured this way before it is kept.
This section exists because the failure is the point. Anyone can publish a table of wins. A ledger that never records a reversal is a marketing page wearing a lab coat.
Check any row yourself
Nothing here needs to be taken on trust. Every figure comes from a tool Google ships free:
npx lighthouse https://saffrondesigns.io/ --view
Or open Chrome DevTools on any site above, choose the Lighthouse panel, select Mobile, and press Analyze. Run it four times the way we did and take the worst.
Your numbers will not match ours exactly, and that is the point. Network conditions, hardware and the sites themselves all change: two of the figures in our own older comparison file were a month out of date and materially wrong by the time we re-ran them. A performance claim without a date and a method is not a claim. That is why this page carries both, and why the raw run data is published beside it rather than summarised.
What this page does not claim
It does not say we design better than the studios listed. Several of them make work we genuinely admire, and Lighthouse has nothing to say about taste, craft or art direction. It measures how a page is built and delivered: whether it loads, whether it shifts under your thumb, whether it works on a mid-range phone on a bad connection.
That is the entire argument. An award tells you how something looks. It says nothing about whether it works, and until now nobody was publishing the second number next to the first.
The rule
Every row on this page was measured the same way on the same day, and the worst run is the one shown. When we ship new work, it gets a row. When we re-measure, the old figures stay in the file with their dates on them.
A ledger only means something if you cannot edit the past. That is why this one is dated, published as data, and includes the row we would rather not show you.
Want your own site measured the same way? The X‑Ray runs the identical audit against any URL and shows you every failing check, free.
Run the X‑Ray on your site Download the raw data Book a call