100 on PageSpeed: what we changed to get there

Load time is a ranking factor, but the usual advice rarely helps. This piece describes what we actually changed on our own site, how much each measure delivered, and where we took a wrong turn.

A glowing shape accelerates to the right while dark blocks dissolve behind it

In short

  • The single biggest gain did not come from minifying files but from replacing a 458 KB library with 6 KB of our own code.
  • The second largest cause was half-configured compression: the server compressed only HTML, not CSS and JavaScript.
  • A layout shift score of 1 cost 24 points — caused by late-loading CSS that only took effect after the first paint.
  • What helped least: image formats. What helped most: leaving things out.

The starting score was 58 out of 100 on mobile. That is not a bad score for a site with a 3D effect in the header, but it is one Google noticeably penalises in mobile search. We finished at 100 on desktop and 98 on mobile, with 100 for accessibility as well.

What follows is not a general checklist but the actual sequence — including the places where we got it wrong.

A glowing shape accelerates to the right while dark blocks dissolve behind it
A page does not get fast through optimising, but through leaving things out.

The starting point

Measurement (mobile)beforeafter
Performance5898
Accessibility95100
First contentful paint6.1 s1.4 s
Largest contentful paint11.1 s2.1 s
Total transferred298 KB184 KB
of which JavaScript121 KB5 KB

The five changes that actually counted

1. Replace the library, do not optimise it

The header shows a particle cloud in 3D. A well-known graphics library was running for that — 458 KB after minification. The measurement showed that 51 KB of it was never executed at all.

The decisive question was how much of it we actually needed. It came to eleven building blocks: renderer, scene, camera, geometry, material and a few helper classes. All of them can be written directly against the browser's own graphics interface.

Result: 458 KB became 6 KB. The rendering is identical — we kept the same shaders.

Careful Two details have to be reproduced or the result looks wrong: the library converts colours from hex format into linear light, and it blends additively with premultiplied alpha. Without both, our particle cloud came out noticeably harsher than before.

2. Compression was only half switched on

The server configuration had compression set to "on" — but the line specifying which file types to compress was commented out. That is the default in many distributions. The result: only HTML was being compressed.

So the graphics library went over the wire uncompressed. After switching it on: 1,243 KB became 251 KB. We now also place pre-compressed versions alongside the files, so the server does not recompute on every request.

Effort: two lines of configuration. Effect: nearly a megabyte per first visit.

3. Remove the animation library entirely

A second library, 114 KB, handled fade-ins on scroll. The measurement additionally flagged it as the cause of forced layout reflows — it queried geometry while scrolling and forced the browser to recalculate the layout mid-motion.

Replaced by an IntersectionObserver that merely sets a class. CSS does the actual movement. 114 KB less, no more reflows, around 40 lines of our own code.

4. The favicon was 205 KB

A small thing with a surprising effect, because it loads very early. Replaced with a 6 KB SVG generated from the existing logo.

Worth knowing

For display in Google search results, an SVG favicon is no use — Google accepts only raster formats there, and the image has to be square with an edge length that is a multiple of 48 pixels.

Provide only an SVG and you get the grey placeholder in the search result instead of your own logo. Specifying both alongside each other is the right approach.

5. Querying geometry inside the animation frame

One script read the document's total height on every scroll event. The browser cannot answer that property from memory — it has to recalculate the layout of the whole page, mid-scroll.

The rule that follows, and holds everywhere: read in the event, write in the frame afterwards. Never the other way round. Since then the page height is measured once and remembered, instead of being queried sixty times a second.

The mistake that cost 24 points

On the left a dense stack of grey blocks, towards the right only a few glowing ones remain
What remains decides the load time — not how well the remainder is compressed.
From practice

To get rid of the last render-blocking request, we had split the stylesheet: the part needed for the visible area went straight into the document, the rest was loaded afterwards. A common approach.

We cut at a marker in the source file, behind which we assumed the visible area ended. In fact, what sat behind it were the rules for the logo in the header, for the spacing below it, and for the fade-ins. So the page built without them and was shunted into place a second later.

The result: a Cumulative Layout Shift of 1.0 — and with it 76 instead of 100 points on desktop. Particularly unpleasant: locally, with a throttled connection, the value was 0, because the stylesheet arrived early enough there. The fault was only visible on a fast connection.

