Is there a way to determine the monitor's frame rate using JavaScript?

  1. Could JavaScript be used to determine the refresh rate of a monitor, typically set at 60Hz for most LCD monitors?
  2. Is there a method available to execute a function after a specific number of frames have passed?

There has been some curiosity about my reasons for needing this information. Here's the scenario: I am working on an animation that runs in a continuous loop, rendering frame by frame. It is crucial that each iteration synchronizes with the monitor's refresh rate to avoid tearing. Currently, I am using setTimeout(loop, 16) within the loop method, which is somewhat effective. The second parameter should ideally be set as 1/(refresh rate), hence why I posed these questions.

Answer №1

If you're working on modern browsers, one trick you can try is using window.requestAnimationFrame along with a simple callback to track the time between each call and calculate the frames per second (FPS).

Additionally, you can choose to skip rendering every few calls to adjust the frame rate as needed.

I've provided a basic example at http://jsfiddle.net/rBGPk/ - the calculations may not be perfect, but it should give you an idea of how to implement this concept.

Answer №2

The method outlined below calculates the FPS by measuring the time interval between two consecutive animation frames.

Caution: This approach may yield inaccurate results as a skipped animation frame can occur when the CPU is occupied with other tasks.

// Function to obtain FPS as a Promise
const getFPS = () =>
  new Promise(resolve =>
    requestAnimationFrame(t1 =>
      requestAnimationFrame(t2 => resolve(1000 / (t2 - t1)))
    )
  )

// Call the function to retrieve the FPS
getFPS().then(fps => console.log(fps));

Tips

Answer №3

Measure time interval between repaint events:

const getRepaintInterval = () => {
  return new Promise((resolve) => {
    requestAnimationFrame((t1) => {
      requestAnimationFrame((t2) => {
        resolve(t2 - t1);
      });
    });
  });
};

Alternatively, calculate the Frames Per Second (FPS) within a specific second:

const getFps = () => new Promise(resolve => {
    let repaint = 0;
    const start = performance.now();
    const withRepaint = () => {
        requestAnimationFrame(() => {
            if ((performance.now() - start) < 1000) {
                repaint += 1;
                withRepaint();
            } else {
                resolve(repaint);
            }
        });
    };
    withRepaint();
});

Or determine FPS using a designated start and end point:

const fpsHandler = () => {
    let repaint;
    let stop;
    let ret;
    let start;
    const init = () => {
        ret = undefined;
        stop = false;
        repaint = 0;
        start = performance.now();
    };
    init();
    const withRepaint = () => {
        requestAnimationFrame(() => {
            if (!stop) {
                repaint += 1;
                withRepaint();
            }
        });
    };
    return {
        start: () => {
            init();
            withRepaint();
        },
        end: () => {
            stop = true;
            if (!ret) ret = repaint / ((performance.now() - start) / 1000);
            return ret;
        }
    }
};

const { start, end } = fpsHandler();

Answer №4

This method is strong and reliable, utilizing the requestAnimationFrame feature.

function calculateFramesPerSecond(options){
    var requestFrame = window.requestAnimationFrame ||
        window.webkitRequestAnimationFrame ||
        window.mozRequestAnimationFrame;
    if (!requestFrame) return true; // Determine if "true" is returned;
                                    // set default FPS, display error message, etc...
    function checkFramerate(){
        if (index--) requestFrame(checkFramerate);
        else {
            // var result = 3*Math.round(count*1000/3/(performance.now()-start));
            var result = count*1000/(performance.now()- start);
            if (typeof options.callback === "function") options.callback(result);
            console.log("Calculated: "+result+" frames per second");
        }
    }
    if (!options) options = {};
    var count = options.count||60, index = count, start = performance.now();
    checkFramerate();
}

The higher the count, the more precise the FPS value will be, but it will also increase test duration.

You can implement additional logic for rounding to intervals like 15/12s, such as 24, 30, 48, 60, 120... FPS.


Here's the condensed version (rounded to 3 FPS):

function calcFPS(a){function b(){if(f--)c(b);else{var e=3*Math.round(1E3*d/3/(performance.now()-g));"function"===typeof a.callback&&a.callback(e);console.log("Calculated: "+e+" frames per second")}}var c=window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame;if(!c)return!0;a||(a={});var d=a.count||60,f=d,g=performance.now();b()}

Usage examples:

calcFPS(); // Simply logs output to the console (console log can be removed,
           // rendering this call unnecessary)

calcFPS({count: 30}); // Manually define the count (test duration should be around 500ms
                      // on a 60FPS display)

calcFPS({callback: useFPS}); // Specify a callback function to utilize
                             // the FPS value

var FPS = 0, err = calcFPS({count: 120, callback: fps => FPS = fps});
if (err) FPS = 30; 

Answer №5

In some previous answers, it was explained to use the requestAnimationFrame method and calculate the time difference between frames. Here is a solution that leverages the requestAnimationFrame function along with the timestamp already provided to the callback. In this case, there is no need for an additional call to performance.now().

var previousTimestamp, divInterval, divFPS;

const raf = window.requestAnimationFrame || window.mozRequestAnimationFrame ||
    window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;

const rafLoop = timestamp => {
    let interval = timestamp - previousTimestamp;
    let fps = 1000 / interval;
    divInterval.innerHTML = `Interval: ${interval}`;
    divFPS.innerHTML = `FPS: ${fps}`;
    previousTimestamp = timestamp;
    raf(rafLoop);
};

divInterval = document.getElementById('interval');
divFPS = document.getElementById('fps');

