├── .gitattributes ├── .gitignore ├── README.md ├── bower.json ├── dist └── index.min.js ├── package.json ├── src └── index.js └── test └── test.html /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | *.log 3 | Thumbs.db 4 | node_modules 5 | npm-debug.log 6 | *.orig 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Fireball 2 | Breakpoints for performance. 3 | 4 | Fireball is a small script that runs when your web page is loaded. It generates a score based on the performance of the user's hardware. 5 | 6 | It hands off the work to a different thread so won't slow the rest of your site down while it's running. 7 | 8 | ## Installation 9 | 10 | ``` 11 | npm install fireball --save 12 | ``` 13 | 14 | ``` 15 | bower install fireball 16 | ``` 17 | 18 | ## Example usage 19 | 20 | ### Setup 21 | Fireball uses a [Worker](https://developer.mozilla.org/en-US/docs/Web/API/Worker) to calculate a score, which is loaded dynamically when fireball initialises. No need to host the worker script in another file. 22 | 23 | ### Running fireball, the simple way 24 | ```javascript 25 | var Fireball = require('fireball-js'); 26 | Fireball.run(); 27 | ``` 28 | 29 | or if adding the script directly or using bower Fireball will be already available: 30 | 31 | ```javascript 32 | Fireball.run(); 33 | ``` 34 | 35 | 36 | The resulting score will be available in your JavaScript as `Fireball.getScore()` after a few seconds. 37 | 38 | ```javascript 39 | if (Fireball.getScore() > 8000){ 40 | //Do something to delight the user 41 | } else { 42 | //Do something boring but easy on the CPU 43 | } 44 | ``` 45 | 46 | ### Running fireball with classes 47 | ```javascript 48 | Fireball.run({ 49 | speedRanges: [ 50 | {min: 0, className: 'speed-of-sloth'}, 51 | {min: 4000, className: 'speed-of-tortoise'}, 52 | {min: 8000, className: 'speed-of-puppy'}, 53 | {min: 16000, className: 'speed-of-cheetah'} 54 | ] 55 | }); 56 | ``` 57 | 58 | These breakpoints will be added as classes to the `
` so you can target them in CSS. E.g. 59 | 60 | ```css 61 | body.speed-of-sloth .my-element { 62 | /* no box shadows, transitions, etc. */ 63 | } 64 | 65 | body.speed-of-cheetah .my-element { 66 | /* some hella fancy animation */ 67 | } 68 | ``` 69 | 70 | ### Running fireball with all the bells and whistles 71 | ```javascript 72 | Fireball.run({ 73 | debug: true, //shows an onscreen readout. Defaults to false 74 | runs: 7, //defaults to 7 75 | defaultScore: 5000, //defaults to 0 76 | classEl: 'body', //append a class indicating speed to this element. Defaults to 'body' 77 | speedRanges: [ //the speed breakpoints and classnames to use 78 | {min: 0, className: 'sloth'}, 79 | {min: 4000, className: 'tortoise'}, 80 | {min: 8000, className: 'puppy'}, 81 | {min: 16000, className: 'cheetah'} 82 | ], 83 | callback: function(score) { 84 | //do something now that the tests are done 85 | // or store the score in a global variable if you're that way inclined 86 | } 87 | }); 88 | ``` 89 | 90 | You can also register a callback like so. 91 | 92 | ``` 93 | Fireball.onSuccess(callback); 94 | ``` 95 | 96 | `callback` will be passed a single argument, the score. 97 | 98 | This is handy if you have a modular system and want to access the fireball score in a different module 99 | without using a global variable. If the score has already been calculated this will execute immediately. 100 | 101 | ## Browser support 102 | Chrome, Firefox, Safari, Android 4.4+. 103 | 104 | Won't work in the current IE (11) or Edge (25). 105 | 106 | ## Benchmark 107 | The Fireball score is roughly aligned with [the Octane benchmark](http://chromium.github.io/octane/) score; 108 | if a machine gets 15,000 on octane, the Fireball score will be within a few thousand of that. Probably. 109 | 110 | Check out the demo on [my site](http://www.dg707.com/fireball) to see what score your machine gets. 111 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fireball", 3 | "version": "1.1.2", 4 | "homepage": "https://github.com/davidgilbertson/fireball", 5 | "description": "Fireball is a small script that runs when your web page is loaded. It generates a score based on the performance of the user's hardware.", 6 | "main": "dist/index.min.js", 7 | "keywords": [ 8 | "fireball", 9 | "performance" 10 | ], 11 | "authors": [ 12 | "David Gilbertson> "+str+"
"}function appendClasses(options){var i=void 0,className=options.speedRanges[0].className;for(i=1;i> ${str}
`; 91 | } 92 | } 93 | 94 | function appendClasses(options) { 95 | let i; 96 | let className = options.speedRanges[0].className; 97 | 98 | for (i = 1; i < options.speedRanges.length; i++) { 99 | if (finalScore >= options.speedRanges[i].min) { 100 | className = options.speedRanges[i].className; 101 | } 102 | } 103 | 104 | options.speedRanges.forEach(speedRange => { 105 | if (finalScore >= speedRange.min) { 106 | className = speedRange.className; 107 | } 108 | }); 109 | 110 | if (className) { 111 | const classSelector = options.classEl || 'body'; 112 | const classEl = getEl(classSelector); 113 | 114 | if (classEl) { 115 | classEl.classList.add(className); // If window.Worker exists, classList almost certainly does 116 | } 117 | } 118 | } 119 | 120 | const fireballWorker = () => { 121 | 'use strict'; 122 | 123 | self.addEventListener('message', () => { 124 | self.postMessage(runTest()); 125 | }, false); 126 | 127 | function runTest() { 128 | const OPS = 1000000; 129 | var startTime = new Date().valueOf(); 130 | 131 | for (let i = 0; i < OPS; i++) { 132 | /=/.exec('111soqs57qo8o8480qo18sor2011r3n591q7s6s37r120904'); 133 | /=/.exec('SbeprqRkcvengvba=633669315660164980'); 134 | /=/.exec('FrffvbaQQS2=111soqs57qo8o8480qo18sor2011r3n591q7s6s37r120904'); 135 | } 136 | 137 | const endTime = new Date().valueOf(); 138 | return OPS / (endTime - startTime); 139 | } 140 | }; 141 | 142 | function run(options = {}) { 143 | if (typeof window === 'undefined') return; 144 | 145 | finalScore = options.defaultScore || 0; 146 | 147 | if (!window.Worker) { 148 | options.callback && options.callback(); 149 | return; 150 | } 151 | 152 | const debug = options.debug || false; 153 | const runs = options.runs || 7; 154 | let count = 0; 155 | const blobURL = URL.createObjectURL(new Blob( 156 | [`(${fireballWorker.toString()})()`], 157 | {type: 'application/javascript'} 158 | )); 159 | 160 | const fbWorker = new Worker(blobURL); 161 | 162 | URL.revokeObjectURL(blobURL); 163 | 164 | fbWorker.addEventListener('message', e => { 165 | logScore(e.data); 166 | }, false); 167 | 168 | function logScore(rawScore) { 169 | const thisScore = parseInt(rawScore * 6.1813, 10); //align it roughly with Octane 170 | 171 | scores.push(thisScore); 172 | finalScore = getMedianScore(); 173 | 174 | count++; 175 | 176 | if (debug) log(finalScore.toLocaleString()); 177 | 178 | if (count < runs) { 179 | setTimeout(() => { 180 | fbWorker.postMessage('run'); 181 | }, 100); 182 | } else { 183 | hasFinished = true; 184 | 185 | runCallbacks(); 186 | 187 | if (debug) log('_finished_'); 188 | 189 | if (options.speedRanges) appendClasses(options); 190 | 191 | options.callback && options.callback(); 192 | } 193 | } 194 | 195 | fbWorker.postMessage('run'); 196 | } 197 | 198 | function getScore() { 199 | return finalScore; 200 | } 201 | 202 | function onSuccess(callback) { 203 | if (hasFinished) { 204 | callback(finalScore); 205 | } else { 206 | callbacks.push(callback); 207 | } 208 | } 209 | 210 | export default { 211 | run, 212 | getScore, 213 | onSuccess, 214 | }; 215 | -------------------------------------------------------------------------------- /test/test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |