- Exponential smoothing एक सरल technique है जिसे toggle buttons, camera, UI elements, audio volume जैसी animations में व्यापक रूप से इस्तेमाल किया जा सकता है, जहाँ किसी target value की ओर smoothly follow करना होता है
- मुख्य formula है
position += (target - position) * (1 - exp(- speed * dt)), और यह केवल current position और target position के आधार पर target changes पर naturally react करता है
- Linear movement और सामान्य easing में बीच में click, तेज camera input, बड़े
dt पर jump, jitter, input queue जैसी handling की जरूरत पड़ती है, लेकिन exponential smoothing उसी structure से complexity घटाता है
1 - exp(- speed * dt) proportional update formula द्वारा solve किए जाने वाले differential equation के solution से आता है; छोटे dt में यह पुराने formula जैसा ही होता है और बड़े dt में भी overshoot से बचाता है
- यह mathematically target value तक ठीक-ठीक नहीं पहुँचता, लेकिन floating-point precision और user द्वारा detect की जा सकने वाली change की limits के कारण real animations में यह पर्याप्त रूप से complete जैसा behave करता है
Toggle button में दिखने वाली समस्या
- Toggle button के switch की position
turned_on ? max_x : min_x की तरह simply calculate की जा सकती है, लेकिन state बदलते ही position तुरंत teleport हो जाती है, जिससे liveliness की कमी लगती है
- Linear animation current position को constant speed से update करने और range को limit करने के तरीके से implement होती है
position.x += (turned_on ? 1 : -1) * speed * dt;
position.x = clamp(position.x, min_x, max_x);
- Constant speed movement में position time के respect में linear function बन जाती है, जिससे movement dull दिख सकता है
- Easing functions जोड़ने से movement को और smooth बनाया जा सकता है
- Classic cubic smoothstep:
3t² - 2t³
- Square-root easing:
sqrt(t)
smoothstep में 1 - f(t) = f(1 - t) symmetry होती है, इसलिए forward/backward animation के लिए वही code इस्तेमाल किया जा सकता है
sqrt के लिए direction के अनुसार अलग formula चाहिए
- On होने पर:
sqrt(t)
- Off होने पर:
1 - sqrt(1 - t)
sqrt शुरुआत में तेजी से move करता है और target के पास पहुँचने पर smoothly slow होता है, लेकिन simple 2-state toggle में भी t, direction, easing calculation जैसी state management की जरूरत पड़ती है
- अगर user animation के बीच में फिर से click करे, तो existing easing method position के अचानक jump जैसी discontinuity पैदा कर सकता है
Exponential smoothing formula
- Exponential smoothing target position तय करने के बाद current position को थोड़ा-थोड़ा target की ओर खींचने का तरीका है
target = (state.value ? max_x : min_x);
position.x += (target - position.x) * (1 - exp(- dt * speed));
- Update amount current position और target position के difference
target - position.x के आधार पर तय होता है
1 - exp(- dt * speed) हर frame में target की ओर कितना move करना है, यह तय करने वाला interpolation coefficient है
- बीच में target बदल जाए तब भी अलग progress time या animation direction state के बिना movement नए target की ओर तुरंत जारी रहता है
- Toggle button example में यह
sqrt easing की तरह तेजी से शुरू होकर target के पास धीमा होता है, और बीच में click होने पर jump की समस्या भी कम करता है
Camera movement में और बड़ा फायदा
- Map पर move करने वाले camera में भी यही समस्या दिखती है
- Constant-speed interpolation को सरलता से implement करें तो axis-wise target direction के sign का उपयोग होता है
position.x += sign(target.x - position.x) * speed * dt;
position.y += sign(target.y - position.y) * speed * dt;
- Animation complete होने की जगह पर
target - position का sign positive और negative के बीच आता-जाता है, जिससे jitter हो सकता है
- इसे रोकने के लिए
delta को max_delta range में limit करने वाला अलग update function चाहिए
float update(float & value, float target, float max_delta)
{
float delta = target - value;
delta = min(delta, max_delta);
delta = max(delta, -max_delta);
value += delta;
}
- Camera movement पर cubic easing apply करने के लिए requested movement events को queue में डालकर एक-एक करके process करना पड़ता है, जिससे structure complex हो जाता है
- Animation के दौरान user input ignore करने का तरीका user के लिए बहुत frustrating लगता है
- Exponential smoothing इस्तेमाल करने पर camera movement को toggle button जैसे लगभग उसी code से handle किया जा सकता है
position.x += (target.x - position.x) * (1.0 - exp(- speed * dt));
position.y += (target.y - position.y) * (1.0 - exp(- speed * dt));
- User जल्दी-जल्दी click करे तो target और current position के बीच का difference बढ़ जाता है, इसलिए camera naturally और तेज हो जाता है
1 - exp(- speed * dt) क्यों?
- Simple proportional update को निम्न formula से लिखा जा सकता है
position += (target - position) * speed * dt;
- इस formula में target और current position का difference जितना बड़ा होता है, movement उतनी तेज होती है, और current position व target position के अलावा extra state की जरूरत नहीं होती
lerp के रूप में लिखें तो भी यही formula बनता है
position = lerp(position, target, speed * dt);
speed * dt 0 के करीब हो तो movement slow होती है, और 1 के करीब हो तो target तक तेजी से approach करता है
speed * dt 1 से बड़ा हो जाए तो interpolation target को पार कर जाने वाला overshoot पैदा करता है
- Example में
speed = 220, dt = 1 / 125 होने पर speed * dt 1 से बड़ा हो जाता है और jitter दिखाई देता है
- अगर
speed * dt < 2 हो तो absolute difference घट सकता है, लेकिन speed * dt > 2 होने पर यह useful behavior नहीं कर पाता
- Interpolation coefficient को
min(1, speed * dt) से limit किया जा सकता है, लेकिन dt बढ़ने वाली situations को smooth तरीके से handle करने का यह solution नहीं है
- Code slow होने से frame rate गिर सकता है
- User किसी दूसरे tab या window में चला जाए तो code रुक सकता है और फिर कई seconds के
dt के साथ wake up हो सकता है
- Physics simulation में बड़े
dt को limit किया जा सकता है या multiple updates में बाँटा जा सकता है, लेकिन animation में camera और buttons का बड़े dt पर भी naturally काम करना user experience के लिए बेहतर है
Differential equation के रूप में solution
A += B * dt form आम तौर पर dA/dt = B नाम की differential equation के numerical solution के बराबर होता है
- Simple update formula निम्न equation solve करने जैसा है
d(position) / dt = (target - position) * speed
- Variables को
x = position, a = target, c = speed मानें तो यह इस तरह होगा
dx / dt = (a - x) * c
- इस equation को directly solve करने पर यह form मिलता है
x = x0 + (a - x0) * (1 - exp(-c * t))
- इसलिए अगर छोटे
dt में सही formula position += (target - position) * speed * dt है, तो arbitrary dt के लिए इस्तेमाल किया जा सकने वाला formula यह है
position += (target - position) * (1 - exp(- speed * dt));
- Taylor expansion में
exp(x) ≈ 1 + x होता है, इसलिए छोटे dt में 1 - exp(-speed * dt) ≈ speed * dt बनता है और पुराने simple formula जैसा हो जाता है
speed * dt बहुत बड़ा होने पर भी exp(-speed * dt) 0 के करीब जाता है और 1 - exp(...) 1 के करीब हो जाता है, जिससे यह stably target के करीब value तक move करता है
- वही formula
lerp से भी लिखा जा सकता है
position = lerp(position, target, 1 - exp(- speed * dt));
position = lerp(target, position, exp(- speed * dt));
Speed value चुनना
- आम animations को “0.125 seconds में move” जैसे duration के रूप में सोचना आसान है, लेकिन exponential smoothing में technically infinite time बीतने पर ही target तक ठीक-ठीक पहुँचा जाता है
exp(- speed * time) time के साथ छोटा होता जाता है लेकिन 0 नहीं होता, इसलिए start value और target value अलग हों तो position भी mathematically target के बिल्कुल बराबर नहीं होती
- व्यवहार में floating-point precision limit तक पहुँच जाता है, या camera position का difference user को न दिखने जितना छोटा हो जाता है, जिससे animation खत्म हुई जैसी दिखती है
speed का मतलब है कि 1 / speed time के दौरान position, target के exactly e = 2.71828... गुना करीब हो जाती है
- Real use में आम तौर पर
speed को 5..50 range में रखा जाता है, और same feel वाली linear/cubic animation speed के लिए exponential smoothing का लगभग 2 * speed ठीक लग सकता है
Signal processing से connection
- “exponential smoothing” या “exponential moving average” search करने पर Wikipedia का Exponential smoothing article मिल सकता है
- Fixed
dt में target हर iteration पर बदलता हो, तो value iteration index के आधार पर कुछ इस तरह update होती है
factor = 1 - exp(- speed * dt)
- आम तौर पर
factor को सीधे 0 और 1 के बीच की value के रूप में set किया जाता है
- Discrete exponential smoothing, animation में इस्तेमाल होने वाले तरीके का discrete analogue है
- Signal processing में भी यह इसलिए इस्तेमाल होता है क्योंकि previous values की list या complex state maintain किए बिना केवल current average value से काम कर लेता है
- Digital audio में आम तौर पर
dt sampling frequency के reciprocal 1 / freq के रूप में fixed होता है
- Example:
1/44100, 1/48000
1 टिप्पणियां
Hacker News की राय
लगता है कि यहाँ मुख्य बात पर्याप्त रूप से कवर नहीं हुई है। यह 0 और 1 के बीच की कोई और easing curve या
smoothstep()भर नहीं है, बल्कि लगभग किसी भी input को नियमित रूप से संभालने वाला stateless तरीका है, इसलिए सच में उपयोगी हैअगर आपने CSS transitions इस्तेमाल किए हैं, तो यह समस्या परिचित होगी। अगर duration 400ms रखा है, तो आखिर 400ms ही क्यों? क्या यह तय की जाने वाली दूरी पर निर्भर नहीं होना चाहिए?
जैसा दूसरों ने कहा, exponential smoothing में समस्या है कि यह destination के asymptotically करीब तो पहुँचता है, लेकिन कभी पहुँचता नहीं। जब step threshold से छोटा हो जाए तो animation रोक देने वाला obvious solution है, लेकिन वह elegant नहीं है
inertial scrolling में मिलते-जुलते तरीके का इस्तेमाल करते समय pseudo-friction term जोड़ना उपयोगी रहा। यह term exponential term को cancel कर देता है, इसलिए व्यवहार में minimum speed जैसा काम करता है। Desmos example यहाँ है: https://www.desmos.com/calculator/98ufbuzxhj
Dके लिए ordinary differential equation के रूप में समझें, तो शायद इसे हल किया जा सकता है। exponential smoothingdD/dt=-C*Dका Euler update है, और इसका solutionD(t)=A*exp(-C*t)है, इसलिए यह 0 के करीब asymptotically जाता है लेकिन पहुँचता नहींइसे ऐसी equation में बदल दें जो finite time में 0 तक जाती हो, जैसे
dD/dt=-C*sqrt(D)। solution आधे quadratic जैसा चलता है और 0 पर पहुँचने के बाद वहीं रहता है। इस equation का Euler update भी मनचाहे तरीके से stateless हैgame developer के रूप में, मुझे लगता है कि ज़्यादातर UI के लिए पहले से तय duration वाले easing tween बेहतर होते हैं। लेकिन जब starting point और end point साफ न हों और continuous, unpredictable motion को smooth बनाना हो, तब यह दूसरी किस्म का animation बहुत उपयोगी होता है
उदाहरण के लिए, जब player mouse से grid पर tile drag करे और उसे grid पर snap कराना हो, या लेख के example की तरह camera move करना हो
ऐसे मामलों में exponential interpolation trick बहुत उपयोगी है, लेकिन यह widely known नहीं है। बहुत से games ज्यादा inaccurate linear interpolation इस्तेमाल करते हैं और फिर 60fps के standard वाले दौर के उलट, जब कोई उसे 240Hz monitor पर चलाता है, तो animation का feel पूरी तरह अजीब हो जाता है
इसलिए यह लेख देखकर अच्छा लगा। ऐसी जरूरत से ज्यादा specific knowledge आम तौर पर team के senior से junior तक apprenticeship जैसी oral tradition से जाती है, इसलिए इसे access करना मुश्किल होता है
लेख अच्छा लगा, लेकिन toggle switch में
sqrtcubic function से बेहतर है—लेखक का यह judgement objectively गलत है, ऐसा कहना चाहूँगा। असली toggle switch आम तौर पर जैसे काम करते हैं, उसे देखें तो इस situation में cubic function बेहतर choice हैघर के circuit breaker switch या analog synth और audio equipment में अक्सर दिखने वाले switches के बारे में सोचिए। वे खास aesthetic को लक्ष्य करने वाले devices हैं, और मेरे पास मौजूद छोटे Hughes & Kettner guitar amp में भी दो switches हैं जिन्हें दबाने का feel अच्छा है
ऐसे switches में शुरुआत में थोड़ा resistance होता है, फिर spring mechanism की वजह से अचानक नई position में क्लिक करके चले जाते हैं। इस motion को
sqrtया exponential smoothing की तुलना में cubic function बेहतर model करता हैउस छोटी-सी आपत्ति को छोड़ दें तो लेख बहुत अच्छा था। यह अच्छी तरह दिखाता है कि सही easing function की तरह animation का अच्छा इस्तेमाल user experience को बेहतर बनाता है, लेकिन linear interpolation example की तरह अगर इसे सावधानी के बिना implement किया जाए, तो यह खटक सकता है और experience खराब कर सकता है
[0] यह toggle switch की type पर निर्भर करता है। फिर भी Minimoog जैसे उपकरणों में दिखने वाली किस्म भी “resist करके फिर नई position में क्लिक करना” वाला behavior दिखाती है, जिससे उसे operate करना मज़ेदार होता है। वैसे, यह gear bragging नहीं है; मेरे पास Minimoog नहीं है
यह देखकर लगातार हैरानी होती है कि simple nonlinear tricks online interactions में कितनी बार आनंद जोड़ देती हैं। color perception के मामले में तो यह समझने के लिए भी crucial है कि दो colors किसी व्यक्ति को पर्याप्त अलग क्यों नहीं दिखते
अजीब बात यह है कि इंसान हमेशा acceleration को अच्छी तरह नहीं समझते। यह मानकर कि आग flat ground की तरह लगभग constant speed से चलेगी, hill पर ऊपर की ओर भागना नहीं चाहिए। आग hill पर चढ़ते हुए accelerate करती है
बच्चे फेंकी हुई ball के जमीन के साथ-साथ चलने की speed जल्दी सीख लेते हैं, लेकिन gravity की वजह से वह हाथ से टकराते समय कितनी तेज होगी, यह हमेशा अच्छी तरह नहीं समझते
यह interesting है कि इस लेख का ज़्यादातर हिस्सा आखिरकार easing पर ही आकर टिकता है। लगता है हर नई generation को इसे खुद फिर से discover करना पड़ता है
मुझे 90s के आखिर में Yugo Nakamura की experimental website से fascinate होने की याद है। यह उन शुरुआती websites में से एक थी जो मैंने देखीं, जिसमें organic feel देने के लिए easing का freely use किया गया था: https://www.youtube.com/watch?v=NLt7Gwnt3WY
किसी कारण से लगता है कि ऐसा toggle अच्छा होगा। touch या click करके hold करने पर target के लगभग 75% तक धीरे-धीरे move करे, और छोड़ने पर बाकी हिस्सा क्लिक करके move हो जाए
UX के लिहाज से इसका क्या मतलब होगा, यह पक्का नहीं पता। शायद इसका मतलब हो कि setting अंत में वास्तव में apply या save होती है
या यह “क्या आप सच में sure हैं?” dialog का हिस्सा हो सकता है। hold करते समय setting apply हो, लेकिन अपनी जगह क्लिक करके बैठने से पहले Escape से वापस किया जा सके
input:checked + .slider:active:before { transform: translateX(8px); transition: 1s; }input:not(:checked) + .slider:active:before { transform: translateX(18px); transition: 1s; }यह लेख पसंद आया। करीब 10 साल पहले मैंने लगभग बिल्कुल यही तकनीक लिखी थी, तब उसे
lazy-easyकहा था और आज भी इस्तेमाल कर रहा हूंकभी-कभी आप पूरा state management किए बिना सिर्फ smooth animation चाहते हैं: https://www.hailpixel.com/articles/lazy-animation-with-lazy-...
लेख अपने-आप में सचमुच अच्छा है। demo Chrome में ठीक चलता दिखता है, लेकिन Firefox में scroll करते समय अटक जाता है और page rendering पूरी तरह रुक जाती है
असल में यह काफी अच्छा approach है, और animation/easing techniques के proof of concept के तौर पर भी अच्छा है। Flickity की बहुत याद आती है
https://metafizzy.co/blog/initial-demos/
https://metafizzy.co/blog/math-time-resting-position/
https://metafizzy.co/blog/particle-to-slider/
https://metafizzy.co/blog/flickity-begins/
खास तौर पर यह demo: https://codepen.io/desandro/pen/myXdej
यह तकनीक सिर्फ switches के लिए उपयोगी नहीं है। असल में आप switch में ऐसे elements इस्तेमाल भी नहीं करेंगे, और पूरी site पर
requestAnimationFrameloops के 20 instance parallel में भी नहीं चलाएंगे। आप जानबूझकर टूटा हुआ element भी नहीं डालेंगेसाथ ही, delta पर्याप्त छोटा हो जाने पर rendering रोकने वाला optimization भी नहीं है, और इसे production-level बनाने के लिए दर्जनों छोटे tweaks और भी हो सकते हैं
यहां की प्रतिक्रियाएं देखकर लगता है कि लोग लेख पढ़े बिना assumptions बना रहे हैं, पेड़ों में जंगल नहीं देख पा रहे हैं, या इतने biased और cynical हैं कि smart दिखने के लिए गैर-विशेषज्ञ फैसले शेयर कर रहे हैं
HN कब से Reddit जैसा हो गया?
HN के Reddit जैसा बन जाने वाली बात पर, शायद tech overall में एक vague cynical माहौल है। इसके पीछे यह अविश्वास है कि governments और companies उभरती शक्तिशाली technologies का इस्तेमाल कैसे करना चाहती हैं—क्या वे इन्हें व्यक्ति की freedom बढ़ाने के बजाय व्यक्तियों को सीमित और manage करने के इरादे से बना रही हैं
Emotional Design(https://en.wikipedia.org/wiki/Emotional_Design) के सार के काफी करीब है। बहुत छोटी animation के पीछे भी कहने को बहुत कुछ होता है