// Initializes the previousTimestamp variable and then calls the rafLoop function.
raf(timestamp => {
    previousTimestamp = timestamp;
    raf(rafLoop);
});
<div id="interval"></div>
<div id="fps"></div>

For another example with animation using the technique derived from this snippet, check out this CodePen link.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Tips on positioning a div based on the screen dimensions

In my table, there is an icon that reveals a chart as a popup when hovered over. This div is where the chart is displayed: <div id="chart-outer" style="@style" class="popup-chart close"> <h2 id="charttitle&q ...

Error SCRIPT438: The function 'formatCurrency' is not supported by this object

I'm encountering an "SCRIPT438: Object doesn't support property or method 'formatCurrency'"" error when attempting to format currency for cells within a jQuery datatable using the jQuery formatCurrency library. Below is the code snippe ...

Is there a simple method to automatically increase the version number of Mongoose documents with each update request?

I'm eager to utilize Mongooses document versioning feature with the "__v" key. Initially, I struggled with incrementing the version value until I learned that adding this.increment() when executing a query is necessary. Is there a method to have this ...

Delete an entry in a singular mapping in a one-to-one connection [TypeORM]

Is there a way to remove an index from a one-to-one relationship in TypeORM? @OneToOne(() => Customer, { cascade: true }) @JoinColumn({ name: 'customer', referencedColumnName: 'uid' }) customer: Customer I searched the d ...

Preventing Columns in SlickGrid from Being Reordered

Is there a way to prevent specific columns in SlickGrid from being reordered? I have tried looking for a solution but couldn't find one. Unlike the 'resizable' option, there doesn't seem to be an option for each column to allow or disal ...

Using window.location.replace() for redirection after AJAX call succeeds

Attempting to redirect the page after a successful ajax call, the code below is functional: $.ajax( { type: "POST", url: path, data: response1, contentType: "application/json", success: -> window.lo ...

Error: Unable to extract the 'id' property from 'this.props.Name' because it is undefined in ReactJS

Can you please assist me in resolving this issue? Error: Cannot destructure property 'id' of 'this.props.Name' as it is undefined. src/component/Detail.js file import React, { Component } from 'react'; import { Character } f ...

How can I implement a division animation with Javascript upon clicking a hyperlink?

I need help with animating a div when clicking on a link. Specifically, when clicking on the "About" link, the height of the div should change. Currently, the height is set to '0em' in the CSS file. I attempted to change the height using "documen ...

Transmit information across disparate components in Vue.js

This situation involves a Graph component displayed in the body of the page, allowing user modifications. Additionally, there is a Search component located in the header of the page. These two components are independent - Graph is exclusive to the body o ...

Confirmation dialog with user-defined button text

When the confirm prompt box is used, it typically displays "ok" and "cancel" buttons. I am looking to customize the label text for the buttons to read as Agree and Not Agree instead. If you have any suggestions on how to achieve this modification, please ...

Analyzing the functionality of Express with Mocha and Chai

Currently facing an issue with testing my express server where I am anticipating a 200 response. However, upon running the test, an error occurs: Test server status 1) server should return 200 0 passing (260ms) 1 failing 1) Test server statu ...

Can the Browser Handle Quick Sorting of Enormous XML Data?

Currently, our team is facing a problem due to server restrictions that are beyond our control. We are required to use an XML file and parse it using JavaScript/jQuery instead of utilizing a database for a job. Moreover, we do not have direct write access ...

The Tiny Scrollbar jQuery plugin experiences a malfunction when the defer attribute is added to the script tags in the javascript code

After successfully setting up the jQuery plugin, Tiny Scrollbar, I encountered an issue when I attempted to defer the loading of the necessary javascript files. Here is an example of the code snippet: <script type="text/javascript" src="https://ajax.g ...

My loop encountered an 'Undefined' error while attempting to retrieve an object from a JSON file

Hey everyone, I could use some help with a problem I'm having in my for loop. The issue is that I keep getting an "undefined" word in the output of my loop. I am trying to retrieve objects from a JSON file and the loop itself seems to be working corre ...

Initiating, halting, and rejuvenating a timer in JavaScript

Here is a simple code snippet where I'm experimenting with starting, stopping, and resetting a JavaScript timer. The goal is to have a timer running on a page that sends a message once it reaches the end of its countdown before restarting. The stop b ...

Loading a div using Ajax within a frame

My php page includes a div that is supposed to be populated by an Ajax call. function showcopay() { var apa = document.getElementById("alert_id").value; $("#copay").load('show_copay.php?pid='+apa); } The parent page of the div used to be ...

What are the steps to implement server side routing using react-router 2 and expressjs?

If you're looking to delve into server side rendering with react-router, the documentation linked here might fall short in providing a comprehensive understanding. In particular, it may not offer enough insights on how to leverage react-router 2 for r ...

Using Slick Slider and Bootstrap CSS to display text on top of images

Currently, I am utilizing the slick slider from and I want to overlay text on top of the image. I tried using bootstrap carousel-caption, which works well with any image. However, with slick slider, it seems like the text is not at the highest level as I ...

"Obtaining a three.js sprite within a Verold script - the ultimate guide

Greetings fellow users of stack overflow! I've recently been experimenting with the world editor known as verold, based on three.js. The features it offers are quite impressive, but I've encountered an issue with the scripting aspect. My curren ...

The v-list-group does not automatically expand sub-groups based on the path specified in the group prop

I have a navigation sidebar that includes nested v-list-groups. Based on the documentation, the "group" prop of v-list-group should expand based on the route namespace. To see more information, visit: https://vuetifyjs.com/en/components/lists/ While this ...