perf(web): render membrane nebula at 1/4 resolution to kill load jank#29
Conversation
The fixed background canvas painted 6 screen-sized radial-gradient glow clouds every frame directly to the main canvas. A forced-raster microbenchmark at 2560x1237 measured this at ~21ms/frame — on its own over the 16.7ms frame budget — making it the dominant cost of the heavy first animation frames that produce the intermittent startup jank. The clouds are inherently soft, so render them to a 1/4-resolution offscreen canvas and upscale-blit once per frame. Drift and pulse stay fully live; only the fill overdraw shrinks. Measured 21.2ms -> 0.28ms per frame (~77x). Stars (0.37ms) and the frame shadowBlur (0.9ms) were confirmed minor and left unchanged. Note: felt jank must be confirmed live (rAF is suspended in the background automation tab used to benchmark), but the dominant per-frame op cost is verified to drop dramatically. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Merging this PR will degrade performance by 30.7%
|
| Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|
| 👁 | parse_k8s_path[/api/v1/namespaces/prod/secrets/db-password] |
5.9 µs | 8.5 µs | -30.7% |
Comparing perf/web-membrane-nebula-lowres (b255bfb) with main (c078133)
There was a problem hiding this comment.
Code Review
This pull request optimizes the rendering of the galaxy gas layer in useMembrane.ts by drawing it onto a 1/4 scale offscreen canvas and upscaling it, which reduces fill rate overhead. The review feedback correctly points out a potential IndexSizeError in some browsers if drawImage is called with dimensions of 0 before the first resize occurs, and suggests a guard condition to prevent this.
There was a problem hiding this comment.
No issues found across 1 file
Architecture diagram
sequenceDiagram
participant RAF as requestAnimationFrame
participant CM as Canvas Manager (useMembrane)
participant OC as Offscreen Canvas (nebCanvas)
participant MC as Main Canvas
participant Browser as Browser Compositor
Note over RAF,Browser: Per-frame render pipeline
loop Every animation frame
RAF->>CM: frame callback (timestamp)
CM->>CM: resize check & DPR transform
CM->>CM: star rendering (unchanged)
Note over CM: Stars: 0.37ms
CM->>CM: shadowBlur glow (unchanged)
Note over CM: shadowBlur: 0.9ms
CM->>OC: nebula rendering
Note over CM,OC: 1/4 resolution offscreen path
CM->>OC: clearRect (0,0,nebW,nebH)
loop For each of 6 nebula clouds
CM->>OC: update drift position (x+=dx, y+=dy)
CM->>OC: calculate alpha (pulse wave)
CM->>OC: createRadialGradient (sx,sy,sr)
CM->>OC: fillStyle = gradient
CM->>OC: arc + fill()
end
Note over CM,OC: 0.28ms total (was 21.2ms at full res)
CM->>MC: drawImage(nebCanvas, 0,0 → 0,0,W,H)
Note over CM,MC: Upscale blit to main canvas
MC-->>Browser: composite frame
Browser-->>RAF: next frame ready
end
alt Initial load (first frames)
Note over CM,MC: Heavy gradient rasterization is now on small offscreen canvas
Note over MC,Browser: Main canvas paint is cheap (scaled blit)
end
Greptile SummaryThis PR moves the membrane nebula rendering onto a smaller offscreen canvas.
Confidence Score: 5/5This looks safe to merge.
Important Files Changed
Reviews (3): Last reviewed commit: "chore: retrigger CI for fresh CodSpeed b..." | Re-trigger Greptile |
Belt-and-suspenders: skip the offscreen render + drawImage when the nebula buffer dimensions are 0, so no IndexSizeError can fire on the first frame before resize() runs (Safari throws on a zero-size source rect). Addresses PR review feedback. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
CodSpeed flagged a phantom -32% regression on an unrelated Rust benchmark (parse_k8s_path) though this branch changes only frontend TS. The first commit's CodSpeed run passed; forcing a fresh measurement. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|



배경
랜딩페이지 접속 초반에 간헐적 jank가 있었습니다. 원인을 추정이 아닌 측정으로 특정했습니다.
useMembrane의 고정 배경 캔버스가 화면 크기의 라디얼 글로우 구름 6개를 매 프레임 메인 캔버스에 직접 fill 하고 있었습니다. 래스터를 강제한 마이크로벤치마크(2560×1237)에서 프레임당 비용은 다음과 같았습니다:nebula 하나가 16.7ms 프레임 예산을 혼자 초과했습니다. 비용은 gradient 생성이 아니라 화면 크기 fill 오버드로우의 래스터화라, gradient 캐싱은 무효였습니다. 이게 로드 초반 무거운 첫 프레임들 = 초기 jank의 실체입니다.
변경
nebula를 1/4 해상도 오프스크린 캔버스에 렌더 후 업스케일 blit. 구름이 본디 흐릿해 업스케일해도 시각적으로 동일하고, 드리프트·펄스는 매 프레임 그대로 유지됩니다.
검증 노트
op 비용 감소는 벤치마크로 확실합니다. 다만 체감 jank 해소는 라이브 확인 필요 — 벤치마크에 쓴 백그라운드 자동화 탭에서는 rAF가 정지되어 실제 FPS를 못 잽니다. 리뷰어가 포그라운드에서 reload 몇 번 / DevTools Performance로 확인 부탁드립니다.
🤖 Generated with Claude Code
Summary by cubic
Render the membrane nebula at 1/4 resolution on an offscreen canvas and upscale each frame to remove load jank. Add a zero-size guard to avoid Safari IndexSizeError on the first frame; drift/pulse stay live; cost ~21.2ms → ~0.28ms/frame (~77x); stars and shadowBlur unchanged. CI retriggered for fresh CodSpeed baseline (no code changes).
Written for commit b255bfb. Summary will update on new commits.