# tween.js tweens object properties and hands you the clock, the units and the cleanup

> tween.js is a dependency-free TypeScript tweening engine built on Robert Penner's equations. It interpolates numeric properties of plain objects, refuses to append CSS units or interpolate colors, and deliberately ships no animation loop, so every timing decision stays with the caller.

**tweenjs/tween.js** — JavaScript/TypeScript animation engine

- Repository: https://github.com/tweenjs/tween.js
- Website: https://tweenjs.github.io/tween.js/
- Stars: 10,149 · Forks: 1,394
- Language: TypeScript
- License: NOASSERTION
- Published: 2026-09-21 · Updated: 2026-09-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/tweenjs-tween-js

## No CSS units, no color blending, no excuses

The feature list is short enough to read as a contract, and two of its lines are refusals. tween.js does not take care of CSS units, with appending px given as the example, and it does not interpolate colors. What it does is tween properties of an object. So a tween reads start values from a plain object you pass in and writes new numbers back into that same object as time advances, which is why the opening example animates a bare coords object rather than an element. If your animation target is element.style.width, the value coming out of the tween is a number and the unit is your problem. That is a real cost for DOM work, and it is paid at every call site rather than once. Color fades are the same story: there is no channel, no hex parsing and no premultiplied alpha handling to lean on. The upside is that numeric data of any shape works, including arrays, which the example set covers with dedicated files.

## The engine runs no clock of its own

tween.js does not make its own animation loop, and the README says that plainly as a feature rather than a gap. Integration flexibility is the reason. In the opening snippet the tween is constructed with auto start turned off, the second argument to the constructor being false, so nothing advances until something drives it. That driver is your frame loop calling update on the tween with an elapsed time value, and the examples folder includes pause_tween and video_and_time entries that show the shape of that arrangement. The consequence is that loop ownership transfers to you completely. Pausing, resuming after the tab was hidden, cleaning up when the component unmounts, and dropping tween references so they can be collected are all code you write and none of them are provided. Readers who expect a self-running animation end up with a tween that sits at its start values forever.

## Easing math you can reuse without a Tween instance

Easing functions are reusable outside of Tween, and custom easing functions are supported, which separates the math from the animation. That split matters for testing and for runtime configurability: a curve can be selected from a data attribute or a settings object and handed to easing() as a plain function, rather than being baked into a tween chain at build time. The README names the source of the equations, optimised versions of Robert Penner's set, which tells you the curves are ports rather than inventions, and the examples include a graphs_custom_functions entry for the custom side. The limitation is about coverage, not accuracy. The README does not enumerate which easing names exist in the build, so the set of available curves has to be read out of the distributed bundle or the docs site rather than assumed from an easing cheat sheet. Penner has a long tail of curves, and guessing at names is a wasted afternoon.

## dist/ ships three entry points and an empty dependency list

The package manifest describes a library that adds nothing to your dependency tree. The dependencies object in package.json is empty. Three build outputs are declared, next to an empty dependency list:

```json
"main": "dist/tween.cjs",
"module": "dist/tween.esm.js",
"types": "dist/tween.d.ts",
"dependencies": {}
```

main is the CommonJS build, module is the ESM build, and types is the declaration file. An exports map points the import condition at the ESM file, the require condition at the CJS file, and types at the declaration file, so bundlers, Node and TypeScript each get the artifact they ask for. The published files array contains dist, README.md and LICENSE, meaning examples, test and source are not in the tarball. The repository root shows how the library is assembled before publishing: src holds TypeScript sources, tsconfig.json drives tsc, rollup.config.js bundles, and the build script starts by removing dist and .tmp and then runs node scripts/write-version.js before the compile steps. That version-writing step is the reason the manifest version and the in-bundle version cannot drift apart.

## Install methods end with a CDN tag two majors behind

The install section ranks methods from most to least recommended, and the top of that list is the npm route:

```bash
npm install @tweenjs/tween.js
```

After that you use import syntax. Without a build tool there are two variants. Either serve node_modules as part of the site and point a standard importmap tag at the ESM build:

```html
<script type="importmap">
	{
		"imports": {
			"@tweenjs/tween.js": "/node_modules/@tweenjs/tween.js/dist/tween.esm.js"
		}
	}
</script>
```

Module scripts then import the package by name. A third path links the importmap to the CDN instead of a local node_modules folder. The detail worth checking before you copy anything is the version in the unpkg URLs. Both CDN snippets are pinned to 23.1.3, while package.json declares 25.0.0 and the most recent release tag is v25.0.0. The two snippets also spell the URL differently, one with the /browse/ path segment and one without, and the README does not note a difference between them. So the no-build-tool path gives you an old engine by default, and readers who paste the snippet into a page are running 23.1.3 without being told.

## The license signal is MIT in the file and unresolved in the repository

There are two license signals and they do not agree. The license field in package.json is MIT, and the manifest also declares a bugs URL pointing at the GitHub issue tracker and keywords of tween and interpolation. The repository metadata for this project reports NOASSERTION instead, which is what an automated reader sees when the license is not one it recognises. A LICENSE file does sit at the repository root, and the published files array ships it, so the text exists somewhere. The consequence is procedural rather than alarming: anyone checking licenses in bulk sees an unresolved field, and anyone reading the tarball sees MIT. This is not legal advice, it is a note about where to look. Read the LICENSE file in the root before you vendor the code into a closed product, and do not rely on the repository metadata field alone, because the same field will read the same way on every mirror and package registry entry derived from it.

## main has moved on since the last published release

The maintenance picture is unusual enough to state precisely. The repository is not archived and the last push was on 2026-09-22. The three most recent releases are v25.0.0, v24.0.0 and v23.1.3, and all three were published on the same day, 2024-07-26, within about an hour of each other. package.json still says 25.0.0. So the branch keeps receiving commits while the newest npm tag is more than two years old, and a `npm install @tweenjs/tween.js` gives you 25.0.0 rather than whatever is at the tip of main. The test script makes that cheap to inspect: npm test runs the build, then the prettier check, then nodeunit against test/unit/nodeunitheadless.cjs, and the headless entry point suggests the suite does not need a browser to run. Contributors get their environment from CONTRIBUTING.md, and the Chinese README lives beside the English one as README_zh-CN.md. Bug reports and security issues are not pointed anywhere in the README itself, so the issue tracker URL in the manifest is the only route recorded there.

## Conclusion

Adopt tween.js when the thing you animate is numeric data you already control and you want easing without a 30 kilobyte framework's opinions. Skip it when the animation target is a DOM style that needs px or a hex color, because neither is handled. Before wiring it in, check three things against your own code: whether you already have a frame loop that can drive update, whether your easing choice has to be configurable at runtime, and whether a CDN copy of an old tag would ship to production unnoticed. If you fork the repo to publish under your own name, scripts/write-version.js and the release:* scripts are the parts that have to survive the fork.

## FAQ

### What is tween.js in one sentence?

It is a JavaScript and TypeScript tweening engine for animating properties, built on optimised Robert Penner equations, published as @tweenjs/tween.js with no runtime dependencies and no animation loop of its own.

### What is a tween in programming?

In tween.js terms a tween interpolates the numeric properties of a plain object from their starting values to target values across a duration, shaped by an easing equation, writing the current values back into that object as it advances.

### Does tween.js handle CSS units and colors?

No. The feature list states that it does not take care of CSS units, giving appending px as the example, and that it does not interpolate colors. You supply units and any color blending yourself.

### Which tween.js version do the CDN examples load?

Both unpkg snippets in the installation section are pinned to @tweenjs/tween.js@23.1.3, while package.json declares version 25.0.0 and the newest release tag is v25.0.0.

## Sources

- [Issues](https://github.com/tweenjs/tween.js/issues)
- [Project website](https://tweenjs.github.io/tween.js/)
- [README](https://github.com/tweenjs/tween.js/blob/main/README.md)
- [Releases](https://github.com/tweenjs/tween.js/releases)
- [tweenjs/tween.js on GitHub](https://github.com/tweenjs/tween.js)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/tweenjs-tween-js