The lesson: either the whole stylesheet goes into the document or none of it. A partial inline assumes you know exactly which rule is needed in the visible area — and on a growing site you never know that for long.

Tip Measure without throttling when you suspect layout shifts. A test tool with a simulated mobile connection delivers the file early enough and does not show the shift. Measure only that way and you will believe the problem is solved.

What helped little

For completeness, the measures that appear in every guide and were barely measurable for us:

Image formats. Our home page has hardly any images — the logo is an SVG. Where images do appear, conversion is of course worthwhile; as a main lever it only works on image-heavy pages.

Shrinking fonts further. We self-host two font families and preload the ones needed in the visible area. Further optimisation would be possible but does not move the score.

Server location. Widely recommended, irrelevant in our case: time to first byte was already at 20 milliseconds. If you are already good there, a distribution network gains you nothing.

The approach, to follow yourself

  1. Measure before changing anything. Mobile and desktop separately, note the values.
  2. Find the largest transferred file. Almost always it is JavaScript. The question is not how to shrink it, but whether you need it.
  3. Check compression. Not whether it is on, but for which file types.
  4. Count render-blocking requests. Every stylesheet file in the head holds up the first paint.
  5. Check for layout shifts without throttling. The one test we were missing.
  6. Measure again after every change. Otherwise you will not know at the end what worked.
Prompt
Help me turn my PageSpeed report into an ordered plan.
Be strict; praise nothing.

My measurements:
- Mobile: [score], desktop: [score]
- LCP, TBT, CLS per device: [values]
- Largest transferred files with size: [list]
- Render-blocking requests: [list]
- Messages from the report: [paste text]

About the site:
- How it is built: [CMS / static / site builder]
- Is JavaScript needed for the visible content? [yes/no]
- How is CSS delivered? [external / inlined / partly inlined]

Tasks:
1. Assign each message to a cause and say which metric it affects
   – LCP, TBT or CLS.
2. Sort the measures by effect per effort. For each, state how
   many points it realistically moves and why.
3. Name the measures from the report that will bring practically
   nothing given how my site is built – and justify that rather
   than simply omitting them.
4. For every large JavaScript file, ask first whether it is
   needed before suggesting minification.
5. Point it out if CSS is only partly inlined, and explain why
   that can be worse for CLS than not inlining at all.
6. Tell me what to re-measure after each change.

Do not invent measurements.

In closing

The score itself is not the goal — it is a measurement, not a business result. What counts is the experience behind it: a page readable after 1.4 seconds gets read by more people than one that takes six. For mobile visitors on a cellular connection, that difference is larger than any text optimisation.

Getting there, in our case, ran through one recurring question: do we need this? Five of six changes consisted of removing something, not adding something.

Common questions

How do you reach 100 on PageSpeed Insights?

On most websites through three steps: remove or replace the largest JavaScript file, actually switch on compression for CSS and JavaScript, and eliminate layout shifts. Image formats and server location are rarely the bottleneck.

How important is load time for Google rankings?

It is a confirmed factor but not a strong one — content and relevance weigh more. The bigger effect is indirect: slow pages get abandoned more often, and that behaviour feeds into the assessment.

What is Cumulative Layout Shift and how do you fix it?

The measurement for elements that change position after the first paint. Most common causes: images without fixed dimensions, late-loading stylesheets, and fonts with markedly different metrics. It is fixed by reserving the space up front — through width and height attributes or a fixed aspect ratio.

Should you inline CSS into the HTML?

For small stylesheets yes, but then completely. A partial inline with the rest loaded afterwards saves one request and buys layout shifts as soon as a rule for the visible area is missing. Ours was 7 KB compressed — at that size the full inline is worth it.

How much do you gain by dropping libraries?

In our case it was the largest single item: 458 KB of graphics library became 6 KB of our own code, plus 114 KB of animation library down to zero. Whether it pays off depends on how much of the library you actually use — the measurement flags unused JavaScript.

Marketing that sets itself up

The Studio Engine beta is live. Claim your spot and help shape it from the start.

Join the beta →
← Back to overview