{"id":15388244,"url":"https://github.com/georapbox/timer","last_synced_at":"2026-03-11T13:17:53.133Z","repository":{"id":32941412,"uuid":"141688298","full_name":"georapbox/Timer","owner":"georapbox","description":"Minimal javascript library to create and manage timers in the browser","archived":false,"fork":false,"pushed_at":"2023-01-08T17:11:48.000Z","size":817,"stargazers_count":4,"open_issues_count":1,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-13T15:16:29.113Z","etag":null,"topics":["javascript","library","timer"],"latest_commit_sha":null,"homepage":"https://georapbox.github.io/Timer/","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/georapbox.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-07-20T08:53:53.000Z","updated_at":"2023-03-09T01:36:07.000Z","dependencies_parsed_at":"2023-01-14T22:46:09.504Z","dependency_job_id":null,"html_url":"https://github.com/georapbox/Timer","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/georapbox%2FTimer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/georapbox%2FTimer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/georapbox%2FTimer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/georapbox%2FTimer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/georapbox","download_url":"https://codeload.github.com/georapbox/Timer/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249145894,"owners_count":21220056,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["javascript","library","timer"],"created_at":"2024-10-01T14:56:07.273Z","updated_at":"2026-03-11T13:17:53.128Z","avatar_url":"https://github.com/georapbox.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![npm version](https://img.shields.io/npm/v/@georapbox/timer.svg)](https://www.npmjs.com/package/@georapbox/timer)\n[![npm license](https://img.shields.io/npm/l/@georapbox/timer.svg)](https://www.npmjs.com/package/@georapbox/timer)\n\n[demo]: https://georapbox.github.io/Timer\n[license]: https://github.com/georapbox/Timer/blob/master/LICENSE\n[changelog]: https://github.com/georapbox/Timer/blob/master/CHANGELOG.md\n\n# Timer\n\nA lightweight, drift-free timer library built for the browser — precise, pause-resumable, and easy to use.\n\n[API documentation](#api) \u0026bull; [Demo][demo]\n\n## Installation\n\n```sh\nnpm install --save @georapbox/timer\n```\n\nThe library is exported in ESM format. You can import it the following way:\n\n```js\nimport { Timer } from '@georapbox/timer';\n```\n\n## API\n\n### Constructor\n\n```js\nnew Timer({ elapsed, duration })\n```\n\n**Options**\n\n| Name | Type | Description |\n| ---- | ---- | ----------- |\n| `elapsed` | `number` | Initial elapsed time in milliseconds. Default: `0`. Must be ≥ `0`. |\n| `duration` | `number` | Total duration in milliseconds. Default: `Infinity`. Must be ≥ `0` or `Infinity`. |\n\n\n**Throws**\n\n- `TypeError` if either `elapsed` or `duration` is not a number.\n\n**Notes**\n\n- The timer is paused after construction; call `.start()` to begin.\n- Internally drift-free: time is computed from absolute timestamps, not accumulated deltas.\n\n### Instance Methods\n\nAll methods (except getters) return the instance for chaining.\n\n#### `start()`\n\nStarts or resumes the timer. Has no effect if the timer is already running or has reached its full duration (`elapsed \u003e= duration`).\n\n#### `stop()`\n\nPauses the timer. The elapsed time is preserved for resuming later.\n\n#### `reset()`\n\nStops and resets the timer to its initial elapsed time (the value passed at construction).\n\n#### `time()`\n\nReturns a snapshot of the timer's current time state:\n\n```js\nconst { elapsed, remaining } = timer.time();\n```\n\n- `elapsed` — elapsed time in ms\n- `remaining` — remaining time in ms (`Infinity` if `duration` is `Infinity`)\n\n#### `on(type, listener, options)`, `off(type, listener, options)`\n\nAttach, detach, event listeners for timer events.  \n(See [Events](#events) below for details.)\n\n### Static Methods\n\n#### `Timer.now()`\n\nReturns a high-resolution, monotonic timestamp in milliseconds.\nUses `performance.now()` when available, otherwise falls back to `Date.now()`.\n\n### Properties\n\n#### `elapsed`\n\nReturns the current elapsed time in milliseconds.\n\n#### `remaining`\n\nReturns the remaining time in milliseconds (`Infinity` if `duration` is `Infinity`).\n\n#### `running`\n\nReturns `true` if the timer is currently running; otherwise `false`.\n\n### Events\n\nThe Timer instance emits standard `Event` objects using the DOM [`EventTarget`](https://developer.mozilla.org/docs/Web/API/EventTarget) API to signal state changes during its lifecycle. You can subscribe using `.on()` and unsubscribe using `.off()` methods.\n\n| Event Name | Fired When | Notes |\n| ---------- | ---------- | ----- |\n| `start` | The timer starts or resumes after being stopped. | Fired once per start. |\n| `tick` | On each animation frame while running. | Frequency depends on the browser's `requestAnimationFrame` (≈60fps). |\n| `stop` | The timer is paused manually. | Elapsed time is preserved for resuming. |\n| `reset` | The timer is reset to its initial elapsed time. | Stops the timer if running. |\n| `finish` | The timer reaches its total duration. | Fired once automatically at completion. |\n\n\u003e [!NOTE]\n\u003e Events are standard `Event` objects (not `CustomEvent`s) emitted through the DOM `EventTarget` interface. They don't bubble through the document — they're scoped to the `Timer` instance itself.  \n\u003e To access timing data, call `.time()` or read `.elapsed` / `.remaining` from the event's `currentTarget`. \n\n\u003e [!IMPORTANT]\n\u003e When removing listeners with `.off()`, you must pass the **same function reference** you used with `.on()`.  \n\u003e Creating a new anonymous function won't remove the previous listener — this mirrors native `addEventListener` / `removeEventListener` behavior in the DOM.\n\n```js\nconst timer = new Timer({ duration: 5000 });\n\nconst onStart = () =\u003e console.log('Timer started');\nconst onTick = evt =\u003e {\n  const { elapsed, remaining } = evt.currentTarget.time();\n  console.log(`Elapsed: ${elapsed}ms, Remaining: ${remaining}ms`);\n};\nconst onFinish = () =\u003e console.log('Timer finished');\n\ntimer\n  .on('start', onStart)\n  .on('tick', onTick)\n  .on('finish', onFinish)\n  .start();\n\n  // Later...\n  timer.\n    off('start', onStart)\n    .off('tick', onTick)\n    .off('finish', onFinish);\n```\n\n### Usage Examples\n\n#### Basic Countdown\n\n```js\nconst timer = new Timer({ duration: 10_000 })\n  .on('tick', evt =\u003e {\n    const { remaining } = evt.currentTarget;\n    label.textContent = `${Math.ceil(remaining / 1000)}s`;\n  })\n  .on('finish', () =\u003e {\n    label.textContent = 'Done!';\n  });\n\ntimer.start();\n```\n\n#### Pause and Resume\n\n```js\ntimer.stop(); // Pause\n// ...later\ntimer.start(); // Resume\n```\n\n#### Reset\n\n```js\nconst t = new Timer({ elapsed: 2000, duration: 10_000 }); // start \"2s in\"\nt.start();\n// ...\nt.reset(); // back to 2000ms elapsed\n```\n\n#### Infinite timer\n\n```js\nconst infiniteTimer = new Timer({ duration: Infinity })\n  .on('tick', () =\u003e console.log('Timer is running indefinitely'))\n  .start();\n```\n\n## Changelog\n\nFor API updates and breaking changes, check the [CHANGELOG][changelog].\n\n## License\n\n[The MIT License (MIT)][license]\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgeorapbox%2Ftimer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgeorapbox%2Ftimer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgeorapbox%2Ftimer/lists"}