You should have two loops - multiplying things by fixed timestep scalars is not a viable option in a long term. All you need in basically just:
// NOTE Runs logic like calculations, transformations etc. 60 times per second
setTimeout(() => onLogicStep(), 1000 / 60);
// NOTE Adjusts objects on the scene to the last available logic step and renders to the screen
requestAnimationFrame(() => onRenderStep());
Don’t use requestAnimationFrame for logic loops since (a) it’s framerate dependent, (b) it stop whenever user minimises the browser or changes the tab.
I slightly better approach is to use Clock.getDeltaT or deltaT from the time passed into the animate callback, and move things by that amount… this gives you smooth motion but is less deterministic in how things update and some calculations like friction get trickier.
But an even better approach imo, is to use your fixed step approach, but then interpolate position and quaternion at render time, based on the time between the previous and current 60fps keyframes. That way you still only have to update your sim at 60fps but you can get smoother motion on higher refresh rate displays. You have to offset your rendertime by -(1/60) so you’re sim is 1 frame behind, but that gives you the 2 frames to tween between. Once you have that system, you can get away with surprisingly chunky physics update and still have it look and feel decent… like 16fps or lower… or even 10fps… which can then sometimes match your network tickrate… so then it makes it a little conceptually easier to do lockstep networking.