Fix deviceMemory & hardwareConcurrency Mismatch (2026)

12 min read

Picture a test profile with carefully chosen settings. Canvas is consistent. The WebGL renderer string matches a real Intel UHD 620. The timezone agrees with the proxy. Every value reads as reasonable on its own.

But the profile reports navigator.hardwareConcurrency = 16, which reads as a 16-core machine, and the laptop it runs on is an M1 MacBook Air with 8 cores. Any script that runs a parallel Web Worker benchmark sees fewer cores at work than the browser reports.

Sixteen reported cores. Eight actual. The benchmark runs at half the expected parallelism. Each value looks fine; together they don't describe a real machine.

Hardware specs are the values people most often set to arbitrary "realistic" numbers without asking whether the machine can actually perform like them.

By the end of this guide, you'll understand how deviceMemory and hardwareConcurrency can be cross-checked against actual performance, and how to configure profiles whose declared specs match measurable behavior. It is useful whether you are a site owner deciding how much to trust declared values, a QA engineer testing how your site treats different devices, an agency keeping client profiles consistent, or a privacy-minded user choosing less unique values.

Prerequisites

  • JustBrowser installed (the 7-day trial, card required, includes everything this needs)
  • Basic understanding of browser fingerprinting
  • 15 minutes
  • A profile you want to audit

If you're new to fingerprinting, the JA4 TLS fingerprint guide covers the network-layer side. This post goes deeper into one specific cross-check.

What Scripts Actually Check

Here is what a fingerprinting script sees.

Open your browser console and run:

console.log('Declared RAM:', navigator.deviceMemory, 'GB');
console.log('Declared cores:', navigator.hardwareConcurrency);

On an M1 MacBook Air, the output looks like this:

Declared RAM: 8 GB
Declared cores: 8

Simple enough. Here is what a script can do next:

// Parallel benchmark - time how fast we can run workers
const workerCount = navigator.hardwareConcurrency;
const workers = [];
const start = performance.now();

for (let i = 0; i < workerCount; i++) {
  workers.push(new Promise(resolve => {
    const blob = new Blob([`
      let sum = 0;
      for (let i = 0; i < 10000000; i++) sum += Math.sqrt(i);
      postMessage(sum);
    `], {type: 'application/javascript'});
    const worker = new Worker(URL.createObjectURL(blob));
    worker.onmessage = () => { worker.terminate(); resolve(); };
  }));
}

Promise.all(workers).then(() => {
  console.log('Parallel time:', performance.now() - start, 'ms');
});

A 16-core machine running 16 workers in true parallel finishes in roughly 1/16th the time of serial execution. An 8-core machine reporting 16 cores? The workers queue up, and the timing shows it.

Scripts don't need to be this obvious. They embed these benchmarks in canvas rendering, audio processing, or crypto operations. The point is that they measure whether a browser performs like its declared specs suggest. Fraud and bot-management tools use checks like this because declared values cost nothing to change.

The deviceMemory Problem

navigator.deviceMemory returns a coarse approximation of RAM: the amount is rounded to a power of two and clamped to lower and upper bounds the browser sets (MDN notes those bounds can change). Only a handful of values are possible, so it looks easy to set.

Scripts can't read RAM directly, but memory pressure can show up indirectly:

  • Garbage collection: a machine short on memory pauses for garbage collection more often under the same workload
  • Allocation limits: a very large allocation that succeeds on one machine can fail on a smaller one

A profile reporting 8GB that runs out of headroom on a modest workload contradicts its own declaration.

Memory-based checks are harder to run reliably than core-count ones, because they need sustained memory pressure and careful measurement. Whatever the platform, its terms apply to every account you hold: a consistent profile is not a way around a block, and if you disagree with a decision, use the platform's appeal route.

Where Profile Configurations Go Wrong

Most multi-account browsers let you set deviceMemory and hardwareConcurrency to any value. That is useful for testing how a site treats different devices, but it makes it easy to end up with values that don't agree with each other or with the machine.

There are three failure modes:

1. Arbitrary high values

Someone sets 8GB RAM and 24 cores because it "looks like a developer machine." The actual laptop has 4GB and 4 cores, so every benchmark shows the difference. Consistent beats impressive.

2. Mismatched within the profile

DeviceMemory set to 4GB but hardwareConcurrency to 16. 16-core systems don't ship with 4GB RAM, and scripts can flag statistically unlikely hardware combinations. It's like claiming you drive a Lamborghini with a lawnmower engine.

3. Values don't match the profile's configured identity

A profile configured as Windows 10 on an Intel i3 that reports 16 cores. i3 processors don't have 16 cores. The user agent says one thing and the hardware specs say another.

Step 1: Audit Your Current Profile

Launch your JustBrowser profile and run a coherence check.

Open the console and read the declared values:

// Declared specs
console.log('deviceMemory:', navigator.deviceMemory);
console.log('hardwareConcurrency:', navigator.hardwareConcurrency);

Now run a quick parallel benchmark:

// Time a parallel workload
async function benchParallel(threadCount) {
  const workers = [];
  const start = performance.now();

  for (let i = 0; i < threadCount; i++) {
    workers.push(new Promise(resolve => {
      const code = `
        let x = 0;
        for (let j = 0; j < 5000000; j++) x += Math.sin(j);
        postMessage(x);
      `;
      const w = new Worker(URL.createObjectURL(new Blob([code])));
      w.onmessage = () => { w.terminate(); resolve(); };
    }));
  }

  await Promise.all(workers);
  return performance.now() - start;
}

// Run with declared count vs actual system performance
const declared = navigator.hardwareConcurrency;
const time = await benchParallel(declared);
console.log(`${declared} workers completed in ${time.toFixed(0)}ms`);

Compare this across profiles. A profile reporting 16 cores should complete this benchmark noticeably faster than a profile reporting 8 cores on the same physical machine, provided the machine really has the cores.

If the timing is nearly identical regardless of the declared core count, the declared hardwareConcurrency is higher than the machine can back up.

Step 2: Configure Consistent Hardware Specs in JustBrowser

In JustBrowser, open your profile's fingerprint settings and find the hardware parameters.

For hardwareConcurrency:

  1. Open the profile's hardware settings
  2. Find the CPU core count (hardwareConcurrency)
  3. Set it to match your actual physical hardware, or lower

The key point: a profile can report fewer cores than the machine has, but not more. A 16-core machine can consistently report 4 cores (the extra cores just sit idle). A 4-core machine cannot consistently report 16, because parallel execution timing shows it.

For teams running many test or client profiles, a simple approach is to set hardwareConcurrency to 4 or 8 across all profiles. These are common values in real fingerprint populations, and most machines have at least 4 cores. The profiles differ in other parameters, but the core count stays consistent and verifiable.

For deviceMemory:

  1. Find the device memory setting (GB)
  2. Set it to match or understate your actual RAM

Same principle. A 32GB machine can report 8GB. A 4GB machine reporting 8GB can show up under memory pressure.

JustBrowser's custom Chromium engine applies hardwareConcurrency and deviceMemory at the C++ level, so the value comes from the engine rather than from a JavaScript getter patch. It does not throttle real execution, which is why you should declare at or below your real hardware so the timing still agrees.

Step 3: Match Hardware to the Profile's Configured Identity

The hardware specs need to make sense with the rest of the profile.

If the profile is configured as:

  • Windows 10 Home
  • Intel Core i5-8250U processor (from User Agent or hints)
  • 1920x1080 screen

Then deviceMemory and hardwareConcurrency should match what that laptop actually has:

  • i5-8250U has 4 cores / 8 threads, so hardwareConcurrency: 8
  • Typical laptops with this CPU ship with 8-16GB, so deviceMemory: 8

An i5-8250U with 24 cores is not a real configuration.

JustBrowser generates a profile's hardware values from population data on real devices, so by default the combination is one that ships together. If you override values manually, check that the result still describes a real machine.

Step 4: Check What the Profile Reports

Read the values back in the configured profile. The console snippet from Step 1 shows the declared deviceMemory and hardwareConcurrency, and WEBGL_debug_renderer_info shows the GPU strings. The profile's consistency report, in the app or through GET /profiles/{id}/fingerprint/validate on the local API, puts them side by side.

Check that they tell one story. A $300 laptop GPU next to a server-class CPU is an unusual pairing.

For automation-heavy workflows using Playwright or Puppeteer, you can script these checks. Start a profile, run a benchmark, log the timing, and compare against expected values.

Common Errors and How to Fix Them

Benchmark timing doesn't scale with declared cores

Cause: The profile reports more cores than your machine has. Worker parallelism is bottlenecked by real hardware.

Fix: Lower hardwareConcurrency to your actual core count or below.

Profile reports 8GB but struggles under modest memory pressure

Cause: Your actual machine has 4GB or less. The browser's memory management exposes the real constraint.

Fix: Set deviceMemory to 4 or lower to match your actual RAM.

A profile looks inconsistent despite correct values

Cause: The values are correct but don't match other parameters. The CPU/GPU/RAM combination doesn't exist in real hardware.

