The night my post pages scored 39

I chose a heavy three.js world for this site and accepted that weak devices would suffer. Then I measured a post page on a phone: 39 out of 100, three and a half seconds of blocked time. Here is what I kept, what I cut, and the test I now run before I sacrifice anything.

Also available in 中文

A phone with its screen switched off lying on pale paper in soft daylight, and beside it a single loop of crimson thread forming an open ring.
The ring is the world behind these pages. On a phone, a post page now sets it down.

A decision I made on purpose

When this site was rebuilt in September I made a choice most advice says not to make. The background is a live three.js scene: a single brush stroke, drawn with tens of thousands of points, that takes three forms, a stroke, a circuit, a galaxy. It is the mark of the site in motion. It is heavy, and I knew it.

So I wrote the decision down, in the handbook my planning session reads first every day: heavy three.js is accepted; weak devices are sacrificed by choice. Content must still be real HTML for search. I did not want to be talked out of it one small compromise at a time.

I still think that was the right way to decide. Write it down, and you can find out later exactly what you were wrong about.

Measure first, change nothing

I run this site with two Claude sessions, a Brain that plans and a Hand that builds. On 30 September, after an SEO audit, the Brain's prompt to the Hand had one step that was different from the others: measure the world's cost on a post page, and change nothing.

The Hand ran Lighthouse in its mobile simulation against a local copy of the mamak post, once with the world and once without, same page, same build.

Post page, phone With the world Without
Score 39 97
Largest Contentful Paint 5.70 s 2.21 s
Total Blocking Time 3636 ms 0 ms
Page weight 733 KB 191 KB

Thirty-nine. Three and a half seconds in which a phone could not respond to a touch, on a page whose only job is to be read.

The home page told a different story: 95 with the world, 97 without. The same scene cost two points there and fifty-eight points on a post. The difference is not the code. It is what the page is for.

Sacrifice by page, not by site

The lazy fix is a switch: world on or world off. The honest one is to ask each page what it is for.

The home page is the mark. The world is the point of it, the thing a visitor came to see, and on a phone it costs two points. It stays.

A post page is for reading. Nobody opens a long essay on a phone to watch particles behind it. There, the world is pure cost, and I had been charging every phone reader 3.6 seconds for a decoration they could barely see behind the text.

So the rule became: a post page draws the world only when all three of these are true. The screen is at least 1024 px wide. The pointer is fine, a mouse or trackpad, which is the cheapest honest signal that this is a laptop and not a phone in landscape. And the reader has not set their system to prefer reduced motion. Fail any one, and the page never downloads the world at all. It gets a still ground instead: the same near-black, one soft crimson glow near the top, not animated.

The Hand built it, with tests that pin the three conditions and all eight combinations of them. Measured again:

Post page Score LCP TBT Weight World loaded
Phone 97 2.18 s 0 ms 191 KB no
Desktop 98 0.98 s 81 ms 734 KB yes

Same page, same essay. The desktop reader still gets the world. The phone reader gets the essay.

The world came back for the text

A day later I was reading the bottom of a post on my own laptop and could barely make out the author box. The ring was crossing the text column. The dark pool behind the article was a soft radial gradient, fine in the middle of a short page, gone by the bottom of a long one.

Measured at the worst spot, where the ring crossed an excerpt, the text had 1.59:1 contrast against the pixels behind it. The minimum for body text is 4.5:1.

The fix is a veil: a dark band at 0.8 opacity behind the whole text column, full height, feathered at the sides so there is no box, and only on desktop where the world runs. The same spot now measures 7.33:1. The ring is still there, at the sides and faintly through the band. The words come first.

Two days, two corrections to one decision, and I would make the decision again. The world is still the identity of this site. It just stopped charging people who came to read.

The page's-job test

Before I let any page pay for anything, four questions.

  1. What is this page for? One verb. Watch, read, decide, buy. If the expensive thing is not the verb, it is a decoration, and decorations are the first thing to go on a slow device.
  2. Measure on the phone, not the laptop. My laptop said the world was fine. It was fine, on a laptop. Lighthouse's mobile simulation was the first thing that told me the truth, and it took one prompt.
  3. Sacrifice by page, not by site. Global switches punish the page where the expensive thing was the point. Decide per page, and write down the conditions so they can be tested.
  4. Keep the identity in a still form. Removing the world from phones did not mean removing the brand. The still ground is the same colour and the same glow. Nobody who arrives on a phone sees a different site, only a quieter one.

And write the original decision down before you measure it. A decision you cannot find is one you cannot correct.

FAQ

What is Lighthouse?

Google's open-source page audit, built into Chrome. It loads a page under a simulated slow phone and network and scores it from 0 to 100 on performance, from metrics such as Largest Contentful Paint, Total Blocking Time and Cumulative Layout Shift. The numbers in this post are its mobile simulation on a local preview, so they compare like with like.

Why not just remove three.js from the site?

Because on the home page it is the point, and there it costs two points on a phone. The cost was only unacceptable on pages whose job is reading. So the decision is per page: the home page always draws the world; a post page draws it only on a wide screen with a fine pointer and no reduced-motion preference.

How does a page know it is on a phone?

It does not, exactly. It asks three media queries: is the viewport at least 1024 px wide, is the pointer fine, and has the reader not asked for reduced motion. All three must be true before the world is downloaded. A phone in landscape fails the pointer test; a small laptop window fails the width test and gets the still ground, which is acceptable.

Did the change hurt the desktop experience?

No. The same post page scores 98 on desktop with the world loaded. What the desktop needed was a reading veil behind the text column, added the next day, which took the worst contrast on the page from 1.59:1 to 7.33:1.