Damped Springs (2012)
ryanjuckett.com
ryanjuckett.com
https://news.ycombinator.com/item?id=35899215
Here are the 16 lines in full:
function sprung_response(t,d,v,k,c,m)
local decay = c/2/m
local omega = math.sqrt(k/m)
local resid = decay*decay-omega*omega
local scale = math.sqrt(math.abs(resid))
local T1,T0 = t , 1
if resid<0 then
T1,T0 = math.sin( scale*t)/scale , math.cos( scale*t)
elseif resid>0 then
T1,T0 = math.sinh(scale*t)/scale , math.cosh(scale*t)
end
local dissipation = math.exp(-decay*t)
local evolved_pos = dissipation*( d*(T0+T1*decay) + v*( T1 ) )
local evolved_vel = dissipation*( d*(-T1*omega^2) + v*(T0-T1*decay) )
return evolved_pos , evolved_vel
end
And the embedded interactive demo in the blog post:https://www.desmos.com/calculator/ynbtai98ns
I'm hesitant about reposting as all of my submissions had been inexplicably blocked by HN filters, by the time someone vouches the post it has already been buried.
Almost exclusively submitting content from the same source might look like spam to the system. I‘d try submitting other interesting things that you come across too and see how that goes in the long term.
It doesn’t though if you go numerical and simulate with Euler or RK4. That approach also fits well to the application in a video game with discrete time steps. I wonder why the author chose to go with the closed form solutions.
function crit_response(dt,pos,vel,rate)
local dissipation = math.exp(-dt*rate)
local disspatePos = pos*dissiptation
local disspateVel = vel*dissiptation
local posCarried = 1 + dt*rate
local velCarried = 1 - dt*rate
local posFromVel = dt
local velFromPos = -dt*rate*rate
local newpos = posCarried*dissipatePos + posFromVel*dissipateVel
local newvel = velFromPos*dissipatePos + velCarried*dissipateVel
return newpos , newvel
end
(intentionally verbose for self-didactic purposes to show how you can actually split the dissipation step into its own loop out from the elastic calculation, meaning that you can use this as a more accurate initial guess in numeric simulations compared to just constant-acceleration approximations)We have an internal debugger that can be enabled using a keyboard shortcut to configure the anim framework live. There you can play with many things including the overall speed of animations, spring stiffness, damping and more. This proved to be useful in recording buttery smooth presentations in 4K@120 Hz - with slow animations, there is just more time to render. :-D
While I agree the motion itself feels fairly natural, there is a lag when you first start to move things around, which feels very weird. Once things start moving, they move as I expect them to, but only after a delay.
This is on Firefox 113.0.2 on Linux/X11 on a 4k@60Hz screen.
Unfortunately, Firefox (my daily driver for all things but OrgPad) is even slower than Chrome/ium for rendering. We, or mainly my colleague, will at some point rewrite most of the rendering into WebGL which should speed things up greatly. On the way there, we might rewrite even more stuff to canvas rendering but there are diminishing returns especially because font drawing in canvas is rather slow - we have to do a lot of caching which makes memory consumption a problem. Btw. you can probably confirm most of this using the browser profiler - you should see that OrgPad code usually occupies a fraction of the ~16 ms needed for smooth 60 Hz rendering. The rest is usually compositing on the GPU, something we can only affect by doing even more rendering on our own. How sad, in 2023 we are not able to draw a few hundred divs on a screen with absolute position without lag.
pos = lerp(pos, target, 0.5)
or does that accelerate too fast (i.e. have the wrong curve)?Reading through, I imagine this solution mitigates that somewhat by having velocity as a factor in it.
Another goal in the article is "Move exactly the same with respect to time regardless of frame rate.". Your solution solution doesn't work for variable frame rates in itself, but it's easy to modify it so it does. Here's an example of doing that, just involves calculating the 0.5 factor from a delta time: https://github.com/Jezzamonn/LD52/blob/b454ebdf9b2406e62ae48...
For its flaws, what you described is usually what I use in games, though I need to tweak it more. In the last game people said the camera made them feel dizzy :(
Also, heh, slurp()
I suspect something based on real physics would look subtly more natural, but not having a math education I suspect I'd probably try that first, maybe with the addition of some nonsense non-physically-inspired inertia modifier.