What I do is this:
- Keep target timestamp, when next tick is supposed to occur. Sleep aiming for this target or next graphics frame, whichever comes earlier.
- Whenever code wakes up - assume any amount of time may have passed
- Check against target: If only 1 tick has passed, set new target = (old_target(!) + tickrate). This is the FPS-independent steady logical tickrate.
- If more than 1 tick has passed, advance by only 1 tick (avoid frame skip - effectively pause game if tab is not focused). Then set new target=now. Obviously it’s also an option to spin through some or all missed ticks, might be desired if tickrate is not RTS-tier low…
- Now draw, in case the time has come for a new graphics frame
let the renderer do whatever but get my gaurenteed 60hz tick?
You’re completely at the browser’s mercy. Usually there is e.g. a hard-limit of one wakeup per second on tabs that are not being shown. If a tab is open, the invocations are still wildly inaccurate from a gamedev pov. emscripten_sleep(10) will wake up after 8-30 ms, depending on browser and os.
setTimeout is already inaccurate, and emscripten_sleep is just a very bad/clever hack that unravels the current stack, stores it and schedules a task with setTimeout which will restore that stack and resume its execution.
My hack against this is to call emscripten_sleep with only 75% of the time I actually want to be delayed, and to never even call it with values lower than 8 ms, but rather just skip those 8 ms and move on directly.
Sdlcoven is now my only emscripten-based web version. The others are based on winit, which is a pain to build around, but nicely communicates "browser requests a draw" (browser always Vsyncs btw.) versus "you wake up because of an event / because you asked to be woken up." It makes for very fluid rendering and gameplay, if it is wired up correctly.