Keep native document geometry while translating content behind one viewport fade. Preserve navigation, focus, media preloading, and Static cleanup without changing 3D quality or render resolution. Validated assets, types, production builds, prerendered artifacts and transfer budgets; compared 64 layouts and checked 87 live interactions plus uncached media and repeated cleanup. High improved from 71.7 to 101.4 FPS at DPR 1 and 47.8 to 62.2 FPS in the full-size DPR 1.5 window, with occasional sub-60 intervals still remaining.
97 lines
5.2 KiB
Markdown
97 lines
5.2 KiB
Markdown
# Scrolling performance investigation
|
||
|
||
Production baseline: `f3f2b03` on `codex/portfolio-performance`.
|
||
|
||
Hardware: ASUS ROG Strix SCAR 16 G635LX, Intel Core Ultra 9 275HX,
|
||
hardware-backed ANGLE Intel Mesa ARL graphics, Chrome 152. Viewport
|
||
1440 × 900, DPR approximately 1, canvas 1425 × 900. Effective High
|
||
and Medium tiers were recorded throughout each measurement. Phone testing
|
||
is deferred.
|
||
|
||
## Findings
|
||
|
||
Matched 45-second High-quality scrolling runs after five seconds of warm-up:
|
||
|
||
| Condition | Presented frames/second | p95 presentation interval |
|
||
| --- | ---: | ---: |
|
||
| Normal page | 71.4 | 25 ms |
|
||
| Chapter masks disabled | 116.1 | 12.5 ms |
|
||
|
||
The 3D scene, render resolution, bloom, and motion were unchanged. Disabling
|
||
masks changes the appearance and was diagnostic only. Native wheel scrolling
|
||
reproduced the pattern. A separate 32-second Medium comparison measured
|
||
80.2 versus 142.4 animation callbacks/second, with p95 intervals of 20.9
|
||
versus 12.4 ms.
|
||
|
||
Full traces showed approximately 8,800 versus 840 page raster operations.
|
||
Static masks performed substantially better than continually changing masks.
|
||
Layer promotion, paint containment, and viewport-sized mask images did not
|
||
resolve the slow frames. Whole-world GPU samples had a median of 2.7 ms;
|
||
JavaScript scene submission was approximately 0.7 ms. Removing the entire
|
||
car or terrain did not resolve the 25 ms p95 scrolling intervals. Bypassing
|
||
bloom helped less than disabling masks and still exceeded the 60 FPS budget.
|
||
|
||
The absolute rate in this session exceeded the earlier approximately 45 FPS
|
||
control, including when rerunning the original measurement harness. The
|
||
reason for that session difference was not isolated. These are matched
|
||
same-session diagnostic comparisons, not newly shipped improvements.
|
||
Detailed tracing adds overhead; GPU timer intervals may include stalls and
|
||
cannot be added directly to displayed frame intervals.
|
||
|
||
## Implementation direction
|
||
|
||
Keep the fades visually close while separating the stationary reading edge
|
||
from scrolling content, allowing rasterized content and mask data to be
|
||
reused. Preserve native document scrolling, navigation, focus, content,
|
||
3D quality, audio, and shared journey outputs. Validate responsive layouts,
|
||
short viewports, reversals, quality changes, Static mode, reduced motion,
|
||
resource cleanup, and sustained rendering before retaining the change.
|
||
|
||
The user allows modest visual differences if the result does not look worse.
|
||
No production changes were made during investigation. All browser runs
|
||
inventoried and reused the single existing Chrome tab/window. Temporary
|
||
scripts, traces, screenshots, and detailed measurements remain in the ignored
|
||
`.perf/investigation/` folder; no tests or frameworks were added.
|
||
|
||
## Stationary reading fade implementation
|
||
|
||
The native document retains its original height and anchor coordinates. One
|
||
sticky reading viewport owns the fade; the content translates behind it.
|
||
The opening fade still advances with scrolling, then stays fixed. Live heading
|
||
and project-header entrance animations are replaced by this reading edge,
|
||
while Static mode retains normal document flow and existing styles.
|
||
|
||
Media observers use the reading viewport as their live root. Images use a
|
||
1250px lookahead; an uncached probe confirmed they load by 1200px before the
|
||
viewport, matching the baseline probe. Video activation retains its 300px margin. Static cleanup
|
||
restores native lazy loading. Car, terrain, bloom, render resolution, audio,
|
||
content, and shared journey calculations are unchanged.
|
||
|
||
Validation: 64 chapter/layout comparisons across eight viewport sizes retained
|
||
page height and anchor geometry within 0.002px. All 87 live interaction checks
|
||
passed without errors, hidden headings, or horizontal overflow. Repeated
|
||
quality changes retained stable listener/resize-observer counts and restored
|
||
Static styles. Graphics allocation counts matched the preceding production
|
||
baseline; this is not a claim that all browser/driver allocations are released.
|
||
Uncached media-boundary and image-attribute cleanup checks passed. Approved
|
||
assets, types, both builds, prerendering, artifacts, and transfer budgets passed.
|
||
Independent code review found no remaining concrete issues. All browser work
|
||
reused the same single Chrome tab and window.
|
||
|
||
Final presented-frame measurements on the same hardware and effective High:
|
||
|
||
| Scrolling condition | Before | After | Before/after p95 interval |
|
||
| --- | ---: | ---: | ---: |
|
||
| 1440 × 900 CSS, DPR 1, 45 seconds | 71.7 FPS | 101.4 FPS | 25 / 16.7 ms |
|
||
| Maximized 1707 × 944 CSS, DPR 1.5 | 47.8 FPS | 62.2 FPS | 33.3 / 20.8 ms |
|
||
|
||
The maximized baseline ran 32 seconds; the final implementation ran 60 seconds.
|
||
The same first 32-second route cycle was also compared in the temporary
|
||
measurement evidence. The 60-second run's lowest complete one-second interval
|
||
was 58 FPS: this improves full-resolution scrolling but does not guarantee
|
||
locked 60 FPS. Both conditions retained their original canvas dimensions
|
||
(1425 × 900 and 2538 × 1416 respectively). Before the final image-preload
|
||
compatibility adjustment, Medium measured 81.4 → 125.7 presented FPS at DPR 1,
|
||
and native wheel input independently confirmed the scrolling improvement.
|
||
Temporary implementation diagnostics remain in ignored `.perf/fade/` files.
|