Don't fill cards the reader is flinging past

Profiling the reported stutter on a fixed 400-card grid: 30 cards mounted
during one fast scroll, 693ms of blocked main thread -- about 23ms per
card. Building the card is 15.6ms of that. The rest is what a mount sets
off: a thumbnail request and decode, the height correction its load
triggers, a forced layout to measure the title, and an /api/resolve call
that runs yt-dlp on the server. Measured separately, a single fast scroll
fired twelve of those, peaking at nine a second, for videos the reader
never stopped on.

None of it is work anyone asked for while the list is moving. So a mount
during a fling now only places the card: right size, right position, text
and heart in place, so the grid and the scrollbar stay exactly correct.
Everything that costs waits 140ms for the scroll to settle, and then runs
only for the cards still on screen. Whatever was scrolled past is
unmounted having cost almost nothing.

The threshold is 1600px/s, well above a deliberate scroll, so reading at
a normal pace behaves as it did. The visible trade is that flinging
through a long list shows card text over an empty thumbnail box until you
slow down.

Roughly half the blocked time was browser style, layout, paint and decode
that no amount of restructuring removes -- this avoids provoking that work
for cards nobody looks at, rather than making it cheaper.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QBDkEXP4htyXTCZUwMLphd
This commit is contained in:
Simon
2026-09-09 18:47:33 +00:00
parent 7624ca559a
commit e603111d70
2 changed files with 79 additions and 6 deletions

View File

@@ -170,7 +170,16 @@ def check_cards(c, cards, phase):
def main():
c = Checks()
with sync_playwright() as p:
browser = p.chromium.launch(args=["--no-sandbox"])
# Lean launch flags: this runs alongside the app's own server, and a
# default Chromium spikes hard enough at startup to get itself killed on
# a constrained box.
browser = p.chromium.launch(args=[
"--no-sandbox",
"--disable-dev-shm-usage",
"--disable-gpu",
"--renderer-process-limit=1",
"--js-flags=--max-old-space-size=512",
])
page = browser.new_page(viewport={"width": 1400, "height": 1000})
boot(page)