Understanding the Composition System in Anime.js: Managing Multiple Animations on the Same Property
The composition system in Anime.js resolves conflicts between concurrent animations by maintaining per-target-property linked lists, where replace tweens override or trim overlapping predecessors while blend tweens sum their deltas through a shared lookup tween.
The Anime.js library (juliangarnier/anime) implements a sophisticated composition system to handle scenarios where multiple animations target the same CSS property or attribute on a single element. Instead of simply overwriting values, the engine uses distinct composition types—defined in src/core/consts.js—to determine whether a new tween should replace existing ones or blend additively with them. This architecture ensures predictable behavior whether you are interrupting in-flight animations or layering cumulative effects.
Composition Types and Constants
The foundation of the system rests on three composition behaviors defined in src/core/consts.js at lines 40–45:
replace(default): New tweens overwrite or override previous tweens for overlapping time ranges.blend(additive): New tweens add their delta values to existing animations (e.g.,+=10syntax).none: The tween runs independently without affecting or being affected by other tweens on the same property.
Each tween carries a _composition flag that the engine checks when inserting the animation into the render pipeline.
Replace Composition and the Sibling Linked List
When a tween uses compositionTypes.replace, the system manages it through the composeTween function in src/animation/composition.js.
The Lookup Table Structure
The engine maintains two lookup tables to organize tweens:
lookups._rep: Stores linked lists of replace-type tweens per target-property pair.lookups._add: Stores linked lists of additive tweens.
These maps are instantiated at lines 45–50 in src/animation/composition.js. The helper getTweenSiblings (lines 58–68) retrieves the appropriate list for a given target and property.
Inserting and Sorting Tweens
When composing a replace tween, the engine inserts it into the sibling list using addChild, sorting by absolute start time via addTweenSortMethod:
if (tweenCompositionType === compositionTypes.replace) {
addChild(siblings, tween, addTweenSortMethod, '_prevRep', '_nextRep');
// Handle overlapping previous tweens...
}
The linked list uses _prevRep and _nextRep pointers to maintain the sequence.
Overriding Overlapping Animations
If a previous tween exists and its time range overlaps the new tween, the engine calls overrideTween(prevSibling) (lines 83–88) to resolve the conflict. The algorithm may trim the earlier tween's duration or completely override it, ensuring the new tween fully controls the property for its duration. For looped animations from different instances, the system specifically checks for intersections at lines 118–146 and overrides the overlapping portion to prevent conflicting values.
Blend (Additive) Composition and Delta Accumulation
Additive animations use compositionTypes.blend and follow a different path in composeTween:
} else if (tweenCompositionType === compositionTypes.blend) {
const additiveTweenSiblings = getTweenSiblings(tween.target, tween.property, '_add');
const additiveAnimation = addAdditiveAnimation(lookups._add);
// Create lookup tween, convert TO-value to delta...
}
Lookup Tweens and Base Values
The first time a property receives an additive tween, the system creates a lookup tween that holds the base (original) value. This lookup tween is marked with compositionTypes.replace internally so it behaves like a standard replace tween. The incoming additive tween then has its target value converted to a delta relative to this base:
// The tween contributes only its change amount
_fromNumber = lookupTween._fromNumber - toNumber;
_toNumber = 0;
The tween is linked into the additive sibling list using _prevAdd and _nextAdd pointers.
Aggregating Additive Values
Each frame, the function addAdditiveAnimation in src/animation/additive.js (lines 54–78) walks every additive property list and sums the _number (or _numbers for complex values) of all active sibling tweens. It writes the total back to the lookup tween's target value (lookupTween._toNumber = additiveValue), then forces a render of the temporary additive animation via render(animation, 1, 1, 0, tickModes.FORCE).
This design allows multiple += animations to run simultaneously, with their combined deltas applied to the element's base value.
Removing Tweens and Cleanup
When a tween is cancelled or completes, removeTweenSliblings (lines 68–99 in src/animation/composition.js) cleans the appropriate sibling list. For additive tweens, if the removed tween was the last child, the function also removes the lookup tween and deletes the map entry to prevent memory leaks.
Summary
- Composition types (
replace,blend,none) are defined insrc/core/consts.jsand determine how concurrent animations interact. - Replace tweens are stored in
lookups._replinked lists; overlapping tweens are overridden or trimmed byoverrideTweento ensure the latest animation takes precedence. - Blend tweens are stored in
lookups._addand use a hidden lookup tween to hold base values; deltas are summed each frame insrc/animation/additive.js. - Linked lists use
_prevRep/_nextRepfor replace tweens and_prevAdd/_nextAddfor additive tweens to maintain sort order. - Cleanup is handled by
removeTweenSliblings, which purges empty lists and lookup tweens when animations end.
Frequently Asked Questions
What happens when two animations target the same property in Anime.js?
When two animations target the same property, the composition system in Anime.js checks the _composition flag of the new tween. If both use the default replace type, the later tween overrides the earlier one for any overlapping time range, potentially trimming the first tween's duration via overrideTween. If both use blend, their delta values are summed each frame and applied to a shared lookup tween, creating a cumulative effect.
How do I create additive animations in Anime.js?
To create additive animations, use the blend composition type (often triggered by relative values like +=10 or -=20). The engine automatically creates a lookup tween to store the base value, converts your target value to a delta, and links the tween into the additive sibling list (lookups._add). Each frame, addAdditiveAnimation aggregates all active deltas and renders the combined result.
What is the difference between replace and blend composition in Anime.js?
Replace composition (the default) means a new tween takes full control of the property, overriding or trimming any overlapping tweens so only the latest value applies. Blend composition means the tween adds its change to existing values; the engine sums all active additive deltas and applies the total to the element's base value, allowing simultaneous animations to contribute to the final result.
Where does the composition logic live in the Anime.js source code?
The core composition logic resides in three key files: src/core/consts.js defines the composition type constants; src/animation/composition.js implements composeTween, getTweenSiblings, and overrideTween for managing sibling linked lists; and src/animation/additive.js handles the frame-by-frame aggregation of additive values via addAdditiveAnimation.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →