54

I need to get any information about the CPU/GPU/memory.The number of cores, memory value, memory and cpu usage... I found a way to do this for IE:How to Use JavaScript to Find Hardware Information

solutions for other browsers I do not know. Any idea how to do it? maybe webgl has access to information about your computer? or flash? or any other technology?

Thank you very much

Nickolay
  • 31,095
  • 13
  • 107
  • 185
Alex Nester
  • 541
  • 1
  • 4
  • 3
  • 2
    In google chrome you can use console.memory to find out the amount of memory available in the JS heap. – Benjamin Gruenbaum Mar 17 '13 at 19:01
  • I'd have a look at whether a Flash shim might help. I haven't seen any cross-browser API for hardware info before. – Drew Noakes Mar 17 '13 at 19:02
  • Those methods no longer work in IE. Browsers are trying to make it harder to track people. One way to track people is to look up all the info about their machine. That plus their ip address is often enough to identify someone or a least a specific machine. So, browser don't show CPU/GPU/Memory/# cores/etc. (https://panopticlick.eff.org/) – gman Mar 18 '13 at 15:32

3 Answers3

37

This code will print GPU info and will list all info you can get from the Performance object of this browser (it's different for each browser).

<html>

<body>
  <canvas id="glcanvas" width="0" height="0"></canvas>
  <script>
    var performance = window.performance || window.mozPerformance || window.msPerformance || window.webkitPerformance || {};

    const performanceKeys = [];
    for (var value in performance) {
      performanceKeys.push(value);
    }
    document.write("<br>");
    document.write(performanceKeys.sort().map((p) => '<a href="https://developer.mozilla.org/en-US/docs/Web/API/Performance/' + p + '">' + p + "</a>").join("<br>"));
    document.write("<br>");

    document.write("<br><br><br>");

    var canvas;
    canvas = document.getElementById("glcanvas");
    var gl = canvas.getContext("experimental-webgl");

    document.write(gl.getParameter(gl.RENDERER) + "<br>");
    document.write(gl.getParameter(gl.VENDOR) + "<br>");
    document.write(getUnmaskedInfo(gl).vendor + "<br>");
    document.write(getUnmaskedInfo(gl).renderer + "<br>");


    function getUnmaskedInfo(gl) {
      var unMaskedInfo = {
        renderer: '',
        vendor: ''
      };

      var dbgRenderInfo = gl.getExtension("WEBGL_debug_renderer_info");
      if (dbgRenderInfo != null) {
        unMaskedInfo.renderer = gl.getParameter(dbgRenderInfo.UNMASKED_RENDERER_WEBGL);
        unMaskedInfo.vendor = gl.getParameter(dbgRenderInfo.UNMASKED_VENDOR_WEBGL);
      }

      return unMaskedInfo;
    }
  </script>
</body>

Output in Chrome:

addEventListener
clearMarks
clearMeasures
clearResourceTimings
dispatchEvent
eventCounts
getEntries
getEntriesByName
getEntriesByType
mark
measure
memory
navigation
now
onresourcetimingbufferfull
removeEventListener
setResourceTimingBufferSize
timeOrigin
timing
toJSON



WebKit WebGL
WebKit
NVIDIA Corporation
NVIDIA GeForce GTX 775M OpenGL Engine

Output in Firefox:

addEventListener
clearMarks
clearMeasures
clearResourceTimings
dispatchEvent
eventCounts
getEntries
getEntriesByName
getEntriesByType
mark
measure
navigation
now
onresourcetimingbufferfull
removeEventListener
setResourceTimingBufferSize
timeOrigin
timing
toJSON



Mozilla
Mozilla

Output in Safari:

addEventListener
clearMarks
clearMeasures
clearResourceTimings
dispatchEvent
getEntries
getEntriesByName
getEntriesByType
mark
measure
navigation
now
onresourcetimingbufferfull
removeEventListener
setResourceTimingBufferSize
timeOrigin
timing
toJSON



WebKit WebGL
WebKit
NVIDIA Corporation
NVIDIA GeForce GTX 775M OpenGL Engine
Boris Verkhovskiy
  • 14,854
  • 11
  • 100
  • 103
Thomas
  • 543
  • 5
  • 11
  • 1
    detects Nvidia Geforce GTX 960M (laptop) as GTX 980 (desktop) – user3625699 Jan 15 '22 at 12:53
  • If you're interested in this question, you may like to know that [Firefox has started "bucketing" GPU names](https://bugzilla.mozilla.org/show_bug.cgi?id=1715690) -- quite a lot of GPUs now show up as "GTX 980". It's an anti-fingerprinting measure. – Coderer Mar 21 '22 at 16:19
  • These days the only difference is that Chrome has [`performance.memory`](https://developer.mozilla.org/en-US/docs/Web/API/Performance/memory) (deprecated) and Safari doesn't have [`performance.eventCounts`](https://developer.mozilla.org/en-US/docs/Web/API/Performance/eventCounts). – Boris Verkhovskiy May 28 '23 at 08:42
14

Currently Chrome Canary supports returning the amount of CPU cores using:

navigator.hardwareConcurrency

This worked for me in Chrome Canary 37.

Bas van Dijk
  • 9,933
  • 10
  • 55
  • 91
  • Works in Chrome 40 stable. – cprcrack Feb 02 '15 at 23:47
  • I wrote about hardwareConcurrency in other browsers here: http://stackoverflow.com/a/31896597/1026 – Nickolay Aug 08 '15 at 17:30
  • 1
    Is this cores or threads? Because when I did this I got the number 4 back, but my processor only has two cores (it does have 4 threads though). I'm guessing this is because I have an [i7](http://ark.intel.com/products/64893/Intel-Core-i7-3520M-Processor-4M-Cache-up-to-3_60-GHz)? – Hanna Aug 31 '15 at 19:31
  • According to https://wiki.whatwg.org/wiki/Navigator_HW_Concurrency it returns the number of logical processors available to the user agent, up to an optional thread limit per origin. – Bas van Dijk Sep 01 '15 at 11:40
  • 2
    @Johannes with processors supporting Hyperthreading the system sees your double core processor as quad core. Not browsers only, but OS itself. – GuardianX Aug 11 '16 at 11:36
  • Also implemented in firefox as of now. – r3wt Jan 07 '17 at 02:43
11

Estimate CPU speed by timing how long it takes to decrement a variable millions of times:

const runs = 150000000;
const start = performance.now(); // in ms, usually with 100us resolution
for (let i = runs; i>0; i--) {}
const end = performance.now();
const ms = end - start;
const cyclesPerRun = 2;
const speed = (runs / ms / 1000000) * cyclesPerRun;
console.log(`Time: ${Math.round(ms)/1000}s, estimated speed: ${Math.round(speed*10)/10} GHz`);

* cyclesPerRun is a very rough way to map "subtractions per second" to clock speed and it varies a lot across browsers (because their JavaScript engines might optimize the code differently) and CPUs (because they might be able to pipeline more instructions, they might ramp up their frequency faster, etc.). You might be better off just using runs / ms as a generic "CPU speed" parameter instead of trying to estimate the clock speed in GHz with cyclesPerRun.

You can also estimate cyclesPerRun yourself by running the code above on a CPU you know the clock speed of and then doing this:

const knownSpeed = 3.2; // GHz
const estimatedCyclesPerRun = knownSpeed / (runs/ms/1000000);
console.log("cyclesPerRun = " + estimatedCyclesPerRun);

Also this benchmark depends on the number of browser tabs the user has open or if some other program (like a video game) is already using the computer's resources, etc.

Boris Verkhovskiy
  • 14,854
  • 11
  • 100
  • 103
Aaron Becker
  • 593
  • 1
  • 6
  • 10
  • Well.. I guess it's accurate if you tweak the `_speedconstant` for "your" machine, because running this in my 4-year-old machine estimates 23.753GHz. This is also susceptible to javascript engine optimizations. – olivarra1 Oct 18 '17 at 18:16
  • 1
    @olivarra1 Yep, it is not the most reliable, but I found that it is good for a quick client-side approximate estimate of CPU speed, for mobile devices especially. I have used it in the past when other solutions like the first one fail, or when the browser cannot be identified. I can see your point though, it doesn't really work on PCs with JS optimizations or multiple cores :) – Aaron Becker Oct 20 '17 at 03:47
  • 3
    Yes, that's definitely better than nothing.... I will consider the output of this method as a generic "JS speed value" more than the GHz of the processor, which is more than enough to optimize the app to the power of the machine. – olivarra1 Oct 20 '17 at 08:02
  • @olivarra1 Good idea :) – Aaron Becker Oct 23 '17 at 01:12
  • Tried this script on Intel Core i7 iMac (late 2013) model at 3.1GHz, it reports 7.7GHz... seems quite inaccurate. – Raptor Jan 23 '18 at 06:25
  • We used this technic in 1999, when my computer had < 100Mhz and a for loop with 10000 empty iterations took 1 second. – Daniel W. Dec 13 '18 at 17:02
  • 2
    I'm also getting 7.89Ghz on a processor that boosts up to 4.9Ghz but usually runs at more like 3.7 (i9-11950H). It's worth noting that most modern processors have highly variable speeds. It seems like thie benchmark constant needs a /2 somewhere at the end because of some perf improvement in V8 perhaps? e.g. `var _speedconstant = 4.5e-9;` gives 3.75Ghz for me, which is a lot closer to realistic, but would need to profile on several CPUs to test. Of note it works if I 4x the `amount` val, but if I 8x it, it goes way off and estimates 0.58Ghz. Also: would be ideal to run this in a webworker. – Kyle Baker Nov 07 '22 at 21:20
  • It works fine, as expected for me :) I am getting 3.36GHz on a Ryzen 5 2600 on Win 10 in Google Chrome – Antonio Noack Dec 05 '22 at 20:46
  • @KyleBaker: Perhaps V8 unrolls while JIT-compiling, into an asm loop that does `sub eax, 2` / `jge keep_looping` or something, to do 2 source-iterations per asm iteration. Or unrolling more but using floating-point math (which has higher latency thus a longer loop-carried dependency chain) since it's Javascript. This counted-loop benchmark idea sounds is tremendously error-prone and dependent on how a JIT-compiler optimizes it into asm. And if it runs long enough, will spend extra time re-optimizing it. – Peter Cordes May 20 '23 at 00:05
  • So not only do you have CPU frequency ramp-up, you have different JIT optimizers on different browsers or node.js versions. This seems very error-prone, and only usable for different CPUs with the same JS implementations. – Peter Cordes May 20 '23 at 00:06
  • @PeterCordes but in a different sense, it's also the only true answer if you want to know the cpu speed because it directly measures how many nanoseconds it takes to perform a subtraction and a conditional jump (FLOPS effectively). I would assume that all modern JIT'd implementations (there's only 3 that matter) would become that, and because it's doing so many iterations the warmup should be negligible. Then it's a question of instructions per floating point subtraction and pipeline depth. Treating the result as a generic "speed" value and not directly as GHz is a good suggestion. – Boris Verkhovskiy May 20 '23 at 08:21
  • @BorisVerkhovskiy The major JS JIT engines are very capable of using integer math for JS operations when they can prove that the value is always an integer. Like in this case, `runs` is initialized with an integer value (in range for int32 so FP math on it will be exact) and only has integer subtraction done on it, so it's an easy and important optimization that JITs would definitely look for. (If it was using FP, it wouldn't be measuring max FLOPS, it would bottleneck on FP subtract latency. e.g. one subtract per 4 clocks on Intel Skylake, vs. 2/clock for independent scalar subtracts.) – Peter Cordes May 20 '23 at 08:29
  • @BorisVerkhovskiy: A better designed loop could be a lot less likely to get optimized or unrolled, e.g. start with `x = 2^52 - 1e7` or something, and add `x += 0.999` until `x` stops changing. A non-integer increment will require FP math, which is not associative and can't be easily unrolled. Once it gets to 2^52 or 2^53, I forget which, adding `0.99` to a `double` won't increase it because adjacent representable doubles are 2 apart. Of course then you're benchmarking FP math addition latency, so i7-5xxx (Broadwell) would benchmark 5/3 x faster than an i7-4xxx (Haswell), so that's not great – Peter Cordes May 20 '23 at 08:34
  • @BorisVerkhovskiy: correction: https://en.wikipedia.org/wiki/Double-precision_floating-point_format#Precision_limitations_on_integer_values - it's 2^53 where doubles start rounding to multiples of 2, where `x + 0.99 == x` will be true. (And or `x + 1 == x` since the nearest-even tiebreak will round down at some point.) – Peter Cordes May 20 '23 at 08:36
  • Fair enough, I assumed it didn't because it but it seems slightly unsafe, what if you iterate past MAX_SAFE_INTEGER? I don't have a laptop with me right now, but I would honestly love to check out exactly what instructions it ends up running, I saw Node.js has options to print out bytecode and assembly. Like I said, there's only 3 JS runtimes that matter (and almost everyone's using the latest versions) and there's a fairly manageable number of different CPUs in existence. – Boris Verkhovskiy May 20 '23 at 08:37
  • @BorisVerkhovskiy: If a JIT can't prove that a value won't grow so large that FP rounding needs to take effect, it could use 32-bit integers and check for overflow out of their range. 32-bit array indices are fine for most things. (Overflow of 32 bits is still cheap in asm, e.g. `add eax, 1` sets CF when it wraps.) If a JIT *can* prove that a loop counter will stay in a safe range, it can check outside the loop and only use integer work inside the loop. e.g. `for(i = 0 ; i< n ;i++)` without other modification of `i` will only loop to `n-1`, so check that once. – Peter Cordes May 20 '23 at 08:41
  • @BorisVerkhovskiy: Yeah, now that you mention it, I'm a bit curious what asm we'd get for this empty loop for x86-64, vs. my suggestion for an FP loop. [How to view the assembly code generated from my JavaScript (in Chrome)?](https://stackoverflow.com/q/59411889) – Peter Cordes May 20 '23 at 08:42
  • I ran `node --trace-turbo <( echo "for (let i = 150000000; i>0; i--) {}")` and loaded the resulting file into [Turbolizer](https://v8.github.io/tools/head/turbolizer/index.html) and it's definitely using integer instructions. – Boris Verkhovskiy May 28 '23 at 01:43