# @armniko/ticker

> A lightweight, zero-dependency JavaScript/TypeScript library for running an application loop with separate logic and draw ticks, time scaling, FPS limiting, and low-FPS detection.

Latest version **2.3.0** (published 2026-05-03) · MIT license · 0 weekly downloads

## Install

```sh
npm install @armniko/ticker
pnpm add @armniko/ticker
yarn add @armniko/ticker
bun add @armniko/ticker
```

## Health

**Score 60/100 (C)** — status: active.

Positive: has types; esm support; no vulnerabilities; high quality score.

Warnings: low downloads.

## Facts

| | |
|---|---|
| Version | 2.3.0 |
| Published | 2026-05-03 |
| First published | 2024-04-01 |
| Weekly downloads | 0 |
| License | MIT |
| TypeScript types | bundled |
| Module format | ESM + CommonJS |
| Dependencies | 0 |
| Unpacked size | 25.4 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| Author | Armīns Nikolajevs |
| Maintainers | armins.nikolajevs |
| Keywords | ticker, game-loop, animation-loop, requestanimationframe, rAF, fps, fps-limit, fps-limiter, delta-time, time-scale, tick, render-loop, update-loop, frame-rate, typescript, game-engine |

## Links

- npm: https://www.npmjs.com/package/@armniko/ticker
- npm.io page: https://npm.io/package/@armniko/ticker

## Alternatives

- [@js-joda/timezone](https://npm.io/package/@js-joda/timezone.md) — 383.4K weekly downloads
- [chartjs-adapter-moment](https://npm.io/package/chartjs-adapter-moment.md) — 210.8K weekly downloads
- [strftime](https://npm.io/package/strftime.md) — 171.2K weekly downloads
- [vue-flatpickr-component](https://npm.io/package/vue-flatpickr-component.md) — 115.8K weekly downloads
- [timepicker](https://npm.io/package/timepicker.md) — 51.0K weekly downloads

## Recent versions

- 2.3.0 (latest) — 2026-05-03
- 2.2.0 — 2026-04-25
- 2.1.1 — 2026-04-19
- 2.1.0 — 2025-12-27
- 2.0.0 — 2024-06-29
- 1.1.0 — 2024-04-27
- 1.0.0 — 2024-04-01

## README

# Ticker

[![npm](https://img.shields.io/npm/v/@armniko/ticker)](https://www.npmjs.com/package/@armniko/canvas)
![last published](https://img.shields.io/npm/last-update/@armniko/ticker)
![zero dependencies](https://img.shields.io/badge/zero-dependencies-brightgreen)
[![types](https://img.shields.io/npm/types/@armniko/ticker)](https://www.npmjs.com/package/@armniko/ticker)

A lightweight, zero-dependency JavaScript/TypeScript library for running an application loop with **separate logic and
draw ticks**, **time scaling**, **FPS limiting** and **low-FPS detection**.
<hr>

### Why Ticker?

- 🎯 **Decoupled logic & rendering** — update game state and draw frames independently
- 🎚️ **FPS control** — cap your draw rate to save CPU/battery
- ⏱️ **Time scaling** — slow down or speed up time without touching your game logic
- 📉 **Low-FPS callbacks** — react when performance degrades (e.g. lower visual quality)
- 🧪 **Test-friendly** — ships with `TickerMock` for deterministic unit tests
- 📦 **Zero dependencies** and fully typed

### Installation

```bash
npm install @armniko/ticker
```

### Usage

```typescript
import { Ticker, Time } from '@armniko/ticker';

const element: { position: { x: number, y: number } } = { position: { x: 0, y: 0 } };
const animation: { durationMs: number, distancePx: number } = {
    durationMs: 2000,
    distancePx: 500,
}
const ticker: Ticker = new Ticker();
ticker.addLogicTask((time: Time): void => {
    const pxPerMs: number = animation.distancePx / animation.durationMs;
    element.position.x += pxPerMs * time.deltaMs;
});
ticker.addDrawTask((): void => {
    // draw
});
```

Ticker instance methods:

- start() – starts ticker.
- stop() – stops ticker.
- isStarted() – checks if ticker is started.
- setFps(options: { min?: number; max?: number; expected?: number }) - set min, max or expected FPS
    - min (default: 0) – defines value at which lowFps task callbacks will be called.
    - max (default: 60) – defines drawing FPS limit.
    - expected (default: 60) - defines expected logical and drawing FPS at which app should work in normal conditions.
- setTimeScale(scale: number) – set time scale.
- addLogicTask(callback) – register callback for update app logic. Returns TickerTaskId.
- addDrawTask(callback) – register callback for update app screen. Returns TickerTaskId.
- addLowFpsTask(callback) – register callback that will be called when reached min FPS. Returns TickerTaskId.
- remove(taskId: TickerTaskId) – removes the provided task.
- fps() – current FPS at which app operates.
- timeScale() – current time scale at which app operates.

### Migration

#### v1 -> v2

Before (v1):

```typescript
import { Ticker } from '@armniko/ticker';

const element: { position: { x: number, y: number } } = { position: { x: 0, y: 0 } };
const animation: { durationMs: number, distancePx: number } = {
    durationMs: 2000,
    distancePx: 500,
}
const ticker: Ticker = new Ticker({
    onLogicTick: (): void => {
        const pxPerMs: number = distancePx / animationDurationMs;
        element.position.x += pxPerMs * ticker.msBetweenTicks();
    },
    onDrawTick: (): void => {
        // draw element
    },
});
ticker.start();
```

After (v2):

```typescript
import { Ticker, Time } from '@armniko/ticker';

const element: { position: { x: number, y: number } } = { position: { x: 0, y: 0 } };
const animation: { durationMs: number, distancePx: number } = {
    durationMs: 2000,
    distancePx: 500,
}
const ticker: Ticker = new Ticker();
ticker.addLogicTask((time: Time): void => {
    const pxPerMs: number = distancePx / animationDurationMs;
    element.position.x += pxPerMs * time.deltaMs;
});
ticker.addDrawTask((): void => {
    // draw element
});
ticker.start();
```

### Changelog

<table>
<tr>
    <td>v2.3.0</td>
    <td>
      Added time scale feature.<br>
      Minor performance improvements.<br>
      Fixed low-fps callback to be called gain after fps recovery.<br>
      Fixed edge case bug with a removing task where an incorrect task could be removed.<br>
    </td>
</tr>
<tr>
    <td>v2.2.0</td>
    <td>
      Removed minification of build.<br>
      Added <code>exports</code> field for proper module resolution and types.<br>
      Marked package as side-effect free.
    </td>
</tr>
<tr>
    <td>v2.1.1</td>
    <td>
        Updated packages.
    </td>
</tr>
<tr>
    <td>v2.1.0</td>
    <td>
        Added option to provide Time for TickerMock.<br>
        Migrated from webpack to vite.
    </td>
</tr>
<tr>
    <td>v2.0.0</td>
    <td>
        Multiple tick callbacks support.<br>
        Added TickerMock for testing.<br>
        Deprecated: constructor options, msBetweenTicks(), ticksMissed(). (See migration v1 -> v2)
    </td>
</tr>
<tr>
    <td>v1.1.0</td>
    <td>
        Precompiled UMD and ESM.
    </td>
</tr>
<tr>
    <td>v1.0.0</td>
    <td>
        Initial version.
    </td>
</tr>
</table>

---
_Source: https://npm.io/package/@armniko/ticker · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
