There are underlying reasons, much of which can be gathered under the umbrella term of "virtualisation". Modern computers use layers upon layers of software or hardware to present an interface pretending to each higher level that things are simpler than they really are.
These abstractions, however, are leaky, and almost all of them are leaky in the temporal sense. There's a pretend continuity or constant throughput that's not really there.
Everyone knows that operating systems run short time slices of processes on physical cores, so that each program can "pretend" that it runs continuously on bare metal. But of course, not really, so there are gaps in the flow of execution that can occasionally be perceived by end-users.
If that were the only sin of pretense, then that could be worked around, or carefully tuned, but the reality is that it's just one of many layers.
The garbage collector of managed languages (e.g.: JavaScript in Electron) pauses execution within the process too.
Even unmanaged languages have variable overheads when allocating or de-allocating from the shared heap.
The desktop window manager helps each application pretend that it has a rectangular surface from (0,0) to (w,h), when in reality that is transformed and overlaid. That can introduce a lot of variation, particularly because the DWM has its own threads and its own garbage or heap.
The video card in turn is no longer just a block of memory mapped into the address space of the program doing the drawing, but is its own little computer with cores, threads, schedulers, locks, clocks, delays, and so forth.
The display in turn might further delay things because it has complex overdrive or scaling logic, so it needs to buffer frames.
It's turtles all the way down.