Skip to content

perf: improve LCP by parallelising board load and caching decrypts#216

Merged
d0x2f merged 1 commit into
masterfrom
claude/improve-lcp-metric-VEGuI
May 5, 2026
Merged

perf: improve LCP by parallelising board load and caching decrypts#216
d0x2f merged 1 commit into
masterfrom
claude/improve-lcp-metric-VEGuI

Conversation

@d0x2f
Copy link
Copy Markdown
Owner

@d0x2f d0x2f commented May 5, 2026

Summary

The Core Web Vitals report flags the card body (div.p-1.w-100.pre-wrap in Card.svelte) as the dominant LCP element. Three changes shorten the critical path to its first paint:

  • Parallelise Board.svelte onMount. Previously it serialised six awaits — getBoardgetRankscheckPassword → three subscribeToX calls (each with its own signIn round-trip) — before flipping busy = false. Now getRanks and the three firestore subscription handshakes run concurrently, and busy = false flips as soon as ranks REST + password check resolve. Subscriptions complete in the background; cards/ranks populate as their first snapshots arrive.
  • Drop the 200ms transition:fade on the cards container. Pure win — that 200ms was being added on top of the LCP every load.
  • Memoise signIn() in firestore.js so the three concurrent subscribers share one /auth + signInWithCustomToken round-trip instead of racing three.
  • Cache decrypt() results in encryption.js (LRU, bounded at 1024 entries) so re-renders of the same ciphertext don't redo PBKDF2's 200k iterations. Helps encrypted boards on every reactive update after the first paint.

Two further ideas from the analysis were deferred as too invasive for this PR: pre-decrypting cards eagerly into the store, and moving PBKDF2 off the main thread via Web Workers.

Test plan

  • npm run lint
  • npm run build
  • npx cypress run — full local suite (86 tests across 19 specs) passes, including Encryption.cy.js, ConnectionLost.cy.js, OwnerRealtimeSync.cy.js, OpenPermission.cy.js
  • Verify in production that the LCP "Element" breakdown shifts from Poor toward Good for the card body selector

https://claude.ai/code/session_01CbPv23BpW2H57tvRh7fxqQ


Generated by Claude Code

The card body was the dominant LCP element. Three changes shorten the
critical path to its first paint:

- Board.svelte onMount now starts getRanks and the three firestore
  subscription handshakes in parallel instead of serialising them, and
  drops the 200ms fade-in on the cards container. busy=false now flips
  as soon as ranks REST + the password check resolve, rather than
  waiting for every subscription to register.
- firestore.signIn() is memoised so the parallel subscribers share a
  single /auth + signInWithCustomToken round-trip.
- encryption.decrypt() caches results (LRU, bounded at 1024 entries)
  so re-renders of the same ciphertext don't re-run PBKDF2's 200k
  iterations.
@d0x2f d0x2f merged commit 8fd972a into master May 5, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants