{"id":18568105,"url":"https://github.com/willybrauner/interpol","last_synced_at":"2026-02-23T00:27:39.403Z","repository":{"id":65813242,"uuid":"535309158","full_name":"willybrauner/interpol","owner":"willybrauner","description":"Interpolates values with a GSAP-like API ~ 3kB","archived":false,"fork":false,"pushed_at":"2025-03-13T09:43:16.000Z","size":1079,"stargazers_count":53,"open_issues_count":7,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-31T10:06:32.906Z","etag":null,"topics":["animation","interpolation","javascript","library","motion","typescript","utils"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/willybrauner.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-09-11T13:36:56.000Z","updated_at":"2025-03-13T09:43:17.000Z","dependencies_parsed_at":"2024-02-19T23:28:06.548Z","dependency_job_id":"fe3675de-e9aa-4166-8976-695bedbe5b9e","html_url":"https://github.com/willybrauner/interpol","commit_stats":{"total_commits":34,"total_committers":1,"mean_commits":34.0,"dds":0.0,"last_synced_commit":"374646796b58989cf74452cea6d4a1c9f9088e4e"},"previous_names":[],"tags_count":35,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/willybrauner%2Finterpol","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/willybrauner%2Finterpol/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/willybrauner%2Finterpol/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/willybrauner%2Finterpol/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/willybrauner","download_url":"https://codeload.github.com/willybrauner/interpol/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247640465,"owners_count":20971557,"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":["animation","interpolation","javascript","library","motion","typescript","utils"],"created_at":"2024-11-06T22:28:40.720Z","updated_at":"2026-02-23T00:27:39.392Z","avatar_url":"https://github.com/willybrauner.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003ch1 align=\"center\" style=\"text-align:center\"\u003eInterpol\u003c/h1\u003e\n\u003cp align=\"center\"\u003e\n\u003cimg alt=\"npm\" src=\"https://img.shields.io/npm/v/@wbe/interpol\"\u003e\n\u003cimg alt=\"npm-downloads-src\" src=\"https://img.shields.io/npm/dt/@wbe/interpol\"\u003e\n\u003cimg alt=\"build\" src=\"https://github.com/willybrauner/interpol/workflows/tests/badge.svg\"\u003e\n\u003c/p\u003e\n\u003cp align=\"center\"\u003e\n\u003cimg alt=\"logo\" src=\"https://github.com/willybrauner/interpol/blob/main/interpol.webp\" /\u003e\n\u003c/p\u003e\n\nInterpol library **interpolates a set of number values with a GSAP-like API**.\nThis is the lowest level of animate machine.\nInterpol don't come with dom API, it only provides real time progress of the interpolations that can be use or bind\non... mesh, dom element or anything else, for ~=3kB!\n\n\u003e [!IMPORTANT]\n\u003e Interpol still is in development, the API can change until the first major release.\n\n\u003e [!NOTE]\n\u003e For an in-depth discovery of the library, check out these articles:\n\u003e\n\u003e - [Codrops — Interpol: A Low-Level Take on Tweening and Motion](https://tympanus.net/codrops/2025/10/27/interpol-a-low-level-take-on-tweening-and-motion/)\n\u003e - [willybrauner.com — Interpol](https://willybrauner.com/journal/interpol)\n\n## Summary\n\n- [Summary](#summary)\n- [Install](#install)\n- [Basic usage](#basic-usage)\n  - [Interpol](#interpol)\n  - [Timeline](#timeline)\n  - [Timeline add callback \\\u0026 offsets](#timeline-add-callback--offsets)\n- [Props](#props)\n  - [Props types](#props-types)\n  - [Computed prop values](#computed-prop-values)\n- [Styles helper](#styles-helper)\n- [Easing](#easing)\n- [API](#api)\n  - [interpol constructor](#interpol-constructor)\n  - [Interpol methods](#interpol-methods)\n  - [Timeline constructor](#timeline-constructor)\n  - [Timeline methods](#timeline-methods)\n- [Options](#options)\n  - [Ticker](#ticker)\n    - [Disable internal raf](#disable-internal-raf)\n    - [Use the internal Ticker instance globally](#use-the-internal-ticker-instance-globally)\n  - [Defaults properties](#defaults-properties)\n- [Dev](#dev)\n- [Playground](#playground)\n- [Showcase](#showcase)\n- [Thanks to](#thanks-to)\n- [Credits](#credits)\n- [License](#license)\n\n## Install\n\n```shell\nnpm i @wbe/interpol\n```\n\n## Basic usage\n\n### Interpol\n\n`Interpol` instance usage:\n\n```js\nimport { Interpol } from \"@wbe/interpol\"\n\nnew Interpol({\n  v: [0, 100],\n  z: [-100, 200],\n  duration: 1000,\n  ease: (t) =\u003e t * t,\n  onUpdate: ({ v, z }, time, progress) =\u003e {\n    // Use updated `v` and `z` values on each frame\n  },\n  onComplete: ({ v, z }, time, progress) =\u003e {\n    // Interpol is complete\n  },\n})\n```\n\nIn this example:\n\n- The Interpol will start automatically\n- `v` will be interpolated between `0` and `100` during 1000 milliseconds\n- `z` will be interpolated between `-100` and `200` during 1000 milliseconds\n- `time` is the current time in millisecond\n- `progress` is the current percent progress between `0` and `1`\n\n### Timeline\n\nChaining interpol instancies with `Timeline`:\n\n```js\nimport { Interpol, Timeline } from \"@wbe/interpol\"\n\nconst tl = new Timeline()\n\ntl.add({\n  x: [0, 100],\n  duration: 750,\n  onUpdate: ({ x }, time, progress) =\u003e {},\n})\ntl.add({\n  foo: [100, 50],\n  bar: [0, 500],\n  duration: 500,\n  onUpdate: ({ foo, bar }, time, progress) =\u003e {},\n})\n```\n\nIn this example:\n\n- The timeline will start automatically\n- Interpol 1, will interpolate `x` value between `0` and `100` during 750 ms\n- Interpol 2, will start when Interpol 1 is complete and will interpolate `foo` \u0026 `bar` values during 500 ms\n\n### Timeline add callback \u0026 offsets\n\nA timeline can also be used to add callback function instead of Interpol instance or constructor.\n\n```ts\ntl.add(() =\u003e {\n  // do something when the timeline reaches this point\n})\n```\n\nPlus, you can set an offset to the callback to execute it at a specific time in the timeline.\nTwo types of offsets are available:\n\n- **Relative offset**: a string with a number and an operator, relative to the previous: `\"+=100\"` `\"-=100\"`\n- **Absolute offset**: a number in milliseconds, relative to the timeline start: `100`, `-100`, `0`\n\nA full example:\n\n```ts\nimport { Timeline } from \"@wbe/interpol\"\n\nconst tl = new Timeline()\n\ntl.add({\n  x: [-20, 100],\n  onUpdate: ({ x }, time, progress) =\u003e { ... },\n})\n\n// Set a callback as parameter instead of an Interpol instance or constructor\ntl.add(() =\u003e {\n  console.log(\"Timeline reached this point when previous add is complete\")\n})\n\n// Set a callback with an absolute offset (50ms in this case)\ntl.add(() =\u003e {\n  console.log(\"Timeline reached this point exactly 50ms after the beginning\")\n}, 50)\n\n// Set a callback with a relative offset (-50 in this case)\ntl.add(() =\u003e {\n  console.log(\"Timeline reached this point 50ms before the previous interpol end\")\n}, '-=50')\n```\n\n## Props\n\n### Props types\n\nFor more flexibility, there is three ways to define a single `prop`:\n\n```ts\nnew Interpol({\n  // 1. a simple number, implicite from is `0`\n  // to use only when `from` is `0`\n  x: 100,\n\n  // 2.1. an array\n  // [from, to]\n  x: [0, 100],\n\n  // 2.2. an array with multiples keyframes\n  // [keyframe1, keyframe2, ...]\n  x: [0, 100, -100, 50, 0],\n\n  // 3. an object with explicite `from` and `to` properties\n  // { from?, to, ease?, reverseEase? }\n  x: { from: 0, to: 100 },\n})\n```\n\n### Computed prop values\n\n`from` and `to` can be a number or a function that return a number.\nThree ways to define a `to` computed value on the same `prop` model:\n\n```ts\nnew Interpol({\n  // 1. number\n  x: () =\u003e Math.random(),\n\n  // 2. array\n  x: [0, () =\u003e Math.random()],\n\n  // 3. object\n  x: { from: 0, to: () =\u003e Math.random() },\n})\n```\n\nIn order to refresh computed values, you can use the `refresh` method:\n\n```ts\nconst itp = new Interpol({\n  // ...\n})\nitp.refresh()\n```\n\n## Styles helper\n\nOne of the main usage of Interpol is to animate DOM element style properties.\nThe API provide `styles`, a core helper function to simplify the DOM manipulation.\nThe function uses a DOM cache to associate multiple transformation functions with the same DOM element at the same time.\n\nDefinition:\n\n```ts\ndeclare const styles: (\n  element: HTMLElement | HTMLElement[] | Record\u003cany, number\u003e | null,\n  props: Record\u003cstring, string | number\u003e,\n  autoUnits: boolean = true,\n) =\u003e void\n```\n\nExample:\n\n```ts\nimport { Interpol, styles } from \"@wbe/Interpol\"\n\nnew Interpol({\n  x: [-100, 0],\n  y: [0, 50],\n  opacity: [0, 1],\n  onUpdate: ({ x, y, opacity }) =\u003e {\n    // set updated interpol values to the DOM element\n    styles(element, { x, y, opacity })\n    // Is Equivalent to:\n    // element.style.transform = `translate3d(${x}px, ${y}px, 0px)`\n    // element.style.opacity = opacity\n  },\n})\n```\n\n## Easing\n\n`ease` \u0026 `reversedEase` functions are used to interpolate the progress value. the default one is `(t) =\u003e t`.\n\n```js\nnew Interpol({\n  ease: (t) =\u003e 1 - Math.pow(1 - t, 4),\n})\n```\n\n[GSAP like ease functions](src/core/ease.ts) are available in Interpol as string or object:\n\n```js\nimport { Interpol, Power3 } from \"@wbe/interpol\"\n\n// as typed string\nnew Interpol({\n  ease: \"power3.out\",\n})\n\n// or, import the object\nnew Interpol({\n  ease: Power3.out,\n})\n```\n\n## API\n\n### interpol constructor\n\nFor mor details, see the full type definition [types.ts](src/core/types.ts)\n\n```ts\n// A Value can be a number or a computed number\ntype Value = number | (() =\u003e number)\n\n// The propsValues type can be a single number, an array or an object\nexport type PropsValues =\n  // 1. to\n  | Value\n  // 2. [from, to] or [keyframe1, keyframe2, ...]\n  | Value[]\n  // 3. { from, to, ease, reverseEase }\n  | Partial\u003c{ from: Value; to: Value; ease: Ease; reverseEase: Ease }\u003e\n\n/**\n * Interpol constructor\n */\ninterface IInterpolConstruct\u003cK extends keyof Props\u003e {\n  // inline props are an interpol list object, 3 definition types\n  // default: /\n  [x: string]: PropsValues\n\n  // Interpolation duration between `from` and `to` values (millisecond).\n  // ex: 1000 is 1 second\n  // default: `1000`\n  duration?: number | (() =\u003e number)\n\n  // Interpol easing function\n  // default: `t =\u003e t` (lineal easing)\n  ease?: (EaseName | EaseFn) | (() =\u003e EaseName | EaseFn)\n\n  // Overwrite easing function on reverse\n  // default: `ease`\n  reverseEase?: (EaseName | EaseFn) | (() =\u003e EaseName | EaseFn)\n\n  // Make interpol paused at start (not autoplay)\n  // default: `false`\n  paused?: boolean\n\n  // Add delay before first start\n  // default: `false`\n  delay?: number | (() =\u003e number)\n\n  // Enable debug to get interpol logs information\n  // default: `false`\n  debug?: boolean\n\n  // Called one frame before the interpol start with default params values\n  // default: /\n  onStart?: (props: Record\u003cK, number\u003e, time: number, progress: number, instance: Interpol) =\u003e void\n\n  // Called on frame update\n  // default: /\n  onUpdate?: (props: Record\u003cK, number\u003e, time: number, progress: number, instance: Interpol) =\u003e void\n\n  // Called when interpol is complete\n  // default: /\n  onComplete?: (\n    props: Record\u003cK, number\u003e,\n    time: number,\n    progress: number,\n    instance: Interpol,\n  ) =\u003e void\n\n  // Execute onUpdate method when the Interpol instance is create\n  // default: false\n  immediateRender?: boolean\n}\n```\n\n### Interpol methods\n\n```ts\nimport { Interpol } from \"@wbe/Interpol\"\n\nconst itp = new Interpol()\n\n// Play the interpol\n// play(from: number = 0): Promise\u003cany\u003e\nitp.play(from)\n\n// Reverse and play the interpol\n// reverse(from: number = 1): Promise\u003cany\u003e\nitp.reverse(from)\n\n// Pause the interpol\n// pause(): void\nitp.pause()\n\n// Resumes playing without altering direction (forward or reversed).\n// resume(): void\nitp.resume()\n\n// Stop the interpol, will reset time, delta and progress.\n// stop(): void\nitp.stop()\n\n// Compute 'from', 'to' 'duration', 'ease' \u0026 'delay' values if there are functions\n// refresh(): void\nitp.refresh()\n\n// Set progress to a specific value\n// progress(value: number, suppressEvents = true): void\n// value: number between 0 and 1\nitp.progress(progressValue)\n\n// Get current progress value\n// progress(): number\nitp.progress()\n```\n\n### Timeline constructor\n\n```ts\ninterface ITimelineConstruct {\n  // Executed on frame update\n  // default: /\n  onUpdate?: (time: number, progress: number) =\u003e void\n\n  // Executed on complete\n  // default: /\n  onComplete?: (time: number, progress: number) =\u003e void\n\n  // Enable debug to get timeline instance logs\n  // default: `false`\n  debug?: boolean\n\n  // Disable timeline autoplay\n  // default: `false`\n  paused: boolean\n}\n```\n\n### Timeline methods\n\n```ts\nimport { Timeline } from \"@wbe/Interpol\"\n\nconst tl = new Timeline()\n\n// add(interpol: Interpol | IInterpolConstruct, offset: number | string = \"0\"): Timeline\n// @param interpol: Interpol object | Interpol instance | () =\u003e void\n// @param offset:\n//  - relative to the previous interpol (string): \"+=100\", \"-=100\", \"100\", \"-100\"\n//  - absolute (number): 0 (from the tl beginning), 100\ntl.add(Interpol, offset)\n\n// start the timeline\n// play(from: number = 0): Promise\u003cany\u003e\ntl.play(from)\n\n// reverse and play the timeline\n// reverse(from: number = 1): Promise\u003cany\u003e\ntl.reverse(from)\n\n// paused the timeline, will keep time, delta and progress.\n// pause(): void\ntl.pause()\n\n// resume the timeline after pause.\n// resume(): void\ntl.resume()\n\n// stop the timeline, will reset time, delta and progress.\n// stop(): void\ntl.stop()\n\n// call refresh on each interpol instance\ntl.refresh()\n\n// set progress to a specific value\n// value is a number between 0 and 1\n// progress(value: number, suppressEvents = true, suppressTlEvents = true): void\ntl.progress(progressValue)\n\n// Get current progress value\n// progress(): number\ntl.progress()\n```\n\n## Options\n\nGlobal option Object is available to set property for each Interpol \u0026 Timeline instance.\n\n### Ticker\n\n#### Disable internal raf\n\nIt's possible to disable the internal raf and use your own raf callback if needed.\n\n```ts\nimport { engine } from \"@wbe/interpol\"\n\n// disable internal raf to use your own raf\nengine.ticker.disable()\nconst tick = (e) =\u003e {\n  // execute Ticker.raf() callback on your own raf\n  engine.ticker.raf(e)\n  requestAnimationFrame(tick)\n}\nrequestAnimationFrame(tick)\n```\n\n#### Use the internal Ticker instance globally\n\nThe internal Ticker instance is available for a global application use. You can add your own raf callback to the Ticker instance and choose the rank of the callback handler.\n\n```ts\nimport { engine } from \"@wbe/interpol\"\n\n// Set a new raf callback to the Ticker instance\nconst tickHandler = (t) =\u003e console.log(t)\nconst rank = 1\nengine.ticker.add(tickHandler, rank)\n// ...\nengine.ticker.remove(tick)\n```\n\n### Defaults properties\n\n```ts\nimport { engine } from \"@wbe/interpol\"\n// Set default duration factor for all interpol instances, 1 is millisecond / 1000 is second\nengine.durationFactor = 1\n// Set default duration for all interpol instances\nengine.duration = 1000\n// Set default easing for all interpol instances\nengine.ease = (t) =\u003e t * t\n```\n\n## Dev\n\n```shell\n# install dependencies\npnpm i\n\n# build and watch lib changes\npnpm run build:watch\n\n# start tests and watch\npnpm run test:watch\n```\n\n## Playground\n\nAll examples can be found in the [examples](./examples) folder. You can start them all together with:\n\n```shell\n# start dev server for all examples\npnpm run dev --open\n```\n\n## Showcase\n\nThese projects have been built with Interpol. PRs are open to add your project to this list.\n\n- [oggy-story.com](https://oggy-story.com)\n- [julienjussey.com](https://julienjussey.com)\n- [theoplawinski.com](https://www.theoplawinski.com)\n- [willybrauner.com](https://willybrauner.com)\n\n## Thanks to\n\n- [gsap](https://gsap.com)\n- [anime.js](https://animejs.com)\n- [animini](https://github.com/dbismut/animini)\n- [signal](https://github.com/zouloux/signal)\n\n## Credits\n\nInterpol is an open-source project created and maintained by [Willy Brauner](https://willybrauner.com).\n\n## License\n\nSee the LICENSE file for license rights and limitations (MIT).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwillybrauner%2Finterpol","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwillybrauner%2Finterpol","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwillybrauner%2Finterpol/lists"}