Performance
An overlay shares the computer with a game that wants every frame. A few habits keep yours smooth and nearly free.
Ce guide n’existe pour l’instant qu’en anglais.
Read only what you show
Every topic you read is sent to you at its rate, and car comes about 60 times a second. An overlay of the weather that touches car by accident gets 60 updates a second for nothing. Read topics where you use them, not all at the top.
Draw on screen frames
Topics arrive at their own rates and never perfectly evenly; the screen refreshes at its own. For anything that moves, draw once per frame from the latest value, and let messages only update the data:
const frame = (): void => {
needle.style.transform = `rotate(${overlay.telemetry.car.rpm / maxRpm}turn)`;
requestAnimationFrame(frame);
};
overlay.on('ready', () => requestAnimationFrame(frame));Text that changes a few times a second (lap times, positions) can stay in the telemetry handler: drawing it per frame gains nothing.
Smooth graphs and maps
Data arrives a little unevenly, so a graph drawn as messages come looks jerky. overlay.sampledAt[topic] is when the newest data was taken, on the performance.now() clock, the clock of requestAnimationFrame. Keep samples with that time and draw slightly in the past, about 50 ms, interpolating between the two samples around that moment:
type Sample = { at: number; throttle: number };
const samples: Sample[] = [];
overlay.on('telemetry', ({ car }) => {
const at = overlay.sampledAt.car;
// The event comes for every topic; keep a car sample once.
if (at === undefined || at === samples.at(-1)?.at) return;
samples.push({ at, throttle: car.throttle });
while ((samples[0]?.at ?? at) < at - 10_000) samples.shift(); // the last 10 s
});
const frame = (now: number): void => {
drawTrace(samples, now - 50); // 50 ms behind: there is always a sample on each side
requestAnimationFrame(frame);
};
requestAnimationFrame(frame);React
The template’s useOverlay renders App on every message of every topic you read. That is fine for text; for a needle or a graph at 60 frames a second it is a lot of work. Draw the fast parts outside React state:
Without React the page is already drawn by hand: the code above is all it takes.
export function Needle({ overlay, maxRpm }: NeedleProps) {
const ref = useRef<HTMLDivElement>(null);
useEffect(() => {
let id = requestAnimationFrame(function frame() {
const turn = overlay.telemetry.car.rpm / maxRpm;
if (ref.current) ref.current.style.transform = `rotate(${turn}turn)`;
id = requestAnimationFrame(frame);
});
return () => cancelAnimationFrame(id);
}, [overlay, maxRpm]);
return <div className="needle" ref={ref} />;
}Split the rest so a fast topic re-renders only the component that shows it, and pass it only the values it needs.
What else makes drawing faster
- Move and scale with
transformandopacity; changingwidth,topor fonts every frame makes the browser lay out the page again. - Change text and attributes of elements you keep; do not rebuild lists each message. A list of drivers keyed by
carIdxstays put while the order changes. - Numbers that change all the time:
font-variant-numeric: tabular-nums, so the digits do not shift the text around. - Many points or lines (a map, a long trace) belong on a canvas, not in hundreds of elements.