Core Web Vital
Largest Contentful Paint (LCP)
How long until the main content shows.
2.43 sGood
p75 of 37 samples · good ≤ 2.50 s, poor > 4.00 s
- Mobile p75
- 2.61 s
- Desktop p75
- 13 of 20
Fast is a number. This page shows the Core Web Vitals of this site from real visits, lab numbers for my projects, and the fixes the numbers led to.
Real visits
Each page load in production reports its metrics once, when the tab is hidden. The number is the 75th percentile (p75), as Google uses: three of four visits are this fast or faster. Colors use Google’s thresholds.
37 page loads in the last 28 days.
Core Web Vital
How long until the main content shows.
2.43 sGood
p75 of 37 samples · good ≤ 2.50 s, poor > 4.00 s
Core Web Vital
How fast the page responds to a tap, click or key.
Collecting: 8 of 20 page loads. With fewer samples, one slow visit moves the number too much to show it.
Core Web Vital
How much the layout moves while the page loads.
0.00Good
p75 of 37 samples · good ≤ 0.10, poor > 0.25
Loading
How long until the first text or image shows.
1.41 sGood
p75 of 37 samples · good ≤ 1.80 s, poor > 3.00 s
Loading
How long until the server starts to answer.
740 msGood
p75 of 37 samples · good ≤ 800 ms, poor > 1.80 s
What the numbers changed
Lighthouse showed a long delay between the first paint and the largest paint. The cause: the entrance animations ran in JavaScript. The server sent each page at opacity 0, so the heading stayed hidden until the scripts loaded and hydrated.
The fix moves the entrances to CSS. The server now sends the page visible, and the animation starts with the first paint. Content below the fold still reveals on scroll: the browser hides it before its first paint, while it is off screen. I also self-host the mono font, so no stylesheet from another origin blocks the first paint.
| Page | LCP before | LCP after | Change | FCP before → after |
|---|---|---|---|---|
| / | 10.36 s | 5.52 s | −47% | 4.62 s → 4.70 s |
| /chat | 7.97 s | 5.29 s | −34% | 4.50 s → 4.57 s |
| /work | 6.86 s | 4.10 s | −40% | 3.63 s → 3.62 s |
| /skills | 5.43 s | 3.98 s | −27% | 3.48 s → 3.50 s |
Measured 2026-10-28. Both production builds ran in the local Workers runtime, which does not compress files, so all numbers are slower than live. Headless Chromium with real throttling: 150 ms latency, 1.6 Mbps, 4× slower CPU, a 412 × 823 phone viewport, no cache. LCP and FCP come from the browser’s PerformanceObserver. Median of three loads per page.
What the numbers changed
I measured Averno, my 3D browser game, and two problems stood out. The 3D models were the largest part of the download, because they were stored uncompressed and Cloudflare does not compress glTF files. And every file had a fixed name, so the browser had to ask the server about each one again on every visit.
The models now use meshopt compression. Positions stay exact; normals move by 0.3° on average and 1.1° at most. When a model loads, the game unpacks it back into the format that its code expects. Screenshots of the same scene before and after differ in fewer than 0.03% of pixels. Each model and texture now has a content hash in its name, so the browser keeps it for a year and asks for nothing on a repeat visit. The cost: 16 KB more JavaScript for the decoder.
| What | Before | After | Change |
|---|---|---|---|
| Models | 1,405 KB | 507 KB | −64% |
| JavaScript | 393 KB | 409 KB | +4% |
| First visit, desktop | 2,762 KB | 1,877 KB | −32% |
| First visit, phone | 2,318 KB | 1,429 KB | −38% |
| Repeat visit, requests to the server | 40 | 2 | — |
Measured 2026-10-27, on the live game, before and after the deploy. Headless Chromium, empty cache, bytes over the network, three runs per profile. The phone is an emulated touch device, which starts on medium quality. For the repeat visit, the old version ran in the local Workers runtime, which sends the same cache headers as Cloudflare. Frame rate is not measured: the test machine draws WebGL in software.
Lab
The same test for this site and two of my projects: Lighthouse 12.8.2, mobile preset, median of 3 runs, on 2026-10-28. For this site, “before” rows ran before the fix shipped and “after” rows ran after it, on the same day.
| Site | Score | LCP | FCP | CLS | Blocking time |
|---|---|---|---|---|---|
| aliabbas.net — home, before The test machine has no GPU, so the WebGL globe draws in software and blocks the main thread. That blocking time describes the test machine, not a phone, and it also slows the simulated LCP. | 30 | 6.45 s | 4.06 s | 0.00 | Not shown |
| aliabbas.net — home, after The same note about the globe applies. | 31 | 5.88 s | 3.73 s | 0.00 | Not shown |
| aliabbas.net — chat, before | 72 | 4.42 s | 4.35 s | 0.00 | 0 ms |
| aliabbas.net — chat, after | 85 | 3.49 s | 3.13 s | 0.00 | 44 ms |
| aliabbas.net — work, after | 87 | 3.24 s | 2.94 s | 0.02 | 12 ms |
| aliabbas.net — skills, after | 86 | 3.25 s | 3.10 s | 0.00 | 16 ms |
| Pico | 94 | 2.90 s | 1.75 s | 0.00 | 0 ms |
| Whimcall | 87 | 3.23 s | 2.63 s | 0.00 | 0 ms |
Method