├── README.md ├── fpscounter.js ├── fpscounter.min.js └── index.html /README.md: -------------------------------------------------------------------------------- 1 | fpscounter 2 | ========== 3 | 4 | This is a simple in-browser fps counter, with no configuration or options. [View demo](http://rawgithub.com/pete-otaqui/fpscounter/master/index.html). 5 | 6 | 7 | Installation 8 | ------------ 9 | 10 | Simply drop the javascript file into your page: 11 | 12 | 13 | 14 | Or you can add it to any page you are currently browsing with a bookmarklet like this: 15 | 16 | javascript:(function(){var s=document.createElement('script');s.src='//rawgithub.com/pete-otaqui/fpscounter/master/fpscounter.js';document.querySelector('head').appendChild(s);})() 17 | 18 | 19 | Configuration 20 | ------------- 21 | 22 | fpscounter doesn't need any configuration to run, but you can supply some if you like. Simple create a global object called ```fpscounter_options``` before the fpscounter.js file is executed. You have the following configuration directives, defaults are shown: 23 | 24 | 32 | 33 | 34 | Assuming you've disabled ```auto_start```, simply call the ```fpscounter()``` from your code to manually start it: 35 | 36 | 39 | 40 | 45 | 46 | All options (except ```auto_start```) are the same between the ```fpscounter_options``` object and the argument passed to the ```fpscounter()``` function 47 | 48 | Notes 49 | ----- 50 | 51 | fpscounter displays the current fps as text, and a history graph (higher is better) for the last period of time. The history graph automatically scales to delta between the least and greatest values within the history being shown, and these numbers are also printed. 52 | 53 | 54 | License 55 | ------- 56 | 57 | This code is available under the [Creative Commons Attribution 3.0 Unported](http://creativecommons.org/licenses/by/3.0/deed.en_GB) license. 58 | -------------------------------------------------------------------------------- /fpscounter.js: -------------------------------------------------------------------------------- 1 | /* 2 | * fpscounter.js 3 | * 4 | * A simple in-browser fps counter, suitable for using with a bookmarklet 5 | * 6 | * @author Pete Otaqui 7 | * @url https://github.com/pete-otaqui/fpscounter 8 | * @license Creative Commons Attribution 3.0 Unported 9 | * @license http://creativecommons.org/licenses/by/3.0/deed.en_GB 10 | */ 11 | (function(global) { 12 | global.fpscounter = function(options) { 13 | 14 | // late binding for options > global.fpscounter_options > defaults 15 | options = options || {}; 16 | var globals = global.fpscounter_options || {}; 17 | 18 | var defaults = { 19 | remove_on_click: false, 20 | width: 100, 21 | height: 50 22 | }; 23 | Object.keys(defaults).forEach(function(key) { 24 | options[key] = options[key] || globals[key] || defaults[key]; 25 | }); 26 | 27 | 28 | // get the width height for repeated use 29 | var canvas_w = options.width, 30 | canvas_h = options.height; 31 | 32 | // create the new dom elements, the canvas context, the style 33 | var ele = document.createElement('div'); 34 | ele.className = 'fpscounter'; 35 | ele.style.width = canvas_w + 'px'; 36 | ele.style.height = canvas_h + 'px'; 37 | 38 | var canvas = document.createElement('canvas'); 39 | canvas.className = 'fpscounter-canvas'; 40 | canvas.width = canvas_w; 41 | canvas.height = canvas_h; 42 | 43 | var context = canvas.getContext('2d'), 44 | text_fps_x = canvas_w/2 - 14, 45 | text_fps_y = canvas_h/2 + 10, 46 | text_max_x = 4, 47 | text_max_y = 8, 48 | text_min_x = 4, 49 | text_min_y = canvas_h - 4, 50 | fps_font = 'bold 30px Monospace', 51 | min_max_font = '10px Monospace'; 52 | 53 | var gradient_fill = context.createLinearGradient(0,0,0,canvas_h); 54 | gradient_fill.addColorStop(0, '#001133'); 55 | gradient_fill.addColorStop(1, '#112288'); 56 | 57 | var gradient_line = context.createLinearGradient(0,0,0,canvas_h); 58 | gradient_line.addColorStop(0, '#2848d8'); 59 | gradient_line.addColorStop(1, '#3366ff'); 60 | 61 | context.lineWidth = 1; 62 | context.strokeStyle = gradient_line; 63 | 64 | 65 | var style = document.createElement('style'); 66 | style.textContent = '.fpscounter { '+ 67 | 'position: fixed; '+ 68 | 'top: 0; '+ 69 | 'right: 0; '+ 70 | 'background-color: #000; '+ 71 | 'color: #fff; '+ 72 | 'font-size: 30px; '+ 73 | 'font-family: monospace;'+ 74 | 'z-index: 999999'+ 75 | '}'; 76 | 77 | ele.appendChild(canvas); 78 | document.body.appendChild(ele); 79 | document.querySelector('head').appendChild(style); 80 | 81 | 82 | // initialize some timing and history variables 83 | var t_pre, t_now, u_pre, u_lim, 84 | h_arr = [], h_len = canvas_w, 85 | raf_request, raf_running; 86 | 87 | // we won't update anything more often than this many milliseconds 88 | u_lim = 100; 89 | 90 | // reduce an array of values to it members bounding values in the form [min, max] 91 | function h_reduce(memo, item) { 92 | if ( !memo[0] || item < memo[0]) memo[0] = item; 93 | if ( !memo[1] || item > memo[1]) memo[1] = item; 94 | return memo; 95 | } 96 | 97 | function checkfps() { 98 | var fps, c_min_max, c_min, c_delta, first_point, xy; 99 | raf_running = true; 100 | t_now = Date.now(); 101 | // this is where we throttle displayed updates 102 | if ( t_now >= u_pre + u_lim) { 103 | 104 | // get the fps for the history 105 | fps = Math.min(60, Math.round(1/(t_now-t_pre)*1000)); 106 | h_arr.unshift(fps); 107 | 108 | // do required math 109 | context.clearRect(0, 0, canvas_w, canvas_h); 110 | if ( h_arr.length > h_len ) h_arr.pop(); 111 | c_min_max = h_arr.reduce(h_reduce, []); 112 | c_min = c_min_max[0]; 113 | c_max = c_min_max[1]; 114 | c_delta = c_max - c_min; 115 | 116 | 117 | // draw the line graph 118 | context.fillStyle = gradient_fill; 119 | context.beginPath(); 120 | // first_point = fpsToPoint(0, h_arr[0], c_min, c_delta); 121 | context.moveTo(canvas_w, canvas_h); 122 | h_arr.forEach(function(fps_val, index) { 123 | xy = fpsToPoint(index, fps_val, c_min, c_delta); 124 | context.lineTo(xy[0], xy[1]); 125 | }); 126 | context.lineTo(xy[0], canvas_h); 127 | context.lineTo(canvas_w, canvas_h); 128 | context.fill(); 129 | context.stroke(); 130 | 131 | context.fillStyle = '#fff'; 132 | // write the main FPS text 133 | context.font = fps_font; 134 | context.fillText(fps, text_fps_x, text_fps_y); 135 | 136 | // write the limit texts 137 | context.font = min_max_font; 138 | context.fillText(c_min, text_min_x, text_min_y); 139 | context.fillText(c_max, text_max_x, text_max_y); 140 | 141 | // set the "update time" counter 142 | u_pre = t_now; 143 | } 144 | 145 | // set the "frame time" counter 146 | t_pre = t_now; 147 | 148 | // request another update later 149 | if ( raf_running ) { 150 | raf_request = requestAnimationFrame(checkfps); 151 | } 152 | } 153 | 154 | // convert an fps value to an [x,y] array 155 | function fpsToPoint(index, fps_val, min, delta) { 156 | return [ 157 | canvas_w - index, 158 | canvas_h - canvas_h * (fps_val - min) / delta 159 | ]; 160 | } 161 | 162 | // add removal event 163 | ele.addEventListener('click', function() { 164 | raf_running = !raf_running; 165 | if (raf_running) { 166 | start(); 167 | } else { 168 | cancelAnimationFrame(raf_request); 169 | if ( options.remove_on_click ) { 170 | document.body.removeChild(ele); 171 | } 172 | } 173 | }); 174 | 175 | // start 176 | function start() { 177 | t_pre = Date.now(); 178 | h_arr = []; 179 | u_pre = t_pre; 180 | checkfps(); 181 | } 182 | 183 | start(); 184 | }; 185 | 186 | // lots of negatives here because the assumption is we should start 187 | if ( !global.fpscounter_options || global.fpscounter_options.auto_start !== false) { 188 | global.fpscounter(); 189 | } 190 | 191 | })(window); -------------------------------------------------------------------------------- /fpscounter.min.js: -------------------------------------------------------------------------------- 1 | !function(a){a.fpscounter=function(b){function C(a,b){return(!a[0]||ba[1])&&(a[1]=b),a}function D(){var a,b,c,d,h;B=!0,v=Date.now(),v>=w+x&&(a=Math.min(60,Math.round(1e3*(1/(v-u)))),y.unshift(a),i.clearRect(0,0,e,f),y.length>z&&y.pop(),b=y.reduce(C,[]),c=b[0],c_max=b[1],d=c_max-c,i.fillStyle=r,i.beginPath(),i.moveTo(e,f),y.forEach(function(a,b){h=E(b,a,c,d),i.lineTo(h[0],h[1])}),i.lineTo(h[0],f),i.lineTo(e,f),i.fill(),i.stroke(),i.fillStyle="#fff",i.font=p,i.fillText(a,j,k),i.font=q,i.fillText(c,n,o),i.fillText(c_max,l,m),w=v),u=v,B&&(A=requestAnimationFrame(D))}function E(a,b,c,d){return[e-a,f-f*(b-c)/d]}function F(){u=Date.now(),y=[],w=u,D()}b=b||{};var c=a.fpscounter_options||{},d={remove_on_click:!1,width:100,height:50};Object.keys(d).forEach(function(a){b[a]=b[a]||c[a]||d[a]});var e=b.width,f=b.height,g=document.createElement("div");g.className="fpscounter",g.style.width=e+"px",g.style.height=f+"px";var h=document.createElement("canvas");h.className="fpscounter-canvas",h.width=e,h.height=f;var i=h.getContext("2d"),j=e/2-14,k=f/2+10,l=4,m=8,n=4,o=f-4,p="bold 30px Monospace",q="10px Monospace",r=i.createLinearGradient(0,0,0,f);r.addColorStop(0,"#001133"),r.addColorStop(1,"#112288");var s=i.createLinearGradient(0,0,0,f);s.addColorStop(0,"#2848d8"),s.addColorStop(1,"#3366ff"),i.lineWidth=1,i.strokeStyle=s;var t=document.createElement("style");t.textContent=".fpscounter { position: fixed; top: 0; right: 0; background-color: #000; color: #fff; font-size: 30px; font-family: monospace;z-index: 999999}",g.appendChild(h),document.body.appendChild(g),document.querySelector("head").appendChild(t);var u,v,w,x,A,B,y=[],z=e;x=100,g.addEventListener("click",function(){B=!B,B?F():(cancelAnimationFrame(A),b.remove_on_click&&document.body.removeChild(g))}),F()},a.fpscounter_options&&a.fpscounter_options.auto_start===!1||a.fpscounter()}(window); -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | FPS Counter 6 | 19 | 20 | 21 |
22 | 23 |

FPS Counter

24 | 25 |

View fpscounter on GitHub

26 | 27 |

This is a demo of the FPSCounter. Note - this counter has no configuration or setup, simply drop the js file into your page (or add it via bookmarklet: fpscounter).

28 | 29 |

Just watch the counter in the top right. Choose "mild" or "heavy" - which will repeatedly resize the test text as often as possible. With "nothing" happening, you should be seeing near 60 fps.

30 | 31 |

If you want, you can adjust the values for "mild" and "heavy", which controls the amount of times the text is resized within a loop.

32 | 33 |
34 | Process Weight Toggle 35 | 39 | 40 | 44 | 45 | 49 |
50 |
51 | Process Weight Values 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 63 |
64 | 65 |

THIS IS SOME TEST TEXT

66 | 67 |
68 | 69 | 89 | 90 | 91 | 92 | 93 | --------------------------------------------------------------------------------