d3-timer

A synchronized timer queue that coordinates thousands of concurrent JavaScript animations with drift-corrected, frame-consistent timing.

Library
npm
v3.0.1
169stars
ISC

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
42/100Fair
Development Activity0
Maintenance20
Community68
Maturity60
Momentum20

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
66/100Good
Architecture82
Code Quality78
Innovation78
Learning Curve25

d3-timer is a small, dependency-free JavaScript module that replaces raw setTimeout and setInterval calls with a single, efficient timing queue. It powers D3’s animation and transition system, coordinating potentially thousands of concurrent timers against one shared clock so simultaneous animations stay visually synchronized rather than drifting apart over time.

Under the hood it batches all active timers onto a single requestAnimationFrame loop (falling back to setTimeout in non-browser environments), using a linked-list task queue and periodic clock-skew correction to keep timing accurate even when a tab is backgrounded or a callback runs slowly. The module exposes three primitives — timer, timeout, and interval — each returning a lightweight, restartable handle, giving applications precise control over animation lifecycles without paying the overhead of one native timer per animation.

What You Get

  • A timer() primitive for repeating callbacks driven by a shared animation-frame loop
  • A timeout() helper for one-shot delayed callbacks built on the same synchronized clock
  • An interval() helper for precisely-paced repeating callbacks that self-correct for drift
  • A now() function exposing a cached, frame-consistent clock reading shared across all active timers

Common Use Cases

  • Driving D3 transitions and chart animations that must stay visually in sync across many elements
  • Building custom animation loops (physics simulations, canvas/WebGL renderers) without manual requestAnimationFrame bookkeeping
  • Scheduling recurring background work with a lower-overhead, drift-corrected alternative to setInterval
  • Implementing delayed or debounced UI updates that need a consistent, testable shared clock

Under The Hood

Architecture The module is a tiny, single-responsibility state machine in src/timer.js: a singly linked list of Timer instances (taskHead/taskTail) serviced by one shared requestAnimationFrame (or setTimeout-fallback) loop, with wake(), poke(), nap(), and sleep() cooperating to schedule and drain the queue. src/timeout.js and src/interval.js are thin wrappers composed entirely on top of the core Timer/restart primitive rather than duplicating scheduling logic — timeout stops itself after one firing, interval reschedules itself by accumulating an incrementing total delay. All state lives in module-level closures, so the entire scheduling behavior is concentrated in one file; any change to the sleep()/wake()/poke() trio ripples through every timer an application has created.

Tech Stack Plain ES module JavaScript with zero runtime dependencies. Dev tooling is eslint 7, mocha 8, and rollup 2 (with rollup-plugin-terser) for building minified UMD bundles alongside the ESM source; the package ships dual main/module/jsdelivr/unpkg entry points and targets Node >=12 and browsers, using window.requestAnimationFrame when available. It’s designed as one of D3’s independent “d3-module” packages, usable standalone or as part of the full D3 bundle.

Code Quality A focused mocha test suite (six files) covers now, timer, timerFlush, timeout, and interval, exercising both input validation (throwing TypeError on non-function callbacks) and timing behavior (asserting elapsed time falls within a tolerance range, since exact timing can’t be asserted reliably). Tests and ESLint both run in GitHub Actions CI on every push and PR. There are no TypeScript types or JSDoc annotations in the source itself — type information for consumers comes from the separately maintained @types/d3-timer package on DefinitelyTyped.

API Design The public surface is deliberately minimal: timer, timeout, and interval all share a (callback, delay, time) signature that mirrors native setTimeout/setInterval, so adopting it requires almost no code change, and each returned handle exposes only restart()/stop(). There’s no configuration object or class to instantiate directly — calling a function returns a live handle — which keeps boilerplate at zero, though the absence of built-in types means editor autocomplete depends on the separate @types/d3-timer package rather than being self-documenting.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search