Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use requestAnimationFrame instead of setInterval for the game loop #6

Open
andreban opened this issue Aug 16, 2022 · 5 comments
Open
Labels
enhancement New feature or request

Comments

@andreban
Copy link

requestAnimationFrame() (rAF) is a more performant and precise alternative to setInterval() run animation loops in JavaScript that is well supported across browsers. This talk has more details on why that is the case.

Switching the code to rAF should be straightforward:

  1. In the render member of the game data, declare a variable to track the last game update and another for the target delay between frames (1000 ms divided by the target FPS).
    render: {
        lastUpdate: null,
        frameInterval: 1000 / 30, // 30 FPS
    },

Then, change the main loop to use rAF. Whenever rAF is called before an update is due, skip the rendering code.

/**
 * Main loop
 */
function main() {
    const render = (timestamp) => {
        // If this is the first frame, or frameInterval has passed, render the frame.
        if (!data.render.lastUpdate ||
                (timestamp - data.render.lastUpdate > data.render.frameInterval)) {
            clearScreen();
            rayCasting();
            data.render.lastUpdate = timestamp;
        }

        // Schedule the next frame to be rendered.
        requestAnimationFrame(render);
    };
    requestAnimationFrame(render);
}

Let me know if this makes sense to you, and I'll be happy to write a pull request updating the code and tutorials.

@pjamroziak
Copy link

@andreban
Did you create any benchmarks or tests?
I'm asking, because more performant and precise isn't precise in current situation.

@andreban
Copy link
Author

andreban commented Sep 26, 2022

There are some good resources at http://creativejs.com/resources/requestanimationframe/ and https://web.dev/speed-rendering/ on why requestAnimationFrame() is recommended instead of setInterval().

The event loop talk in the first post also helps understand the difference between both - setTimeout and setInterval schedule tasks in the JavaScript event queue. Taking and running a task is independent from the screen refresh rate, and time parameter in those functions promise the next call will run after that amount of time - if passing 16ms, it means at least 16ms later, but that can be longer. As a result, it can lead to frames being rendered twice but the screen being updated once, or the screen being updated without any updates to the render at all.

requestAnimationFrame (rAF) sits in a different place on the event loop - it's invoked just after the browser updates the screen, guaranteeing it's going to be invoked once, and only once, per screen update.

Overall, using rAF for canvas animations is a modern web development best practice and, IMHO, would be great to be reflected on the tutorial.

@pjamroziak
Copy link

I fully understand you, but without any tests where we can compare it... It's just words.
It's like using const in loops, we shouldn't but we used it because overall performance is very similar (not in gamedev ofc).

The second question is it if we should connect the screen refresh rate to main loop rate. What will happen on 360hz? How we will throttle it?

@andreban
Copy link
Author

IMO, benchmarks are beyond the point here, given the differences between both APIs. For the sake of the argument, though, here's a simple demo (source) that illustrates the difference.

On my computer:

  • at 60 FPS, requestAnimationFrame runs solid at 60FPS, while setInterval floats around 58-63 FPS
  • at 300 FPS (max refresh rate for my monitor), requestAnimationFrame runs very close to 300, setInterval's timings seems to vary much more (it's quite hard to read)

On the second question, the timing needs to be controlled manually - the 2nd snipped in the first message on this issue shows how to handle it.

@vinibiavatti1 vinibiavatti1 added the enhancement New feature or request label Nov 9, 2022
@vinibiavatti1
Copy link
Owner

Hi @andreban, thanks to bring this case to us. I liked your demo, it shows the situation much better. I agree with your proposal, but I would keep the same logic for the simple and intermediary tutorials. Why? setInterval is more used and known by the devs then requestAnimationFrame IMO. I didn't want to make the basic tutorial looks more complex with this, so, I would propose to add a new page to the wiki (at the advanced group) with the steps to implement your soluction, and explain the difference and the reason of it.
What do you think? (In this case, I would like to have the PR with the new logic for the advanced raycasting.js, and if you can, the content of the page to me put into the tutorial).

Thanks a lot!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

3 participants