Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Damped Springs (2012) (ryanjuckett.com)
60 points by ingve on June 3, 2023 | hide | past | favorite | 21 comments


I posted a much more concise derivation (and implementation, in just 16 lines) on HN a while back but didn't gain much traction:

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.


Have you tried reaching out to dang ([email protected])? One of my submissions was blocked and I reached out to him - he responded a while later to tell me that the domain was blacklisted (it was an article I'd written while working with a some org's PR dept. - but it helped to know why it was getting blocked).


> I'm hesitant about reposting as all of my submissions had been inexplicably blocked by HN filters

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.


> Because simulating damped springs requires calls to potentially expensive trigonometric and exponential functions…

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.


My thoughts are the same direction. This seems like a lot of analysis for something you ultimately don’t want, you want to integrate the camera motion together with the other physics integrations. Especially if you want the camera to be repulsed by objects in the game, which is typically what you want. This then is an n body problem with no analytical solution.


I was sort of hoping that the closed form analysis was going to be aimed at getting the critical damping factor to then plug it into a dumb Euler simulation so the behaviour is correct. Kinda disappointing that the final thing is so complicated.


if you are only every gonna use critical damping, you can do something like:

    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)


I can see an argument for wanting to go slightly to one side or the other of the critical factor for aesthetic reasons, but yes. If you want "critically-damped spring behaviour" you don't have to go the long way round to get it.


They are referring to the Euler-method and a Runge-Kutta-method (of which the simplest is called RK4) if you are curious:

https://en.wikipedia.org/wiki/Euler_method

https://en.wikipedia.org/wiki/Runge%E2%80%93Kutta_methods


Would love to see some gifs in this showing what it actually looks like in a game. Personally, I feel like the game feel is a lot more important than seeing the math equations


At OrgPad, we apply a similar approach to all animations in the 2D canvas. You can try it out even on our home page which is actually done as an interactive OrgPage: https://orgpad.com. The motion is much more natural and smooth, as if things had a bit of mass/ momentum.

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


> The motion is much more natural and smooth, as if things had a bit of mass/ momentum.

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.


Thank you for the feedback.

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.


It drops a few frames on my iPhone Xs. Even then it feels very responsive; I can be confident that it'll respond to my next touch right away. That makes it much better than most UIs I know, well done! Especially the expand/contract animations feel great.


Thank you for the feedback. iOS is unfortunately a tough platform to do web development for. There is something really bad going on in terms of memory consumption when rendering HTML/CSS - we had to convert a lot of the rendering to canvas to mitigate. In any case, sometimes the browser will exhaust more memory than some rather low limit and kill the page. The limit is different depending on what else is going on in iOS too. There is to the best of our knowledge no effort to notify the running web application using some kind of event so we cannot react programmatically to such situation (e.g. by switching to lower resolution rendering).


Could you just do

    pos = lerp(pos, target, 0.5)
or does that accelerate too fast (i.e. have the wrong curve)?


With that approach, if you're moving forward, the camera will always be catching up. That means majority of the screen will be taken up with what's behind you, whereas as a player you want to see what's in front of you. And that problem gets worse the faster your character is moving (which often happens when the character is falling)

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 :(


What if you just change the 0.5 factor based on some function of distance, like maybe the square or 1.5th power, or a hand-tweaked polynomial curve?

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.


Thanks for the link. util.ts is also very helpful.

Also, heh, slurp()


I find it a little disturbing that bungee jumps don't publish their expected maximum G force. This would help.


Really enjoyed reading this




Consider applying for YC's Winter 2026 batch! Applications are open till Nov 10

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: