Achieving Sub-Frame Interpolation with GSAP and Web Workers
Browser animation timing is limited by the refresh rate (typically 60fps). But with a bit of creative hacking, we can simulate sub-frame interpolation — making animations feel smoother than the frame rate allows. This guide uses GSAP and Web Workers to blend physics-calculated positions with the current render frame, unlocking ultra-fluid motion. Why Sub-Frame Interpolation? Even with requestAnimationFrame, you’re capped at ~16.7ms updates. For high-speed or high-precision visuals (e.g. physics, UI drag), this isn’t enough. We’ll decouple physics updates and blend them at render-time for “in-between” values. Step 1: Spawn a Web Worker for Physics Updates This runs physics logic independently of the main render loop. // physics.worker.js let x = 0, v = 0.1; function step() { x += v; postMessage({ x }); setTimeout(step, 5); // ~200Hz physics loop } onmessage = (e) => { if (e.data === 'start') step(); }; Step 2: Set Up the Main Thread Animation Loop We'll interpolate between the last two physics values. const worker = new Worker('physics.worker.js'); let current = 0, previous = 0, alpha = 0; worker.onmessage = ({ data }) => { previous = current; current = data.x; }; worker.postMessage('start'); function animate(timestamp) { alpha += 0.05; if (alpha > 1) alpha = 1; const interpolated = previous * (1 - alpha) + current * alpha; gsap.set("#ball", { x: interpolated * 300 }); requestAnimationFrame(animate); } requestAnimationFrame(animate); Step 3: Render the Element Basic HTML + CSS to visualize it: ball { width: 30px; height: 30px; border-radius: 50%; background: hotpink; position: absolute; } How It Works The worker calculates the next physics state ~200 times per second, storing the last and current positions. On the main thread, we interpolate between those two values each frame to simulate in-between states. The result is smoother perceived motion without locking to the frame rate. Pros and Cons ✅ Pros Visibly smoother animations under heavy load Offloads calculations to Web Workers Works great for physics-based UI or motion trails ⚠️ Cons Added complexity with worker communication Interpolation logic must be carefully tuned Minimal benefit for low-speed animations
Browser animation timing is limited by the refresh rate (typically 60fps). But with a bit of creative hacking, we can simulate sub-frame interpolation — making animations feel smoother than the frame rate allows. This guide uses GSAP and Web Workers to blend physics-calculated positions with the current render frame, unlocking ultra-fluid motion.
Why Sub-Frame Interpolation?
Even with requestAnimationFrame
, you’re capped at ~16.7ms updates. For high-speed or high-precision visuals (e.g. physics, UI drag), this isn’t enough. We’ll decouple physics updates and blend them at render-time for “in-between” values.
Step 1: Spawn a Web Worker for Physics Updates
This runs physics logic independently of the main render loop.
// physics.worker.js
let x = 0, v = 0.1;
function step() {
x += v;
postMessage({ x });
setTimeout(step, 5); // ~200Hz physics loop
}
onmessage = (e) => {
if (e.data === 'start') step();
};
Step 2: Set Up the Main Thread Animation Loop
We'll interpolate between the last two physics values.
const worker = new Worker('physics.worker.js');
let current = 0, previous = 0, alpha = 0;
worker.onmessage = ({ data }) => {
previous = current;
current = data.x;
};
worker.postMessage('start');
function animate(timestamp) {
alpha += 0.05;
if (alpha > 1) alpha = 1;
const interpolated = previous * (1 - alpha) + current * alpha;
gsap.set("#ball", { x: interpolated * 300 });
requestAnimationFrame(animate);
}
requestAnimationFrame(animate);
Step 3: Render the Element
Basic HTML + CSS to visualize it:
How It Works
The worker calculates the next physics state ~200 times per second, storing the last and current positions. On the main thread, we interpolate between those two values each frame to simulate in-between states. The result is smoother perceived motion without locking to the frame rate.
Pros and Cons
✅ Pros
- Visibly smoother animations under heavy load
- Offloads calculations to Web Workers
- Works great for physics-based UI or motion trails
⚠️ Cons
- Added complexity with worker communication
- Interpolation logic must be carefully tuned
- Minimal benefit for low-speed animations