Fix: Use JustBrowser's fingerprint generator instead of manual overrides, so the combination is one that exists in the wild.

A site you don't control still treats the session as unusual

Cause: That site weighs its own signals, and how it treats a session is its decision.

Fix: There is nothing to tune around. Report your real hardware (on an 8-core machine, 8 cores rather than 6 or 12), and if a platform restricts an account you're entitled to hold, read its terms and use its appeal route.

Why This Coherence Check Exists

Scripts used to read navigator values and trust them. Then overriding those values became easy, and more fingerprinting started to verify declared values rather than trust them. Canvas fingerprinting went through the same evolution, then WebGL. Hardware specs are getting the same treatment.

For a site owner, the lesson is to treat declared hardware as a claim, not a fact. For anyone configuring profiles, it is to make the claim true. (If you want to see how real visitors' devices and performance look on your own site, JustAnalytics covers cookieless analytics and performance monitoring.)

Next Steps

Hardware coherence is one piece of the fingerprint picture. Once your profiles agree on this:

  1. Check timezone alignment. A profile set to Germany should have the Europe/Berlin timezone.

  2. Verify WebGL coherence. The GPU renderer string should match the CPU class. The WebGL fingerprinting deep-dive explains what to check.

  3. Review the full set of parameters in the profile's consistency report, and confirm they tell one story.

And if you're monitoring ad traffic for click fraud, the fingerprint techniques covered here are the same ones fraud detection uses, so it helps to understand both sides.

Changing hardwareConcurrency from 16 to 8, to match an 8-core M1, alters one value and leaves the rest of the fingerprint alone. Same canvas, same WebGL, just coherent hardware specs.

Frequently Asked Questions

Why do fingerprinting scripts cross-check deviceMemory and hardwareConcurrency against actual performance?

Because these values are trivial to override — any multi-account browser, extension or script can set navigator.deviceMemory and navigator.hardwareConcurrency to whatever numbers it likes. So fingerprinting scripts run micro-benchmarks that stress the CPU or allocate memory, then compare measured performance against the declared specs. A profile reporting 8GB RAM and 16 cores that runs a parallel workload like a 4GB, 4-core laptop has an inconsistent identity. The timing reflects the real machine.

What's the difference between deviceMemory and hardwareConcurrency fingerprinting?

navigator.deviceMemory reports approximate RAM in gigabytes (values like 0.25, 0.5, 1, 2, 4, 8). navigator.hardwareConcurrency reports logical CPU cores. Both are used for fingerprinting because they're fairly stable per device but vary across devices. The difference: memory affects allocation benchmarks while cores affect parallel workload timing. More thorough scripts check both against actual measured performance.

Can I just set deviceMemory and hardwareConcurrency to common values to be less unique?

Partly. Common values (8GB RAM, 8 cores) make a browser less unique in a fingerprint population, which is a privacy gain. But if the actual machine has 4GB and 2 cores, benchmark timing will show the difference. The solution isn't just picking plausible values — it's matching what a profile declares to hardware that can back it up. JustBrowser sets both values at the C++ level; pair that with declared values at or below your real hardware so benchmarks agree.

Do all fingerprinting scripts check timing coherence?

No. Simple scripts just read the declared values. More thorough fraud and bot-management systems can add timing benchmarks, and their vendors don't publish exactly what they measure. For a site owner, that is why declared values alone are weak evidence. For a profile user, it is why the simplest consistent setup is one that reports the machine it runs on.


Try JustBrowser

A multi-account browser for teams, built on a custom Chromium engine — not extension-based. Native C++ engine patches at the canvas / WebGL / audio / font / screen layer provide 40+ identity parameters, and each profile keeps its own cookies, storage and proxy. Local REST API + CDP for Playwright, Puppeteer, Selenium. Windows x64 and macOS (Apple Silicon). $9.99/month or $99.99/year. 7-day free trial, card required — cancel any time in the seven days and you are not charged. Unlimited profiles.

Get started → · How it differs from Multilogin / GoLogin / AdsPower

Ready to manage multiple accounts?

Seven days free, then $9.99/month — one plan, everything included.

We'd like to use Google Analytics, a Google service, to understand how our website is used. It sets two cookies in your browser and runs only if you click Accept. You can change your choice at any time with Cookie settings. Cookie Policy

Sign-in cookies and the cookie that remembers this choice are always on; the website needs them to work.

Google Analytics, a Google service, helps us understand how our website is used. It sets two cookies, _ga and _ga_TVZHQ99TZW. It is now onoff in this browser. If your browser sends a Global Privacy Control or Do Not Track signal, it stays off. Cookie Policy