The goal of every video game is to present a situation to the user(s), accept their input, interpret those cues into actions, and calculate a new situation resulting from those actions. Games constantly repeat these steps, over and over again, until some end condition occurs (e.g., winning, losing, or going to bed). Unsurprisingly, this pattern corresponds to how a game engine is programmed.
The specifics depend on the game.
Some games drive this cycle with user input. Imagine you are developing a game like “find the differences between these two identical pictures”. These games present two images to the user; they accept their click (or touch); they interpret the input as success, failure, pause, menu interaction, etc.; finally, they compute an updated scene as a result of that input. The game loop follows the user’s input and stays asleep until they provide it. It’s more of a step-by-step approach that doesn’t require constant refreshing of every frame, only when the player reacts.
Other games require control over each of the smallest possible individual time periods. The same principles apply as above, with a slight addition: each frame of the animation continues the loop, and any change in user input is captured during the first available turn. This “once per frame” model is implemented in what is called the main loop. If your game loops based on time, this will be its authority that your simulations will follow.
But this may not require frame-by-frame control. Your game loop may be similar to the difference-finding example and be based on input events. It may require both input and simulated time. It may even loop on something completely different.
Modern JavaScript, as described in the following sections, fortunately makes it easy to develop an efficient main loop with a once per frame execution. Of course, your game will only be as optimized as you make it. If something looks like it should be attached to a rarer event, then it’s a good idea to break it out of the main loop (but not always).