Journal16 August 2026Vol. 32

The paper

Web optimization

Vanilla is true. 1.8 MB, GPU instancing, TBT zero, Lighthouse 98 — those were not. Here is the file on disk.

The paper stayed vanilla: static HTML, one magazine stylesheet, Three.js 0.169.0 on an import map. No React, no bundler. That part of the earlier note is true. Almost every number under it was not.

The raw KiCad export is still a monster. What ships is a Draco file that scripts/optimize-pcb.mjs wrote on 15 August, from a KiCad 10.0.4 source tagged alu2.glb. This page is the file on disk, not the story I wanted.

Before (raw KiCad in the tab) After (what alu.glb is)
GLB~31 MB1,255,028 bytes (1.26 MB)
Meshes~41k pad/trace primitives9 meshes, 13 primitives, 13 materials
CompressionnoneKHR_draco_mesh_compression
GPU instancingnono — join() by material
Copper vs maskpalette() welded both opaqueseparate copper, pad, silk, mask, FR4
Client mergewalked every mesh on the main threadstill in pcb-look.js; skips if meshes < 32
Stackimport map, demand-rendersame · pixel ratio cap 1.5

That is the table. Not 31 → 1.8. Not “ripped the merger out.” Not “native GPU instancing.”

The GLB, without the fable

optimize-pcb.mjs does dedup → join(keepMeshes) → flatten → join → weld → prune → sparse → draco. It does not call palette() or simplify(). Palette was the previous pipeline’s bug: one atlas, then join(), copper gone under an opaque mask. Draco plus per-layer materials is how the file got small and still shows traces.

loadFittedPcb() in pcb-look.js is the one load path: Draco decode, dress materials, seat the board on y = 0. The bench and the full-screen viewer both call it. The merger is a guard for a raw 41k export, not a step we run on the file in the repo.

Look on the board: soldermask #1a9a48 at opacity 0.86, clearcoat 1, FR4 #0c3320, copper #c4a020. Hemisphere key is 0.6, not 2.5. Mask opacity is not 0.48 — that would have been a different (worse) board.

Lighthouse, without the 98

web/tests/lighthouse_desktop.json is a desktop run of index.html on localhost at 16 August 03:25Z. Chrome extensions were on. Scores from that file:

CategoryScore
Performance43
Accessibility100
Best practices96
SEO100

Same run: FCP 0.9 s, Speed Index 1.2 s, CLS 0.046, TBT 3,130 ms, LCP 10.8 s, TTI 10.8 s, max potential FID 3,530 ms. The 3.5-second freeze in the first draft of this log is that neighborhood — on a dirty profile, with the heavy spin still in the hero. It is not “TBT 0 ms, Performance 98.”

A later pass, after a 960×960 still poster, lazy-loading the bench, and print-media fonts, read about 97 / 100 / 92 / 100 on the front and 100 / 98 / 96 / 100 on the viewer. That pass is not the JSON in tests/. Re-run incognito if you need a number you can cite.

The front hero is now the hand photo (pcb-arrive.webp, 1120×1120, 280 KB). The KiCad orbit is still the designed board: source GIF 223 frames / 9.6 MB; a 45-frame 438 KB WebP is what made it horrid to watch; the encoded spin is 223 frames / 5.0 MB. Poster still 45 KB.

What the viewer actually does

Full-screen viewer.html is still OrbitControls, 1:1 drag, a closed Catmull-Rom story rail, soldermask peel, demand rAF. Pointer down on the canvas aborts the tour. No extra camera library. Gestures first; the path is a tour, not a product film.

Vanilla was the right constraint. The wrong constraint was writing a changelog that described a file we do not ship.