├── README.md
├── assets
├── screenshot-fps.png
├── screenshot-mb.png
├── screenshot-ms.png
└── screenshot-ping.png
├── index.html
├── license
├── package.json
└── stats.js
/README.md:
--------------------------------------------------------------------------------
1 | [](https://www.npmjs.com/package/@jordandelcros/stats-js)
2 |
3 | # stats.js #
4 |
5 | #### JavaScript Performance Monitor ####
6 |
7 | This class is a rework of the original `stats.js` from mrdoob.
8 |
9 | It use a single canvas unlike the original that use DOM elements.
10 |
11 | So, it provides a simple info box that will help you monitor your code performance.
12 |
13 | * **FPS** Frames rendered in the last second. The higher the number the better.
14 | * **MS** Milliseconds needed to render a frame. The lower the number the better.
15 | * **MB** MBytes of allocated memory. (Run Chrome with `--enable-precise-memory-info`)
16 | * **PING** Milliseconds needed to ask request something to the server. The lower the number the better.
17 |
18 | ### Screenshots ###
19 |
20 | 
21 | 
22 | 
23 | 
24 |
25 | ### NPM ###
26 |
27 | ```bash
28 | npm install @jordandelcros/stats-js
29 | ```
30 |
31 | ### Usage ###
32 |
33 | `Stats([realTime]);`
34 |
35 | ```javascript
36 | var stats = new Stats(false);
37 |
38 | document.body.appendChild(stats.domElement););
39 |
40 | function update() {
41 |
42 | window.requestAnimationFrame(update);
43 |
44 | stats.begin();
45 |
46 | // monitored code goes here
47 |
48 | stats.end();
49 |
50 | };
51 |
52 | window.requestAnimationFrame(update);
53 | ```
54 |
55 | to get the **PING** you need to call special methods into server's requests:
56 |
57 | ```javascript
58 | // ...
59 |
60 | function fakeServerRequest(){
61 |
62 | // Call on server request send
63 | stats.beginPing();
64 |
65 | // Fake server latency
66 | setTimeout(function(){
67 |
68 | // Call on server request response
69 | stats.endPing();
70 | fakeServerRequest();
71 |
72 | }, 65);
73 |
74 | };
75 |
76 | fakeServerRequest();
77 |
78 | // ...
79 | ```
--------------------------------------------------------------------------------
/assets/screenshot-fps.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JordanDelcros/stats-js/fcfae0801e887eff7143d4bd7cec1b9684bbb942/assets/screenshot-fps.png
--------------------------------------------------------------------------------
/assets/screenshot-mb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JordanDelcros/stats-js/fcfae0801e887eff7143d4bd7cec1b9684bbb942/assets/screenshot-mb.png
--------------------------------------------------------------------------------
/assets/screenshot-ms.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JordanDelcros/stats-js/fcfae0801e887eff7143d4bd7cec1b9684bbb942/assets/screenshot-ms.png
--------------------------------------------------------------------------------
/assets/screenshot-ping.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JordanDelcros/stats-js/fcfae0801e887eff7143d4bd7cec1b9684bbb942/assets/screenshot-ping.png
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Stats.js
7 |
8 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/license:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Jordan Delcros
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@jordandelcros/stats-js",
3 | "version": "1.0.5",
4 | "description": "This class is a rework of the original stats.js from mrdoob, it use a single canvas unlike the original that use DOM elements.",
5 | "main": "stats.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "git+https://github.com/JordanDelcros/stats-js.git"
12 | },
13 | "keywords": [
14 | "stats",
15 | "stats.js",
16 | "stats-js",
17 | "js",
18 | "perfs",
19 | "performance",
20 | "webgl",
21 | "speed",
22 | "enhancement"
23 | ],
24 | "author": "Jordan Delcros",
25 | "license": "MIT",
26 | "bugs": {
27 | "url": "https://github.com/JordanDelcros/stats-js/issues"
28 | },
29 | "homepage": "https://github.com/JordanDelcros/stats-js#readme"
30 | }
--------------------------------------------------------------------------------
/stats.js:
--------------------------------------------------------------------------------
1 | (function( self ){
2 |
3 | var Stats = function( realTime ){
4 |
5 | if( this instanceof Stats ){
6 |
7 | return Stats.methods.initialize(realTime);
8 |
9 | }
10 | else {
11 |
12 | return new Stats(realTime);
13 |
14 | };
15 |
16 | };
17 |
18 | var PIXEL_RATIO = Math.round(window.devicePixelRatio || 1);
19 |
20 | var SIZE = {
21 | WIDTH: 80 * PIXEL_RATIO,
22 | HEIGHT: 50 * PIXEL_RATIO,
23 | FRAMES: {
24 | WIDTH: 74 * PIXEL_RATIO,
25 | HEIGHT: 32 * PIXEL_RATIO,
26 | X: 3 * PIXEL_RATIO,
27 | Y: 15 * PIXEL_RATIO
28 | },
29 | TEXT: {
30 | HEIGHT: 8 * PIXEL_RATIO,
31 | X: 75 * PIXEL_RATIO,
32 | Y: 10 * PIXEL_RATIO
33 | }
34 | };
35 |
36 | var STYLE = {
37 | FPS: {
38 | DATAS: "#1AFFFF",
39 | FRAMES: "#1B314C",
40 | BACKGROUND: "#1A1A38"
41 | },
42 | MS: {
43 | DATAS: "#1AFF1A",
44 | FRAMES: "#1B4C1B",
45 | BACKGROUND: "#1A381A"
46 | },
47 | MB: {
48 | DATAS: "#FF1A94",
49 | FRAMES: "#4C1B34",
50 | BACKGROUND: "#381A29"
51 | },
52 | PING: {
53 | DATAS: "#FFFFFF",
54 | FRAMES: "#555555",
55 | BACKGROUND: "#222222"
56 | }
57 | };
58 |
59 | var MODES = {
60 | FPS: 0,
61 | MS: 1,
62 | MB: 2,
63 | PING: 3
64 | };
65 |
66 | var SUPPORT_MODE_MB = (window.performance != undefined && window.performance.memory != undefined && window.performance.memory.usedJSHeapSize != undefined ? true : false);
67 |
68 | Stats.methods = {
69 | initialize: function( realTime ){
70 |
71 | this.mode = MODES.FPS;
72 |
73 | this.realTime = (realTime || false);
74 |
75 | this.frameTime = 0;
76 |
77 | this.beginTime = 0;
78 | this.endTime = 0;
79 |
80 | this.isPinging = false;
81 | this.beginPinging = 0;
82 | this.endPinging = 0;
83 |
84 | this.fps = {
85 | value: 0,
86 | current: 0,
87 | min: Infinity,
88 | max: -Infinity,
89 | array: new Array(SIZE.FRAMES.WIDTH)
90 | };
91 |
92 | this.ms = {
93 | value: 0,
94 | current: 0,
95 | min: Infinity,
96 | max: -Infinity,
97 | array: new Array(SIZE.FRAMES.WIDTH)
98 | };
99 |
100 | this.mb = {
101 | value: 0,
102 | current: 0,
103 | min: Infinity,
104 | max: -Infinity,
105 | array: new Array(SIZE.FRAMES.WIDTH)
106 | };
107 |
108 | this.ping = {
109 | value: 0,
110 | current: 0,
111 | min: Infinity,
112 | max: -Infinity,
113 | array: new Array(SIZE.FRAMES.WIDTH)
114 | };
115 |
116 | this.domElement = document.createElement("canvas");
117 | this.domElement.className = "statsjs";
118 |
119 | this.domElement.width = SIZE.WIDTH;
120 | this.domElement.height = SIZE.HEIGHT;
121 |
122 | this.domElement.style.width = (SIZE.WIDTH / PIXEL_RATIO) + "px";
123 | this.domElement.style.height = (SIZE.HEIGHT / PIXEL_RATIO) + "px";
124 |
125 | this.domElement.addEventListener("click", function( event ){
126 |
127 | this.switchMode();
128 |
129 | }.bind(this), false);
130 |
131 | this.context = this.domElement.getContext("2d");
132 |
133 | this.context.imageSmoothingEnabled = false;
134 | this.context.font = "bold " + SIZE.TEXT.HEIGHT + "px sans-serif";
135 | this.context.textAlign = "right";
136 |
137 | return this;
138 |
139 | },
140 | switchMode: function(){
141 |
142 | if( this.mode == MODES.FPS ){
143 |
144 | this.mode = MODES.MS;
145 |
146 | }
147 | else if( this.mode == MODES.MS ){
148 |
149 | if( SUPPORT_MODE_MB == true ){
150 |
151 | this.mode = MODES.MB;
152 |
153 | }
154 | else {
155 |
156 | this.mode = MODES.PING;
157 |
158 | };
159 |
160 | }
161 | else if( this.mode == MODES.MB ){
162 |
163 | this.mode = MODES.PING;
164 |
165 | }
166 | else if( this.mode == MODES.PING ){
167 |
168 | this.mode = MODES.FPS;
169 |
170 | };
171 |
172 | this.draw();
173 |
174 | },
175 | begin: function(){
176 |
177 | this.beginTime = window.performance.now();
178 |
179 | },
180 | end: function(){
181 |
182 | var now = window.performance.now();
183 | var deltaTime = (now - this.frameTime);
184 |
185 | this.endTime = now;
186 |
187 | this.fps.current++;
188 | this.fps.max = Math.max(this.fps.current, this.fps.max);
189 |
190 | this.ms.current = (this.endTime - this.beginTime).toFixed(0);
191 | this.ms.min = Math.min(this.ms.current, this.ms.min);
192 | this.ms.max = Math.max(this.ms.current, this.ms.max);
193 |
194 | if( SUPPORT_MODE_MB == true ){
195 |
196 | this.mb.current = Math.round(window.performance.memory.usedJSHeapSize * 0.000000954);
197 | this.mb.min = Math.min(this.mb.current, this.mb.min);
198 | this.mb.max = Math.max(this.mb.current, this.mb.max);
199 |
200 | };
201 |
202 | if( this.realTime == true ){
203 |
204 | this.fps.value = this.fps.current;
205 |
206 | this.fps.array[this.fps.array.length - 1] = this.fps.value;
207 |
208 | };
209 |
210 | if( deltaTime < 1000 && this.realTime == true ){
211 |
212 | this.draw();
213 |
214 | }
215 | else if( deltaTime >= 1000 ){
216 |
217 | this.fps.min = Math.min(this.fps.current, this.fps.min);
218 |
219 | this.frameTime = now;
220 |
221 | this.fps.value = this.fps.current;
222 | this.ms.value = this.ms.current;
223 | this.mb.value = this.mb.current;
224 | this.ping.value = this.ping.current;
225 |
226 | for( var index = 0, length = SIZE.FRAMES.WIDTH; index < length; index++ ){
227 |
228 | this.fps.array[index] = this.fps.array[index + 1];
229 | this.ms.array[index] = this.ms.array[index + 1];
230 | this.mb.array[index] = this.mb.array[index + 1];
231 | this.ping.array[index] = this.ping.array[index + 1];
232 |
233 | };
234 |
235 | this.fps.array[this.fps.array.length - 1] = this.fps.value;
236 | this.ms.array[this.ms.array.length - 1] = this.ms.value;
237 | this.mb.array[this.mb.array.length - 1] = this.mb.value;
238 | this.ping.array[this.ping.array.length - 1] = this.ping.value;
239 |
240 | this.draw();
241 |
242 | this.fps.current = 0;
243 |
244 | };
245 |
246 | },
247 | beginPing: function(){
248 |
249 | this.beginPinging = window.performance.now();
250 |
251 | },
252 | endPing: function(){
253 |
254 | this.endPinging = window.performance.now();
255 | this.ping.current = parseInt(this.endPinging - this.beginPinging);
256 |
257 | this.ping.min = Math.min(this.ping.current, this.ping.min);
258 | this.ping.max = Math.max(this.ping.current, this.ping.max);
259 |
260 | },
261 | draw: function(){
262 |
263 | this.context.clearRect(0, 0, SIZE.WIDTH, SIZE.HEIGHT);
264 |
265 | if( this.mode == MODES.FPS ){
266 |
267 | this.context.fillStyle = STYLE.FPS.BACKGROUND;
268 | this.context.fillRect(0, 0, SIZE.WIDTH, SIZE.HEIGHT);
269 |
270 | this.context.fillStyle = STYLE.FPS.FRAMES;
271 | this.context.fillRect(SIZE.FRAMES.X, SIZE.FRAMES.Y, SIZE.FRAMES.WIDTH, SIZE.FRAMES.HEIGHT);
272 |
273 | this.context.fillStyle = STYLE.FPS.DATAS;
274 |
275 | var min = (this.fps.min == Infinity ? "∞" : this.fps.min);
276 | var max = (this.fps.max == -Infinity ? "∞" : this.fps.max);
277 |
278 | if( this.realTime == true ){
279 |
280 | this.context.fillText(this.fps.current + " FPS (" + min + "-" + max + ")", SIZE.TEXT.X, SIZE.TEXT.Y);
281 |
282 | }
283 | else {
284 |
285 | this.context.fillText(this.fps.value + " FPS (" + min + "-" + max + ")", SIZE.TEXT.X, SIZE.TEXT.Y);
286 |
287 | };
288 |
289 | for( var line = 0, length = this.fps.array.length; line < length; line++ ){
290 |
291 | var height = (((this.fps.array[line] / this.fps.max) * SIZE.FRAMES.HEIGHT) || 0);
292 |
293 | var x = SIZE.FRAMES.X + line;
294 | var y = (SIZE.FRAMES.Y + SIZE.FRAMES.HEIGHT) - height;
295 |
296 | this.context.fillRect(x, y, 1, height);
297 |
298 | };
299 |
300 | }
301 | else if( this.mode == MODES.MS ){
302 |
303 | this.context.fillStyle = STYLE.MS.BACKGROUND;
304 | this.context.fillRect(0, 0, SIZE.WIDTH, SIZE.HEIGHT);
305 |
306 | this.context.fillStyle = STYLE.MS.FRAMES;
307 | this.context.fillRect(SIZE.FRAMES.X, SIZE.FRAMES.Y, SIZE.FRAMES.WIDTH, SIZE.FRAMES.HEIGHT);
308 |
309 | this.context.fillStyle = STYLE.MS.DATAS;
310 |
311 | var min = (this.ms.min == Infinity ? "∞" : this.ms.min);
312 | var max = (this.ms.max == -Infinity ? "∞" : this.ms.max);
313 |
314 | if( this.realTime == true ){
315 |
316 | this.context.fillText(this.ms.current + " MS (" + min + "-" + max + ")", SIZE.TEXT.X, SIZE.TEXT.Y);
317 |
318 | }
319 | else {
320 |
321 | this.context.fillText(this.ms.value + " MS (" + min + "-" + max + ")", SIZE.TEXT.X, SIZE.TEXT.Y);
322 |
323 | };
324 |
325 | for( var line = 0, length = this.ms.array.length; line < length; line++ ){
326 |
327 | var height = (((this.ms.array[line] / this.ms.max) * SIZE.FRAMES.HEIGHT) || 0);
328 |
329 | var x = SIZE.FRAMES.X + line;
330 | var y = (SIZE.FRAMES.Y + SIZE.FRAMES.HEIGHT) - height;
331 |
332 | this.context.fillRect(x, y, 1, height);
333 |
334 | };
335 |
336 | }
337 | else if( this.mode == MODES.MB ){
338 |
339 | this.context.fillStyle = STYLE.MB.BACKGROUND;
340 | this.context.fillRect(0, 0, SIZE.WIDTH, SIZE.HEIGHT);
341 |
342 | this.context.fillStyle = STYLE.MB.FRAMES;
343 | this.context.fillRect(SIZE.FRAMES.X, SIZE.FRAMES.Y, SIZE.FRAMES.WIDTH, SIZE.FRAMES.HEIGHT);
344 |
345 | this.context.fillStyle = STYLE.MB.DATAS;
346 |
347 | var min = (this.mb.min == Infinity ? "∞" : this.mb.min);
348 | var max = (this.mb.max == -Infinity ? "∞" : this.mb.max);
349 |
350 | if( this.realTime == true ){
351 |
352 | this.context.fillText(this.mb.current + " MB (" + min + "-" + max + ")", SIZE.TEXT.X, SIZE.TEXT.Y);
353 |
354 | }
355 | else {
356 |
357 | this.context.fillText(this.mb.value + " MB (" + min + "-" + max + ")", SIZE.TEXT.X, SIZE.TEXT.Y);
358 |
359 | };
360 |
361 | for( var line = 0, length = this.mb.array.length; line < length; line++ ){
362 |
363 | var height = (((this.mb.array[line] / this.mb.max) * SIZE.FRAMES.HEIGHT) || 0);
364 |
365 | var x = SIZE.FRAMES.X + line;
366 | var y = (SIZE.FRAMES.Y + SIZE.FRAMES.HEIGHT) - height;
367 |
368 | this.context.fillRect(x, y, 1, height);
369 |
370 | };
371 |
372 | }
373 | else if( this.mode == MODES.PING ){
374 |
375 | this.context.fillStyle = STYLE.PING.BACKGROUND;
376 | this.context.fillRect(0, 0, SIZE.WIDTH, SIZE.HEIGHT);
377 |
378 | this.context.fillStyle = STYLE.PING.FRAMES;
379 | this.context.fillRect(SIZE.FRAMES.X, SIZE.FRAMES.Y, SIZE.FRAMES.WIDTH, SIZE.FRAMES.HEIGHT);
380 |
381 | this.context.fillStyle = STYLE.PING.DATAS;
382 |
383 | var min = (this.ping.min == Infinity ? "∞" : this.ping.min);
384 | var max = (this.ping.max == -Infinity ? "∞" : this.ping.max);
385 |
386 | if( this.realTime == true ){
387 |
388 | this.context.fillText(this.ping.current + " PING (" + min + "-" + max + ")", SIZE.TEXT.X, SIZE.TEXT.Y);
389 |
390 | }
391 | else {
392 |
393 | this.context.fillText(this.ping.value + " PING (" + min + "-" + max + ")", SIZE.TEXT.X, SIZE.TEXT.Y);
394 |
395 | };
396 |
397 | for( var line = 0, length = this.ping.array.length; line < length; line++ ){
398 |
399 | var height = (((this.ping.array[line] / this.ping.max) * SIZE.FRAMES.HEIGHT) || 0);
400 |
401 | var x = SIZE.FRAMES.X + line;
402 | var y = (SIZE.FRAMES.Y + SIZE.FRAMES.HEIGHT) - height;
403 |
404 | this.context.fillRect(x, y, 1, height);
405 |
406 | };
407 |
408 | };
409 |
410 | }
411 | };
412 |
413 | Stats.methods.initialize.prototype = Stats.methods;
414 |
415 | if( typeof define !== "undefined" && define instanceof Function && define.amd != undefined ){
416 |
417 | define(function(){
418 |
419 | return Stats;
420 |
421 | });
422 |
423 | }
424 | else if( typeof module !== "undefined" && module.exports ){
425 |
426 | module.exports = Stats;
427 |
428 | }
429 | else if( self != undefined ){
430 |
431 | self.Stats = Stats;
432 |
433 | };
434 |
435 | })(this || {});
436 |
--------------------------------------------------------------------------------