Spaces:
Running
Running
Download src/loop.js from cloudronin/astara: direct link, hf CLI and curl.
- Browser
- Download file 2.31 kB
-
https://huggingface.co/spaces/cloudronin/astara/resolve/main/src/loop.js
- Command line
-
hf download hf://spaces/cloudronin/astara/src/loop.js
-
curl -L -o loop.js https://huggingface.co/spaces/cloudronin/astara/resolve/main/src/loop.js
2.31 kB
| // Fixed-timestep game loop with render interpolation. | |
| // | |
| // Logic advances in exact 1/60s steps so behaviour is frame-rate independent and | |
| // reproducible. Rendering happens once per animation frame and interpolates between | |
| // the previous and current logic states using `alpha`, so motion stays smooth even | |
| // when the display refresh and the logic step do not line up. | |
| export function createLoop({ step = 1 / 60, update, render, maxFrameTime = 0.25, maxSteps = 8 }) { | |
| let accumulator = 0; | |
| let previousTime = 0; | |
| let rafId = 0; | |
| let running = false; | |
| // Debug readout. Owned here because this is the only place that sees real frame times. | |
| const stats = { fps: 0, steps: 0, frames: 0, panics: 0 }; | |
| let fpsElapsed = 0; | |
| let fpsFrames = 0; | |
| function tick(now) { | |
| // A frame can already be queued when stop() runs, so cancelling the handle | |
| // is not enough on its own — check the flag before doing any work or | |
| // scheduling the next one. | |
| if (!running) return; | |
| rafId = requestAnimationFrame(tick); | |
| let frameTime = (now - previousTime) / 1000; | |
| previousTime = now; | |
| if (!Number.isFinite(frameTime) || frameTime < 0) frameTime = 0; | |
| // A tab that was backgrounded returns a huge delta. Clamp it rather than | |
| // simulating thousands of steps at once. | |
| if (frameTime > maxFrameTime) frameTime = maxFrameTime; | |
| accumulator += frameTime; | |
| let steps = 0; | |
| while (accumulator >= step && steps < maxSteps) { | |
| update(step); | |
| accumulator -= step; | |
| steps++; | |
| stats.steps++; | |
| } | |
| // If we hit the step ceiling we are running slower than real time. Drop the | |
| // backlog instead of spiralling. | |
| if (steps === maxSteps && accumulator >= step) { | |
| accumulator = 0; | |
| stats.panics++; | |
| } | |
| render(accumulator / step, frameTime); | |
| stats.frames++; | |
| fpsElapsed += frameTime; | |
| fpsFrames++; | |
| if (fpsElapsed >= 0.5) { | |
| stats.fps = Math.round(fpsFrames / fpsElapsed); | |
| fpsElapsed = 0; | |
| fpsFrames = 0; | |
| } | |
| } | |
| return { | |
| stats, | |
| start() { | |
| if (running) return; | |
| running = true; | |
| previousTime = performance.now(); | |
| accumulator = 0; | |
| rafId = requestAnimationFrame(tick); | |
| }, | |
| stop() { | |
| running = false; | |
| cancelAnimationFrame(rafId); | |
| }, | |
| }; | |
| } | |