A lightweight Ticker class for frame-based game loops.
It uses requestAnimationFrame under the hood and optionally supports an FPS cap.
yarn add @webgamelibs/ticker
# or
npm install @webgamelibs/ticker
Type definitions (.d.ts) are bundled, so it works seamlessly with TypeScript.
import { Ticker } from '@webgamelibs/ticker'
const ticker = new Ticker((deltaTime) => {
// deltaTime: elapsed time in seconds (float)
console.log(`Elapsed time per frame: ${deltaTime.toFixed(3)}s`)
})
// Enable FPS cap
ticker.setFpsCap(60)
// Disable FPS cap
ticker.disableFpsCap()
// Stop the loop when no longer needed
ticker.remove()
new Ticker(onTick: (deltaTime: number) => void, fps?: number)
deltaTime argument is the time passed in seconds since the last tick.| Method | Description |
|---|---|
setFpsCap(fps) |
Dynamically set the FPS cap. Passing a value ≤ 0 disables the cap. |
disableFpsCap() |
Disables FPS capping, resuming uncapped execution. |
remove() |
Stops the internal requestAnimationFrame loop and cleans up. |
onTick is executed at a fixed step interval (1 / fps).onTick multiple times.
If the lag exceeds 2 * fixedStep, an additional call with the full delta time is made to prevent excessive slowdowns.This design ensures a consistent update rate for game logic while still using requestAnimationFrame for smooth rendering.
onTick throws, the error is logged via console.error but the loop keeps running.prevTime is always updated in finally, so the next deltaTime is not inflated by the failed frame.const ticker = new Ticker(() => {
throw new Error('Test error')
})
// The error is logged, but the game loop continues.
requestAnimationFrame pauses or slows in inactive tabs, which may produce very large deltaTime values once the tab becomes active again.
Ticker does not clamp these values by default — clamp manually if needed:
const MAX_DT = 1 / 15 // Clamp to 15 FPS minimum
const ticker = new Ticker((dt) => {
update(Math.min(dt, MAX_DT))
})
This package is tested with Jest.
yarn add --dev jest ts-jest @types/jest jest-environment-jsdom
Minimal example:
import { Ticker } from './ticker'
test('Ticker passes deltaTime to onTick', () => {
const onTick = jest.fn()
const ticker = new Ticker(onTick)
// Mock requestAnimationFrame and advance time manually here...
ticker.remove()
expect(onTick).toHaveBeenCalled()
})
For a full-featured test suite (including FPS cap scenarios and cancellation), see src/ticker.test.ts.
MIT License