fix(tps-chart): restore smooth line render without per-frame recharts re-renders - #118
Merged
Conversation
…ts renders fbcabf2 throttled the sliding clock to 10 Hz to stop recharts re-rendering every animation frame. That also throttled the head-vertex interpolation, so the newest segment now draws in ~3 steps per 300ms block instead of ~18, and the x-domain steps by tens of pixels while the window is still clamped to MIN_WINDOW_MS. Both read as stutter. Split the two concerns instead: - Draw the line and its fill in a <Customized> layer that rewrites the two <path> d attributes from a rAF loop, outside React. The head animation is back at full frame rate and costs no reconciliation. - Keep a transparent <Area> so recharts still owns the y-domain, the tooltip payload and the hover dot. - Make the sliding clock adaptive: publish when the domain has advanced ~1/1000 of the visible window, clamped to [16ms, 100ms]. A narrow window renders per frame but holds few points; the full 5-minute window holds ~1000 points but publishes at 10 Hz. Net recharts re-renders are lower than before fbcabf2 at every zoom level. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Camillebzd
requested review from
Im-Madhur-Gupta,
iamvukasin,
marcuspang and
mijovic
as code owners
August 31, 2026 07:40
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Feeding recharts the raw history left the tooltip and active dot on the newest raw sample while TpsSeries was still sweeping the head towards it, so the dot sat up to one inter-arrival interval (~300ms) ahead of the visible tip, and a full sample's worth of TPS above or below it. Feed recharts the drawn history again. TpsSeries still receives the raw array and still interpolates every frame, so smoothness is unaffected; the residual mismatch collapses to the sliding-clock gap, which CLOCK_STEP_FRACTION already bounds at roughly a pixel. Trades back the stable y-domain: the axis max can rescale mid-sweep again. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Im-Madhur-Gupta
approved these changes
Aug 31, 2026
Im-Madhur-Gupta
left a comment
Collaborator
There was a problem hiding this comment.
LGTM, confirmed smooth on the deployment.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
#112 (
fbcabf2) throttled the sliding clock from per-frame to 10 Hz to stop recharts re-rendering on every animation frame. That was the right diagnosis, but the clock drives two different things and throttling only one of them is harmless:windowStartclamps toMIN_WINDOW_MS = 3s, so for the first ~30 s after load the window is a few seconds wide and 100 ms is tens of pixels per step.drawHistory. Broken at every zoom level. Blocks arrive every ~300 ms, so the head sweeps from the previous TPS value to the new one in 3 steps instead of ~18. The line body scrolls while the tip twitches — that's the stutter.Per-frame motion is genuinely required. The real cost was that every
setNowre-rendered the wholeAreaChart— ~1000 points at the 5-minute window (300 ms blocks × 5 min), plus all axes, ticks and layers, 60× a second.Fix
Split the two: recharts owns what only changes with data, and the per-frame animation leaves React entirely.
TpsSerieslayer rendered via recharts'<Customized>. It receives the plot rect (offset) and the real y-scale (yAxisMap) from the chart, renders two empty<path>elements once, and arequestAnimationFrameloop rewrites theirdattributes each frame with the livenow. Head interpolation is unchanged, so the tip animation is what it was before perf(frontend): throttle TPS chart updates to 10 Hz #112.<Area>stays but is transparent (strokeOpacity={0} fillOpacity={0}). It still drives the y-domain, the tooltip payload and the hover dot, so interaction is unchanged. It keepsstroke="#6E54FF"because recharts derives the active dot's colour from that prop, not from opacity.useSlidingNowis now adaptive rather than fixed-100 ms: it publishes when the domain has advanced ~1/1000 of the visible window, clamped to[16ms, 100ms]. This inverts the tradeoff correctly — the narrow window renders per frame but holds ~10 points; the wide window holds ~1000 points but publishes at 10 Hz. Cost per second is roughly flat across zoom levels.Net recharts re-renders are lower than before #112 at every zoom level, and the line is smooth again.
Notes for the reviewer
tsc, Biome andnext buildall pass, but the visual result has not been checked in a browser. Worth eyeballing the first 30 s after load (narrow window) and the gradient fill, which is reproduced withfillOpacity={0.6}to match recharts'<Area>default.offsetandyAxisMapare recharts v2 internal props passed through<Customized>. Stable in 2.x, but recharts 3 reworked the internal state model — this layer will need revisiting on that upgrade. There's a comment onTpsSeriesexplaining why it isn't just an<Area>.🤖 Generated with Claude Code
Greptile Summary
The PR separates high-frequency TPS line animation from lower-frequency Recharts rendering while preserving Recharts-owned axes, scaling, tooltip data, and hover behavior.
TpsSerieslayer that updates SVG paths on every animation frame.Confidence Score: 5/5
The PR appears safe to merge.
No blocking failure remains.
Important Files Changed
Flowchart
%%{init: {'theme': 'neutral'}}%% flowchart LR H[Raw TPS history] --> C[Adaptive React clock] C --> D[Interpolated chart data] D --> R[Transparent Recharts Area] R --> Y[Y-axis domain] R --> T[Tooltip and active dot] H --> S[Custom TpsSeries] S --> F[Per-frame SVG path updates] R --> SReviews (2): Last reviewed commit: "fix(tps-chart): keep the tooltip attache..." | Re-trigger Greptile