Numerical instability introduced by very small and very big and always varying delta values. At its core it is something like running
pos += speed*delta
for every frame with delta having varying precision depending on what is rendered on the current frame.
The solution is to change this to run
pos += speed
at fixed intervals while still rendering every frame. This will fix any issues with delta timing, but it will force your animations to run at the interval you choose regardless of framerate. If you use something like 60Hz however it will be fine for the vast majority of users since 60Hz screens are everywhere, however you'd still be wasting time rendering unnecessary frames. One solution for this is to cap the framerate (vsync at 60Hz will do that but not everyone likes vsync for unrelated reasons) but a better solution is to interpolate the previous and current states using the interval between updates when rendering, so something like
with inbetween being calculated using something like
inbetween = (now - last_time)/(1000.0/interval)
(last_time becomes now in the next update - ie. when pos += speed is called)
The drawback with this is that in the worst case (inbetween=0) the visible output is one frame behind, though without any form of framecapping (including no vsync) it this should rarely be the case and with 60Hz intervals it shouldn't be visible even on 120Hz+ monitors. But if you want to avoid this you can have some systems use the current position instead of interpolating them, e.g. have the camera's direction in first person games bypass the interpolation so that when the user moves the mouse they get the most instant feedback.
Personally i've used this approach on my last engine and it has buttery smooth and instant response without any perceivable lag or issues even on a 120Hz CRT monitor (pretty much all modern flat panel PC monitors have response time issues that can hide frame latency issues, but nothing stays hidden on a fast CRT :-P).
Could you just allow variable delta between two bounds that you control like 1fps and 600fps and make sure that computations work at both capped extremes to avoid degenerate behavior? Or are there downsides to that approach as well?
Well, considering i've seen several games break even with 120fps (they were probably never tested at such high framerates), i'd say there are. Though the lower the cap, the less likely you are to see issues in such games (probably because the games were tested with those framerates).
But the tiny deltas are one issue. Another is that you get variable deltas and they can go from big to small (even capped) from one frame to another and this can still introduce numerical instability and harder to spot heisenbugs.
It might sound like a PITA to keep the previous state around but you really only need it for things that can't be calculated on the fly (e.g. position that can arbitrarily change) but others can be calculated (e.g. particles) and in the long term you are making a more robust system and saving yourself from having to chase after weird bugs. By having everything run at fixed updates you know that once you see something working, it'll keep working regardless of the framerate.
The solution is to change this to run
at fixed intervals while still rendering every frame. This will fix any issues with delta timing, but it will force your animations to run at the interval you choose regardless of framerate. If you use something like 60Hz however it will be fine for the vast majority of users since 60Hz screens are everywhere, however you'd still be wasting time rendering unnecessary frames. One solution for this is to cap the framerate (vsync at 60Hz will do that but not everyone likes vsync for unrelated reasons) but a better solution is to interpolate the previous and current states using the interval between updates when rendering, so something like with inbetween being calculated using something like (last_time becomes now in the next update - ie. when pos += speed is called)The drawback with this is that in the worst case (inbetween=0) the visible output is one frame behind, though without any form of framecapping (including no vsync) it this should rarely be the case and with 60Hz intervals it shouldn't be visible even on 120Hz+ monitors. But if you want to avoid this you can have some systems use the current position instead of interpolating them, e.g. have the camera's direction in first person games bypass the interpolation so that when the user moves the mouse they get the most instant feedback.
Personally i've used this approach on my last engine and it has buttery smooth and instant response without any perceivable lag or issues even on a 120Hz CRT monitor (pretty much all modern flat panel PC monitors have response time issues that can hide frame latency issues, but nothing stays hidden on a fast CRT :-P).