src/mainloop.js
function animate(timestamp) {
    // Run the loop again the next time the browser is ready to render.
    // We set rafHandle immediately so that the next frame can be canceled
    // during the current frame.
    rafHandle = requestAnimationFrame(animate);

    // Throttle the frame rate (if minFrameDelay is set to a non-zero value by
    // `MainLoop.setMaxAllowedFPS()`).
    if (timestamp < lastFrameTimeMs + minFrameDelay) {
        return;
    }

    // frameDelta is the cumulative amount of in-app time that hasn't been
    // simulated yet. Add the time since the last frame. We need to track total
    // not-yet-simulated time (as opposed to just the time elapsed since the
    // last frame) because not all actually elapsed time is guaranteed to be
    // simulated each frame. See the comments below for details.
    frameDelta += timestamp - lastFrameTimeMs;
    lastFrameTimeMs = timestamp;

    // Run any updates that are not dependent on time in the simulation. See
    // `MainLoop.setBegin()` for additional details on how to use this.
    begin(timestamp, frameDelta);

    // Update the estimate of the frame rate, `fps`. Approximately every
    // second, the number of frames that occurred in that second are included
    // in an exponential moving average of all frames per second. This means
    // that more recent seconds affect the estimated frame rate more than older
    // seconds.
    if (timestamp > lastFpsUpdate + fpsUpdateInterval) {
        // Compute the new exponential moving average.
        fps =
            // Divide the number of frames since the last FPS update by the
            // amount of time that has passed to get the mean frames per second
            // over that period. This is necessary because slightly more than a
            // second has likely passed since the last update.
            fpsAlpha * framesSinceLastFpsUpdate * 1000 / (timestamp - lastFpsUpdate) +
            (1 - fpsAlpha) * fps;

        // Reset the frame counter and last-updated timestamp since their
        // latest values have now been incorporated into the FPS estimate.
        lastFpsUpdate = timestamp;
        framesSinceLastFpsUpdate = 0;
    }
    // Count the current frame in the next frames-per-second update. This
    // happens after the previous section because the previous section
    // calculates the frames that occur up until `timestamp`, and `timestamp`
    // refers to a time just before the current frame was delivered.
    framesSinceLastFpsUpdate++;

    /*
     * A naive way to move an object along its X-axis might be to write a main
     * loop containing the statement `obj.x += 10;` which would move the object
     * 10 units per frame. This approach suffers from the issue that it is
     * dependent on the frame rate. In other words, if your application is
     * running slowly (that is, fewer frames per second), your object will also
     * appear to move slowly, whereas if your application is running quickly
     * (that is, more frames per second), your object will appear to move
     * quickly. This is undesirable, especially in multiplayer/multi-user
     * applications.
     *
     * One solution is to multiply the speed by the amount of time that has
     * passed between rendering frames. For example, if you want your object to
     * move 600 units per second, you might write `obj.x += 600 * delta`, where
     * `delta` is the time passed since the last frame. (For convenience, let's
     * move this statement to an update() function that takes `delta` as a
     * parameter.) This way, your object will move a constant distance over
     * time. However, at low frame rates and high speeds, your object will move
     * large distances every frame, which can cause it to do strange things
     * such as move through walls. Additionally, we would like our program to
     * be deterministic. That is, every time we run the application with the
     * same input, we would like exactly the same output. If the time between
     * frames (the `delta`) varies, our output will diverge the longer the
     * program runs due to accumulated rounding errors, even at normal frame
     * rates.
     *
     * A better solution is to separate the amount of time simulated in each
     * update() from the amount of time between frames. Our update() function
     * doesn't need to change; we just need to change the delta we pass to it
     * so that each update() simulates a fixed amount of time (that is, `delta`
     * should have the same value each time update() is called). The update()
     * function can be run multiple times per frame if needed to simulate the
     * total amount of time passed since the last frame. (If the time that has
     * passed since the last frame is less than the fixed simulation time, we
     * just won't run an update() until the the next frame. If there is
     * unsimulated time left over that is less than our timestep, we'll just
     * leave it to be simulated during the next frame.) This approach avoids
     * inconsistent rounding errors and ensures that there are no giant leaps
     * through walls between frames.
     *
     * That is what is done below. It introduces a new problem, but it is a
     * manageable one: if the amount of time spent simulating is consistently
     * longer than the amount of time between frames, the application could
     * freeze and crash in a spiral of death. This won't happen as long as the
     * fixed simulation time is set to a value that is high enough that
     * update() calls usually take less time than the amount of time they're
     * simulating. If it does start to happen anyway, see `MainLoop.setEnd()`
     * for a discussion of ways to stop it.
     *
     * Additionally, see `MainLoop.setUpdate()` for a discussion of performance
     * considerations.
     *
     * Further reading for those interested:
     *
     * - http://gameprogrammingpatterns.com/game-loop.html
     * - http://gafferongames.com/game-physics/fix-your-timestep/
     * - https://gamealchemist.wordpress.com/2013/03/16/thoughts-on-the-javascript-game-loop/
     * - https://developer.mozilla.org/en-US/docs/Games/Anatomy
     */
    numUpdateSteps = 0;
    while (frameDelta >= simulationTimestep) {
        update(simulationTimestep);
        frameDelta -= simulationTimestep;

        /*
         * Sanity check: bail if we run the loop too many times.
         *
         * One way this could happen is if update() takes longer to run than
         * the time it simulates, thereby causing a spiral of death. For ways
         * to avoid this, see `MainLoop.setEnd()`. Another way this could
         * happen is if the browser throttles serving frames, which typically
         * occurs when the tab is in the background or the device battery is
         * low. An event outside of the main loop such as audio processing or
         * synchronous resource reads could also cause the application to hang
         * temporarily and accumulate not-yet-simulated time as a result.
         *
         * 240 is chosen because, for any sane value of simulationTimestep, 240
         * updates will simulate at least one second, and it will simulate four
         * seconds with the default value of simulationTimestep. (Safari
         * notifies users that the script is taking too long to run if it takes
         * more than five seconds.)
         *
         * If there are more updates to run in a frame than this, the
         * application will appear to slow down to the user until it catches
         * back up. In networked applications this will usually cause the user
         * to get out of sync with their peers, but if the updates are taking
         * this long already, they're probably already out of sync.
         */
        if (++numUpdateSteps >= 240) {
            panic = true;
            break;
        }
    }

    /*
     * Render the screen. We do this regardless of whether update() has run
     * during this frame because it is possible to interpolate between updates
     * to make the frame rate appear faster than updates are actually
     * happening. See `MainLoop.setDraw()` for an explanation of how to do
     * that.
     *
     * We draw after updating because we want the screen to reflect a state of
     * the application that is as up-to-date as possible. (`MainLoop.start()`
     * draws the very first frame in the application's initial state, before
     * any updates have occurred.) Some sources speculate that rendering
     * earlier in the requestAnimationFrame callback can get the screen painted
     * faster; this is mostly not true, and even when it is, it's usually just
     * a trade-off between rendering the current frame sooner and rendering the
     * next frame later.
     *
     * See `MainLoop.setDraw()` for details about draw() itself.
     */
    draw(frameDelta / simulationTimestep);

    // Run any updates that are not dependent on time in the simulation. See
    // `MainLoop.setEnd()` for additional details on how to use this.
    end(fps, panic);

    panic = false;
}
function animate(timestamp) {
    // Run the loop again the next time the browser is ready to render.
    // We set rafHandle immediately so that the next frame can be canceled
    // during the current frame.
    rafHandle = requestAnimationFrame(animate);

    // Throttle the frame rate (if minFrameDelay is set to a non-zero value by
    // `MainLoop.setMaxAllowedFPS()`).
    if (timestamp < lastFrameTimeMs + minFrameDelay) {
        return;
    }

    // frameDelta is the cumulative amount of in-app time that hasn't been
    // simulated yet. Add the time since the last frame. We need to track total
    // not-yet-simulated time (as opposed to just the time elapsed since the
    // last frame) because not all actually elapsed time is guaranteed to be
    // simulated each frame. See the comments below for details.
    frameDelta += timestamp - lastFrameTimeMs;
    lastFrameTimeMs = timestamp;

    // Run any updates that are not dependent on time in the simulation. See
    // `MainLoop.setBegin()` for additional details on how to use this.
    begin(timestamp, frameDelta);

    // Update the estimate of the frame rate, `fps`. Approximately every
    // second, the number of frames that occurred in that second are included
    // in an exponential moving average of all frames per second. This means
    // that more recent seconds affect the estimated frame rate more than older
    // seconds.
    if (timestamp > lastFpsUpdate + fpsUpdateInterval) {
        // Compute the new exponential moving average.
        fps =
            // Divide the number of frames since the last FPS update by the
            // amount of time that has passed to get the mean frames per second
            // over that period. This is necessary because slightly more than a
            // second has likely passed since the last update.
            fpsAlpha * framesSinceLastFpsUpdate * 1000 / (timestamp - lastFpsUpdate) +
            (1 - fpsAlpha) * fps;

        // Reset the frame counter and last-updated timestamp since their
        // latest values have now been incorporated into the FPS estimate.
        lastFpsUpdate = timestamp;
        framesSinceLastFpsUpdate = 0;
    }
    // Count the current frame in the next frames-per-second update. This
    // happens after the previous section because the previous section
    // calculates the frames that occur up until `timestamp`, and `timestamp`
    // refers to a time just before the current frame was delivered.
    framesSinceLastFpsUpdate++;

    /*
     * A naive way to move an object along its X-axis might be to write a main
     * loop containing the statement `obj.x += 10;` which would move the object
     * 10 units per frame. This approach suffers from the issue that it is
     * dependent on the frame rate. In other words, if your application is
     * running slowly (that is, fewer frames per second), your object will also
     * appear to move slowly, whereas if your application is running quickly
     * (that is, more frames per second), your object will appear to move
     * quickly. This is undesirable, especially in multiplayer/multi-user
     * applications.
     *
     * One solution is to multiply the speed by the amount of time that has
     * passed between rendering frames. For example, if you want your object to
     * move 600 units per second, you might write `obj.x += 600 * delta`, where
     * `delta` is the time passed since the last frame. (For convenience, let's
     * move this statement to an update() function that takes `delta` as a
     * parameter.) This way, your object will move a constant distance over
     * time. However, at low frame rates and high speeds, your object will move
     * large distances every frame, which can cause it to do strange things
     * such as move through walls. Additionally, we would like our program to
     * be deterministic. That is, every time we run the application with the
     * same input, we would like exactly the same output. If the time between
     * frames (the `delta`) varies, our output will diverge the longer the
     * program runs due to accumulated rounding errors, even at normal frame
     * rates.
     *
     * A better solution is to separate the amount of time simulated in each
     * update() from the amount of time between frames. Our update() function
     * doesn't need to change; we just need to change the delta we pass to it
     * so that each update() simulates a fixed amount of time (that is, `delta`
     * should have the same value each time update() is called). The update()
     * function can be run multiple times per frame if needed to simulate the
     * total amount of time passed since the last frame. (If the time that has
     * passed since the last frame is less than the fixed simulation time, we
     * just won't run an update() until the the next frame. If there is
     * unsimulated time left over that is less than our timestep, we'll just
     * leave it to be simulated during the next frame.) This approach avoids
     * inconsistent rounding errors and ensures that there are no giant leaps
     * through walls between frames.
     *
     * That is what is done below. It introduces a new problem, but it is a
     * manageable one: if the amount of time spent simulating is consistently
     * longer than the amount of time between frames, the application could
     * freeze and crash in a spiral of death. This won't happen as long as the
     * fixed simulation time is set to a value that is high enough that
     * update() calls usually take less time than the amount of time they're
     * simulating. If it does start to happen anyway, see `MainLoop.setEnd()`
     * for a discussion of ways to stop it.
     *
     * Additionally, see `MainLoop.setUpdate()` for a discussion of performance
     * considerations.
     *
     * Further reading for those interested:
     *
     * - http://gameprogrammingpatterns.com/game-loop.html
     * - http://gafferongames.com/game-physics/fix-your-timestep/
     * - https://gamealchemist.wordpress.com/2013/03/16/thoughts-on-the-javascript-game-loop/
     * - https://developer.mozilla.org/en-US/docs/Games/Anatomy
     */
    numUpdateSteps = 0;
    while (frameDelta >= simulationTimestep) {
        update(simulationTimestep);
        frameDelta -= simulationTimestep;

        /*
         * Sanity check: bail if we run the loop too many times.
         *
         * One way this could happen is if update() takes longer to run than
         * the time it simulates, thereby causing a spiral of death. For ways
         * to avoid this, see `MainLoop.setEnd()`. Another way this could
         * happen is if the browser throttles serving frames, which typically
         * occurs when the tab is in the background or the device battery is
         * low. An event outside of the main loop such as audio processing or
         * synchronous resource reads could also cause the application to hang
         * temporarily and accumulate not-yet-simulated time as a result.
         *
         * 240 is chosen because, for any sane value of simulationTimestep, 240
         * updates will simulate at least one second, and it will simulate four
         * seconds with the default value of simulationTimestep. (Safari
         * notifies users that the script is taking too long to run if it takes
         * more than five seconds.)
         *
         * If there are more updates to run in a frame than this, the
         * application will appear to slow down to the user until it catches
         * back up. In networked applications this will usually cause the user
         * to get out of sync with their peers, but if the updates are taking
         * this long already, they're probably already out of sync.
         */
        if (++numUpdateSteps >= 240) {
            panic = true;
            break;
        }
    }

    /*
     * Render the screen. We do this regardless of whether update() has run
     * during this frame because it is possible to interpolate between updates
     * to make the frame rate appear faster than updates are actually
     * happening. See `MainLoop.setDraw()` for an explanation of how to do
     * that.
     *
     * We draw after updating because we want the screen to reflect a state of
     * the application that is as up-to-date as possible. (`MainLoop.start()`
     * draws the very first frame in the application's initial state, before
     * any updates have occurred.) Some sources speculate that rendering
     * earlier in the requestAnimationFrame callback can get the screen painted
     * faster; this is mostly not true, and even when it is, it's usually just
     * a trade-off between rendering the current frame sooner and rendering the
     * next frame later.
     *
     * See `MainLoop.setDraw()` for details about draw() itself.
     */
    draw(frameDelta / simulationTimestep);

    // Run any updates that are not dependent on time in the simulation. See
    // `MainLoop.setEnd()` for additional details on how to use this.
    end(fps, panic);

    panic = false;
}
function animate ( timestamp ) { // Run the loop again the next time the browser is ready to render. // We set rafHandle immediately so that the next frame can be canceled // during the current frame. rafHandle = requestAnimationFrame ( animate ) ; // Throttle the frame rate (if minFrameDelay is set to a non-zero value by // `MainLoop.setMaxAllowedFPS()`). if ( timestamp < lastFrameTimeMs + minFrameDelay ) { return ; } // frameDelta is the cumulative amount of in-app time that hasn't been // simulated yet. Add the time since the last frame. We need to track total // not-yet-simulated time (as opposed to just the time elapsed since the // last frame) because not all actually elapsed time is guaranteed to be // simulated each frame. See the comments below for details. frameDelta += timestamp - lastFrameTimeMs ; lastFrameTimeMs = timestamp ; // Run any updates that are not dependent on time in the simulation. See // `MainLoop.setBegin()` for additional details on how to use this. begin ( timestamp , frameDelta ) ; // Update the estimate of the frame rate, `fps`. Approximately every // second, the number of frames that occurred in that second are included // in an exponential moving average of all frames per second. This means // that more recent seconds affect the estimated frame rate more than older // seconds. if ( timestamp > lastFpsUpdate + fpsUpdateInterval ) { // Compute the new exponential moving average. fps = // Divide the number of frames since the last FPS update by the // amount of time that has passed to get the mean frames per second // over that period. This is necessary because slightly more than a // second has likely passed since the last update. fpsAlpha * framesSinceLastFpsUpdate * 1000 / ( timestamp - lastFpsUpdate ) + ( 1 - fpsAlpha ) * fps ; // Reset the frame counter and last-updated timestamp since their // latest values have now been incorporated into the FPS estimate. lastFpsUpdate = timestamp ; framesSinceLastFpsUpdate = 0 ; } // Count the current frame in the next frames-per-second update. This // happens after the previous section because the previous section // calculates the frames that occur up until `timestamp`, and `timestamp` // refers to a time just before the current frame was delivered. framesSinceLastFpsUpdate ++ ; /*
     * A naive way to move an object along its X-axis might be to write a main
     * loop containing the statement `obj.x += 10;` which would move the object
     * 10 units per frame. This approach suffers from the issue that it is
     * dependent on the frame rate. In other words, if your application is
     * running slowly (that is, fewer frames per second), your object will also
     * appear to move slowly, whereas if your application is running quickly
     * (that is, more frames per second), your object will appear to move
     * quickly. This is undesirable, especially in multiplayer/multi-user
     * applications.
     *
     * One solution is to multiply the speed by the amount of time that has
     * passed between rendering frames. For example, if you want your object to
     * move 600 units per second, you might write `obj.x += 600 * delta`, where
     * `delta` is the time passed since the last frame. (For convenience, let's
     * move this statement to an update() function that takes `delta` as a
     * parameter.) This way, your object will move a constant distance over
     * time. However, at low frame rates and high speeds, your object will move
     * large distances every frame, which can cause it to do strange things
     * such as move through walls. Additionally, we would like our program to
     * be deterministic. That is, every time we run the application with the
     * same input, we would like exactly the same output. If the time between
     * frames (the `delta`) varies, our output will diverge the longer the
     * program runs due to accumulated rounding errors, even at normal frame
     * rates.
     *
     * A better solution is to separate the amount of time simulated in each
     * update() from the amount of time between frames. Our update() function
     * doesn't need to change; we just need to change the delta we pass to it
     * so that each update() simulates a fixed amount of time (that is, `delta`
     * should have the same value each time update() is called). The update()
     * function can be run multiple times per frame if needed to simulate the
     * total amount of time passed since the last frame. (If the time that has
     * passed since the last frame is less than the fixed simulation time, we
     * just won't run an update() until the the next frame. If there is
     * unsimulated time left over that is less than our timestep, we'll just
     * leave it to be simulated during the next frame.) This approach avoids
     * inconsistent rounding errors and ensures that there are no giant leaps
     * through walls between frames.
     *
     * That is what is done below. It introduces a new problem, but it is a
     * manageable one: if the amount of time spent simulating is consistently
     * longer than the amount of time between frames, the application could
     * freeze and crash in a spiral of death. This won't happen as long as the
     * fixed simulation time is set to a value that is high enough that
     * update() calls usually take less time than the amount of time they're
     * simulating. If it does start to happen anyway, see `MainLoop.setEnd()`
     * for a discussion of ways to stop it.
     *
     * Additionally, see `MainLoop.setUpdate()` for a discussion of performance
     * considerations.
     *
     * Further reading for those interested:
     *
     * - http://gameprogrammingpatterns.com/game-loop.html
     * - http://gafferongames.com/game-physics/fix-your-timestep/
     * - https://gamealchemist.wordpress.com/2013/03/16/thoughts-on-the-javascript-game-loop/
     * - https://developer.mozilla.org/en-US/docs/Games/Anatomy
     */ numUpdateSteps = 0 ; while ( frameDelta >= simulationTimestep ) { update ( simulationTimestep ) ; frameDelta -= simulationTimestep ; /*
         * Sanity check: bail if we run the loop too many times.
         *
         * One way this could happen is if update() takes longer to run than
         * the time it simulates, thereby causing a spiral of death. For ways
         * to avoid this, see `MainLoop.setEnd()`. Another way this could
         * happen is if the browser throttles serving frames, which typically
         * occurs when the tab is in the background or the device battery is
         * low. An event outside of the main loop such as audio processing or
         * synchronous resource reads could also cause the application to hang
         * temporarily and accumulate not-yet-simulated time as a result.
         *
         * 240 is chosen because, for any sane value of simulationTimestep, 240
         * updates will simulate at least one second, and it will simulate four
         * seconds with the default value of simulationTimestep. (Safari
         * notifies users that the script is taking too long to run if it takes
         * more than five seconds.)
         *
         * If there are more updates to run in a frame than this, the
         * application will appear to slow down to the user until it catches
         * back up. In networked applications this will usually cause the user
         * to get out of sync with their peers, but if the updates are taking
         * this long already, they're probably already out of sync.
         */ if ( ++ numUpdateSteps >= 240 ) { panic = true ; break ; } } /*
     * Render the screen. We do this regardless of whether update() has run
     * during this frame because it is possible to interpolate between updates
     * to make the frame rate appear faster than updates are actually
     * happening. See `MainLoop.setDraw()` for an explanation of how to do
     * that.
     *
     * We draw after updating because we want the screen to reflect a state of
     * the application that is as up-to-date as possible. (`MainLoop.start()`
     * draws the very first frame in the application's initial state, before
     * any updates have occurred.) Some sources speculate that rendering
     * earlier in the requestAnimationFrame callback can get the screen painted
     * faster; this is mostly not true, and even when it is, it's usually just
     * a trade-off between rendering the current frame sooner and rendering the
     * next frame later.
     *
     * See `MainLoop.setDraw()` for details about draw() itself.
     */ draw ( frameDelta / simulationTimestep ) ; // Run any updates that are not dependent on time in the simulation. See // `MainLoop.setEnd()` for additional details on how to use this. end ( fps , panic ) ; panic = false ; }
The main loop that runs updates and rendering.

@param {DOMHighResTimeStamp} timestamp
The current timestamp. In practice this is supplied by
requestAnimationFrame at the time that it starts to fire callbacks. This
should only be used for comparison to other timestamps because the epoch
(i.e. the "zero" time) depends on the engine running this code. In engines
that support `DOMHighResTimeStamp` (all modern browsers except iOS Safari
8) the epoch is the time the page started loading, specifically
`performance.timing.navigationStart`. Everywhere else, including node.js,
the epoch is the Unix epoch (1970-01-01T00:00:00Z).

@ignore
The main loop that runs updates and rendering .