Because it's a surprisingly tricky topic on modern operating systems [1], and even trickier in web browsers (smooth scrolling was actually
much easier to achieve on hard-realtime systems like the 8- and 16-bit home computers of the 80's and early 90s).
TL;DR: if you base your animation- or scroll-speed on the 'raw' measured time between two frames, you'll get micro-stutter because it's pretty much impossible to obtain a non-jittery frame duration on modern operating systems or web browsers, all you can do is try to remove the noise via filtering, or 'align' your measured frame duration with the display refresh interval, which on some platforms cannot be queried.
In web browsers the most important problem is that you can't measure the exact frame duration (which is fallout from Spectre/Meltdown), or obtain a precise 'presentation timestamp', or even query the display refresh frequency.
Even in the native OS APIs that provide a presentation timestamp (like DXGI on Windows or CVDisplayLink on macOS) that timestamp has considerable jitter and has not much to do with the actual presentation time when the frame becomes visible to the user.
And as soon as you base your animation timings on such a jittery timestamp you'll get micro-stutter (the easiest way to get smooth animation is actually to assume a fixed frame duration, but then your animation speed will be tied to the display refresh rate).
It's often possible to eliminate the timing jitter with 'noise removal' filters or just tracking an average over the last couple dozen frames, but those then may behave funny in situations where the frame duration changes drastically (such as moving a window between displays with different refresh rate, or when rendering stops and then resumes because the window or browser tab is fully obscured and then becomes visible again).
PS: Raylib's frame pacing code on the web is also a bit on the crude side [2].
...e.g. it just sleeps for 16 milliseconds, and relies on ASYNCIFY to enable a traditional render loop in browsers. It would actually be better to use a frame callback via requestAnimationFrame (or the Emscripten wrapper function emscripten_request_animation_frame), but this means giving up the cross-platform 'own the game loop' application model. Not that requestAnimationFrame alone solves any of the above mentioned time jitter problems though.
[1] https://medium.com/@alen.ladavac/the-elusive-frame-timing-16...
[2[ https://github.com/raysan5/raylib/blob/f1007554a0a8145060797...