Repository navigation
Eliminate lag-spike sources in the engine and host - #3
Conversation
The site hitched at predictable moments; each fix removes one cause. - Grid storage is grow-only: the demo rotation flips gridCellSize (40 -> 16 -> 40), and each flip destroyed and reallocated grid buffers sized by the whole world (~120 MB) mid-transition. - The cell census copies readbacks into per-slot reused arrays instead of slicing ~1.9 MB of fresh typed arrays per frame -- steady GC-pause fuel. - webgpu() descriptors, array-input lists, and per-pass WGSL are cached; all were rebuilt (multi-KB template strings included) every frame. - Compute pipelines compile via createComputePipelineAsync, off the boot task. - The CPU fallback no longer allocates per particle per frame (descriptor and input caching, hoisted closures, in-place integration, reused grid cells, memoized fill styles) and acquires its 2D context with willReadFrequently so per-particle sensor reads stop stalling on GPU readback. - The host skips the several-hundred-ms name/Voronoi and text-field rebuilds when only the page height changed (the mobile URL-bar case), memoizes layout(), stops collecting dev-only density stats in production, early-exits Lloyd relaxation once seeds settle, and yields between the heavy boot steps so page load stays responsive. Verified: lint, build, force/density checks green; headless runs show the height-only resize stall (~600 ms) and the idle-rotation long task (~1 s) gone, with both runtimes rendering identically to baseline. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F9Fq5ub3QM8igyjzkU92pn
The module is enabled in nearly every preset, so the two per-particle Vector allocations ran for every particle of every frame on the CPU fallback. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F9Fq5ub3QM8igyjzkU92pn
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6cfabf0893
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| private positionState: Map< | ||
| number, | ||
| { prev: { x: number; y: number }; post: { x: number; y: number } } | ||
| > = new Map(); |
There was a problem hiding this comment.
Clear cached positions when replacing particles
On the CPU fallback, every width change calls spawnAll() and then setParticles(), creating 8,000 particles with new monotonically increasing IDs, while this persistent map retains every record from the previous swarm. Repeated resizes therefore grow positionState without bound—three objects per retired particle—and clear(), reset(), and destroy() also leave it populated; clear or prune the cache whenever the particle collection is replaced or discarded.
Useful? React with 👍 / 👎.
Hunts down and removes the causes of the intermittent frame hitches on the site. Each fix targets one measured mechanism.
The spikes and their causes
gridCellSize40 → 16 → 40, and each flip destroyed and reallocated GPU grid buffers sized by the whole world (~120 MB at cell 16 on this page) mid-transition.createGridStorageis now grow-only, so after boot no transition reallocates anything. A 70s headless run across both flips now shows zero long tasks (previously a ~1s task).webgpu()descriptor and multi-KB per-pass WGSL strings each frame; all are now cached.layout()is also memoized so spurious ResizeObserver fires stop discarding the overlay canvas backing stores.createComputePipelineAsyncoff the main thread.cpu()descriptors and input objects per pass (withread()called once per key, quadratic in key count),getState/setStateclosures per particle, five objects per particle in integration, a reallocated rows×cols grid every frame, and a newrgba()style string per particle. All removed. The 2D context is now acquired withwillReadFrequently, so the sensors module's per-particlegetImageDatacalls (two per particle per frame in most rotation demos) stop being synchronous GPU readback stalls.Deliberately not changed
.party-canvas { filter: invert(1) contrast(2) }— constant compositor cost, but it is the site's visual baseline, not a spike.arc/filldispatches — constant ~2-5 ms, and batching would change alpha blending at overlaps.Verification
npm run lint,npm run build,check:force(816 assertions),check:density(139,500 assertions), agent-config checks,pre-commitall green.vendor/party/README.md.🤖 Generated with Claude Code
https://claude.ai/code/session_01F9Fq5ub3QM8igyjzkU92pn
Generated by Claude Code