Tweening and Easing Functions for Games
What Tweening Actually Does
A tween takes four inputs: a starting value, an ending value, a duration, and an easing function. On each frame, it calculates what the current value should be based on how much time has elapsed. If you tween x from 0 to 100 over 500 milliseconds, and 250 milliseconds have elapsed, a linear easing function returns 50 (halfway). An ease-out function returns approximately 75 (the motion decelerates, so it covers more distance early and less late). The tween updates the target property with this calculated value, and the renderer draws the result.
The math behind a linear tween is simple interpolation: currentValue = start + (end - start) * (elapsed / duration). The fraction elapsed/duration, commonly called t, ranges from 0 at the start to 1 at the end. A linear tween plugs t directly into the formula. An eased tween runs t through the easing function first to get a modified progress value, then plugs that into the formula. If the easing function returns 0.75 when t is 0.5, the motion is 75% complete at the halfway point of the duration, producing the deceleration effect of ease-out.
Tweens operate on any numeric property. Position (x, y, z), rotation (angle in degrees or radians), scale (1.0 to 2.0), opacity (0 to 1), color channel values (0 to 255), audio volume (0.0 to 1.0), blur radius, border width, progress bar fill, camera zoom level, particle emission rate, anything that can be expressed as a number can be tweened. This universality makes tweening the most broadly applicable animation technique in game development.
The Core Easing Types
Linear moves at constant speed from start to end. The value changes by the same amount each frame. Linear easing looks mechanical because nothing in the real world moves at constant velocity; everything accelerates from rest and decelerates to a stop. Use linear for progress bars, loading indicators, and situations where the visual metaphor is a machine or timer rather than a physical object.
Ease-in starts slow and accelerates toward the end. The motion feels like an object picking up speed from a standstill, like a car pulling away from a traffic light. Mathematically, ease-in is typically t raised to a power: t squared (quadratic ease-in), t cubed (cubic ease-in), or t to the fourth power (quartic ease-in). Higher powers produce more dramatic acceleration. Ease-in is used for objects entering the scene from off-screen, elements that need to feel like they are building momentum, and transitions where you want the beginning to feel gentle.
Ease-out starts fast and decelerates toward the end. The motion feels like an object sliding to a stop, like a ball rolling across the floor and losing speed to friction. Mathematically, ease-out is 1 minus (1-t) raised to a power. Ease-out is the most commonly used easing in game UI because it makes elements feel like they arrive at their final position naturally. Menu panels, toast notifications, modal dialogs, and tooltip popups all benefit from ease-out because the fast start provides visual responsiveness while the deceleration provides a clean, settled landing.
Ease-in-out starts slow, accelerates through the middle, and decelerates at the end. This produces the most physically natural-looking motion because it mimics how real objects move: accelerate from rest, reach top speed, then decelerate to rest. Ease-in-out is ideal for camera pans (the camera should not start or stop abruptly), scene transitions, and any object moving from one position to another as a deliberate, complete action. The visual impression is smooth and controlled.
Specialized Easing Functions
Back easing overshoots the starting point (ease-in-back) or the ending point (ease-out-back) before reaching the target. Ease-out-back moves past the final value by about 10% and then settles back, creating an anticipation-release feel. This is excellent for elements popping into view: the slight overshoot makes the arrival feel bouncy and energetic. Ease-in-back pulls slightly backward before moving forward, creating an anticipation effect like pulling back a slingshot. Both use a parameterized overshoot amount, typically 1.70158 as the default (based on visual testing by Robert Penner, who popularized these curves).
Elastic easing oscillates around the target value like a spring. Ease-out-elastic overshoots, comes back, overshoots less, comes back, and settles. The visual effect is a springy, bouncy arrival that conveys energy and playfulness. This is used for character spawn animations, item drops, notification badges, and anything that should feel alive and physics-driven. The oscillation frequency and decay rate are configurable: more oscillations feel more springy, and faster decay feels more damped.
Bounce easing simulates a ball bouncing. Ease-out-bounce reaches the target, bounces back to a smaller height, bounces again to an even smaller height, and settles. Each bounce is a parabolic arc with decreasing amplitude. This is used for objects falling and landing, score counter animations, and any physics-inspired motion where the metaphor is something dropping from a height. Bounce easing is attention-grabbing, so use it sparingly; a screen full of bouncing elements feels chaotic.
Exponential and circular easing provide alternatives to the polynomial (quadratic, cubic, quartic) curves. Exponential easing uses 2 raised to a power, producing very gentle starts and very rapid finishes (or vice versa for ease-out). Circular easing uses a circular arc function, producing a different acceleration curve that some designers prefer for its smoother middle range. The visual differences between cubic, exponential, and circular are subtle at typical durations (200 to 500 milliseconds), but become apparent in longer animations where the shape of the acceleration curve is more visible.
Tween Libraries for Web Games
Tween.js is the lightweight standard for JavaScript tweening. It is under 10KB minified, has no dependencies, and provides a straightforward API: new TWEEN.Tween(object).to({x: 100}, 500).easing(TWEEN.Easing.Quadratic.Out).start(). You call TWEEN.update(time) in your game loop, and all active tweens advance. Tween.js includes all standard easing functions (linear, quadratic, cubic, quartic, quintic, sinusoidal, exponential, circular, elastic, back, bounce, each in in/out/inout variants), plus chaining, repeat, delay, and callbacks for onStart, onUpdate, and onComplete. For games that need simple, reliable tweening without a large library, Tween.js is the go-to choice.
GSAP (GreenSock Animation Platform) is the most feature-rich animation library for the web. GSAP provides tweening, timelines (sequenced and overlapping tween groups), scroll-triggered animations, SVG morphing, and a plugin ecosystem. For game development, GSAP's timeline feature is particularly useful for complex animation sequences like cutscenes, level intros, and multi-step UI transitions. GSAP is free for most uses and has a paid business license for specific commercial contexts. Its performance is excellent, consistently faster than CSS transitions for complex animations.
Built-in engine tweens are provided by Phaser, PixiJS (via plugin), and BabylonJS. Phaser's tween system is tightly integrated with the game loop and supports easing, duration, delay, repeat, yoyo (play forward then backward), and callbacks. Using the engine's built-in tweens is preferable to adding a separate library because it synchronizes with the game's update cycle and handles pause/resume automatically. BabylonJS provides Animation objects that handle tweening for 3D scene properties with full easing support.
Chaining, Sequencing, and Grouping
Complex animations are built from sequences of individual tweens. A notification banner might slide in from the top (ease-out, 200ms), wait 3 seconds, then slide out to the top (ease-in, 200ms). This three-step sequence is built by chaining tweens with delays: the slide-in starts immediately, the hold is a 3-second delay, and the slide-out begins after the delay ends.
Tween.js supports chaining with the .chain() method, where you create each tween separately and link them: slideIn.chain(hold).chain(slideOut). GSAP provides the timeline construct, which is more flexible: you define a timeline and add tweens at specific points with labels, offsets, and overlap control. A GSAP timeline can play multiple tweens simultaneously with staggered starts, which is useful for cascading menu item appearances (each item slides in 50ms after the previous one).
Grouping tweens runs multiple property changes simultaneously. Sliding a panel while fading it in and scaling it up is three tweens on the same object running at the same time. Most tween libraries handle this by specifying multiple target properties in a single tween call: tween.to({x: 0, opacity: 1, scale: 1}, 300). The library calculates all three property values on each update frame. This is more efficient than creating three separate tweens because it avoids triple the overhead of tracking, callback invocation, and completion checking.
CSS Transitions for Game UI
For game UI elements rendered in HTML (menus, HUD overlays, dialogue boxes, inventory screens), CSS transitions provide tweening natively without any JavaScript library. Adding transition: transform 0.3s ease-out to a CSS class makes any transform change animate smoothly over 300 milliseconds with ease-out easing. This includes translateX/Y for position, scale for size, and rotate for rotation.
CSS transitions run on the browser's compositor thread, separate from the main JavaScript thread where your game logic runs. This means CSS UI animations maintain a smooth 60fps even if the JavaScript thread is busy with physics calculations, AI updates, or garbage collection pauses. This is a significant advantage over JavaScript-driven tweens for UI elements, because game UI should never stutter even when the game itself is under heavy load.
The limitation of CSS transitions is that they only animate between two states: the current CSS values and the new CSS values. They cannot chain, sequence, or react to intermediate values. For multi-step UI animations (slide in, wait, slide out), CSS keyframe animations (@keyframes) or JavaScript-driven tweens are necessary. For single-state-change animations (show/hide, hover effects, active states), CSS transitions are simpler, more performant, and should be the default choice.
CSS custom easing with cubic-bezier() lets you create any easing curve. The cubic-bezier(0.25, 0.1, 0.25, 1.0) function defines a curve through two control points, and you can create ease-out-back, gentle ease-in, or any other curve shape by adjusting the control point coordinates. Online tools like cubic-bezier.com let you visually design curves and copy the CSS value. CSS also includes the built-in keywords ease, ease-in, ease-out, ease-in-out, and linear, which map to predefined cubic-bezier curves.
Timing and Duration Guidelines
Duration determines how an animation feels. Animations under 100 milliseconds feel instant and are perceived as binary (on/off) rather than animated. Animations between 150 and 300 milliseconds feel snappy and responsive, ideal for button feedback, tooltip appearances, and small UI state changes. Animations between 300 and 500 milliseconds feel deliberate and visible, appropriate for menu transitions and panel slides. Animations over 500 milliseconds feel slow and should only be used for large-scale transitions like scene changes or first-time onboarding reveals.
Google's Material Design guidelines recommend 200ms for small UI changes and 300ms for larger ones, which maps well to game UI. Game-specific animations often need to be faster: a damage flash should be 50 to 100 milliseconds because the player needs to register the hit and immediately return attention to gameplay. A screen shake should last 100 to 200 milliseconds to feel impactful without disorienting the player. A collectible pickup animation should be 150 to 250 milliseconds, long enough to see but short enough to not interrupt the game flow.
Yoyo tweens (animating forward then backward to the starting value) are useful for pulse effects, breathing animations, and attention-drawing highlights. A button that pulses to 1.1x scale and back to 1.0x scale on a loop draws attention without being as aggressive as a full animation. The yoyo duration should be 600 to 1200 milliseconds for a relaxed pulse or 200 to 400 milliseconds for an urgent pulse. Ease-in-out is the standard easing for yoyo tweens because the smooth acceleration and deceleration at both ends of the motion prevent jarring direction reversals.
Ease-out is the default easing for most game UI animations because it feels responsive (fast start) and natural (gentle landing). Use CSS transitions for HTML-based game UI to keep animations on the compositor thread and immune to JavaScript frame drops. For canvas-based game objects, Tween.js provides lightweight tweening, and Phaser and BabylonJS provide integrated tween systems. Duration should be 150 to 300ms for most UI animations, shorter for gameplay feedback, and longer only for major scene transitions.