Physics simulation giving different results based on framerate

I’m using Cannon.js to simulate a rocket launch by applying a varying force upwards to a cone mesh.
I’ve got graphs printed of position, velocity and thrust but the position and velocity ones change depending on the device.
Here’s the live page : http://adam.teaches.engineering/Kerbciniak-Space-Program/

On my phone, the max height goes up to about 20 m. On my computer, it goes to about 70-80.

I’ve got an array of force values over time which you can see printed in the top graph. The force was originally measured at an interval of 1000 samples per second. I think this is where something is wrong because in each render loop, the index goes up by one which as far as the force array is concerned, has just happened in 1/1000 th of a second. But the renderloop is actually occurring much slower than that and so I’m using an index value that is based on the time between render loops. I’m not sure how to tackle this?

Here’s a piece of code from the render loop:

 let count = 0;
    const startTime = Date.now();
    engine.runRenderLoop(function () { 
            count += 1;
            const timeDiff = Date.now() - startTime;
            const i = Math.round(timeDiff)
            var forceDirection = new BABYLON.Vector3(0, 1, 0);
            var forceMagnitude = slicedRocketData[i] < 1 ? 0 : slicedRocketData[i] ;
            var contactLocalRefPoint = BABYLON.Vector3.Zero();

            particleSystem.emitRate = 1000 * forceMagnitude;

            var dragDirection = new BABYLON.Vector3(0, -1, 0);
            var dragMagnitude = CD * rho * (1/2) * cone.physicsImposter.getLinearVelocity().y ** 2;

            
            cone.physicsImposter.applyForce(forceDirection.scale(forceMagnitude), cone.getAbsolutePosition().add(contactLocalRefPoint));
            cone.physicsImposter.applyForce(dragDirection.scale(dragMagnitude), cone.getAbsolutePosition().add(contactLocalRefPoint));

            //console.log(`index = ${i}  Force = ${forceMagnitude} velocity = ${cone.physicsImposter.getLinearVelocity().y}  position = ${cone.getAbsolutePosition().y}`);

         
            scene.render();

I no nothing about a cannon, so I hope this will not be a crackpot answer. I do know about animation. If animation makes assumptions about how far something is to move turn every frame, then hardware that cannot keep up at the presumed frame rate will not move as fast.

Wide guess is, take out the timeDiff, & just have i increment 1 every frame. The faster machines should run the animation faster, but the results should be the same.

Really nice, btw

Alternative might be to have an independent i, which is stored for the next run. In a given run, compute a new i, then add / or avg all the slices that were since the last run.

Yeah it seems like on slower frame rates it’ll just skip certain I values which would then skip certain force values. Averaging sounds good although not as accurate.

I wonder if there’s a way to have an independent physics loop that runs at 1000 times per second and has the forces applied properly. Not sure how to code that in javascript…

Would I do something like :

frameRenderReady {
//do animation stuff here every frame
}

runPhysicsSim{

//apply forces here

set Timeout (1ms);

So the run physics sim gets called every 1 ms but when the frame is ready to render the frameRenderReady will be called to update the frame. Is that how the render function works?

This might be helpful:

https://doc.babylonjs.com/babylon101/animations#deterministic-lockstep

1 Like

Alright I’ve figured it out with the help of Adam and deterministic lockstep as well as a physics Observable.
Now the position of the rocket is consistent across multiple devices both fast and slow. Testes on phone, 60hz monitor and 144 hz monitor.

I set a deterministic lockstep and I also made sure to apply the forces in the supplied
" onBeforePhysicsObservable"

It doesn’t work for a timeStep of say 1ms thought. It just causes the rocket to launch super high and unrealistically. Shouldn’t a lower timestep just give more accurate results rather than completely blow up the simulation?