fuzzyworld

explodorama / files / src / ui / fps.ts

fps.ts

Blame: the commit that last changed each line · file · history

0b63d158 2026-10-25 Pavel Safronov1import { getCanvasPixelRatio, mainCanvas } from 'littlejsengine'
2
3/**
4 * A frame-rate meter in the bottom left corner (user request, 2026-10-24: the game crawled in
5 * Android Chrome). Shown with `?fps` in the address (in `npm run dev` too until the user turned it
6 * off there, same day). It says the frames a second, the slowest frame of the last half second,
7 * how long the game's own update and drawing took per frame (the rest of a slow frame is the
8 * browser: style, layout, painting, the GPU), and the canvas's size in pixels with the screen's
9 * pixel ratio.
10 */
11const SHOW = new URLSearchParams(location.search).has('fps')
12const EVERY_MS = 500
13
14let meter: HTMLElement | null = null
15let frames = 0
16let since = performance.now()
17let lastFrame = since
18let slowest = 0
19let gameMs = 0
20
21/** Wraps an engine callback, so the time spent in it counts as the game's own. */
22export function timed(callback: () => void): () => void {
23 if (!SHOW) return callback
24 return () => {
25 const start = performance.now()
26 callback()
27 gameMs += performance.now() - start
28 }
29}
30
31/** Called once a frame, after drawing. */
32export function countFrame(): void {
33 if (!SHOW) return
34 if (!meter) {
35 meter = document.createElement('div')
36 meter.id = 'fps'
37 document.body.append(meter)
38 }
39 const now = performance.now()
40 slowest = Math.max(slowest, now - lastFrame)
41 lastFrame = now
42 frames++
43 if (now - since < EVERY_MS) return
44 const seconds = (now - since) / 1000
45 meter.textContent =
46 `${Math.round(frames / seconds)} fps · slowest ${Math.round(slowest)} ms · game ${(gameMs / frames).toFixed(1)} ms\n` +
47 `${mainCanvas.width}×${mainCanvas.height} px · ×${getCanvasPixelRatio().toFixed(2)}`
48 frames = 0
49 slowest = 0
50 gameMs = 0
51 since = now
52}