explodorama / files / src / ui / fps.ts
fps.ts
| 1 | import { 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 | */ |
| 11 | const SHOW = new URLSearchParams(location.search).has('fps') |
| 12 | const EVERY_MS = 500 |
| 13 | |
| 14 | let meter: HTMLElement | null = null |
| 15 | let frames = 0 |
| 16 | let since = performance.now() |
| 17 | let lastFrame = since |
| 18 | let slowest = 0 |
| 19 | let gameMs = 0 |
| 20 | |
| 21 | /** Wraps an engine callback, so the time spent in it counts as the game's own. */ |
| 22 | export 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. */ |
| 32 | export 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 | } |