Battery, Sensor, and Pointer APIs: The Overlooked Fingerprint Leaks in 2026
Last week I watched a profile burn. Not metaphorically — I mean I sat there, monitoring network requests, and watched a platform's detection script run through its checks. Canvas: pass. WebGL: pass. Audio fingerprint: pass. The profile looked clean on every test site I'd run it through.
Then the script called navigator.getBattery().
Profile claimed to be a Galaxy S24 on mobile data. Battery API returned charging: true with chargingTime: Infinity. A plugged-in mobile phone reports finite charging time. Infinity means "desktop without a battery." Four seconds later: session terminated, account flagged.
One API call. Three lines of JavaScript. And suddenly 47 days of account warm-up meant nothing.
What We're Covering
Look — I'm not going to pretend I knew about Battery API fingerprinting before it burned me. I didn't. Learned the hard way. So here's what took me embarrassingly long to figure out:
- Which sensor and pointer APIs leak fingerprint data (and exactly how they're queried)
- How these APIs expose desktop-vs-mobile spoofing inconsistencies
- What each signal reveals about your real device
- How to test your antidetect profiles for these specific leaks
- What a proper fix looks like (and why JavaScript overrides don't cut it)
The audio/font/hardware fingerprinting post covered AudioContext and navigator.hardwareConcurrency. This is the sequel — different signals, same problem: vectors that most antidetect browsers ignore. Frankly, I ignored them too until that Galaxy S24 incident.
Prerequisites
- An antidetect browser profile (JustBrowser, Multilogin, GoLogin, whatever you're testing)
- Basic JavaScript — you'll be running console commands
- Understanding of why fingerprint consistency matters (if not, start with the CreepJS walkthrough)
- 20 minutes
Step 1: Understanding the Battery Status API
The Battery Status API was designed to let web apps adapt to low-battery situations. Show a warning before the user's device dies. Reduce animation complexity to save power. Reasonable use cases, honestly.
But — and this is what annoyed me when I first learned it — the API also exposes:
navigator.getBattery().then(battery => {
console.log('Level:', battery.level); // 0.0 to 1.0
console.log('Charging:', battery.charging); // true or false
console.log('Charging time:', battery.chargingTime); // seconds or Infinity
console.log('Discharging time:', battery.dischargingTime); // seconds or Infinity
});
Your exact battery percentage. Whether you're plugged in. How long until full charge. How long until dead.
Research from Princeton (2016) showed that battery.level combined with chargingTime could uniquely identify users across sessions — the precise percentage (0.87 vs 0.86) combined with the estimated charge time creates a near-unique fingerprint. Firefox removed the API entirely that same year. Chrome kept it. Because of course Chrome kept it.
For antidetect purposes, the bigger problem isn't the fingerprint itself. It's the consistency check. And this is where I think a lot of people (myself included, historically) get sloppy.
Desktop without battery: charging: true, chargingTime: Infinity, dischargingTime: Infinity, level: 1.0
Plugged-in laptop: charging: true, chargingTime: [some number], level: [varies]
Mobile on battery: charging: false, dischargingTime: [some number], level: [varies]
If your profile claims to be a Galaxy S24 but returns desktop battery values, you've failed a basic plausibility check. I've seen detection scripts that don't even hash the battery state — they just check chargingTime === Infinity when the user-agent claims mobile. Binary pass/fail. Takes 10 lines of code.
Open your antidetect browser's profile and run that snippet in the console. What do you get? Is it consistent with what the profile claims to be?
Step 2: DeviceOrientation and DeviceMotion
These APIs access your device's accelerometer, gyroscope, and magnetometer:
window.addEventListener('deviceorientation', event => {
console.log('Alpha (compass):', event.alpha); // 0-360 degrees
console.log('Beta (tilt forward):', event.beta); // -180 to 180
console.log('Gamma (tilt side):', event.gamma); // -90 to 90
});
window.addEventListener('devicemotion', event => {
console.log('Acceleration:', event.acceleration);
console.log('Rotation rate:', event.rotationRate);
});
Desktop browsers behave one of two ways:
- Don't fire the events at all
- Fire events with
nullvalues for alpha/beta/gamma
Mobile browsers on real devices fire constantly — these sensors update 60 times per second. The values change as you tilt the phone. Even a phone lying flat on a table reports near-constant values with tiny fluctuations (sensor noise).
Detection approach: add an event listener, wait 500ms, check how many events fired and whether values are realistic. A profile claiming iPhone that fires zero orientation events in 500ms? Suspicious. A profile claiming iPhone that fires events with constant alpha: null, beta: null, gamma: null? Definitely spoofed.
The sneaky version: check whether values have realistic variance. Real sensors have noise. They fluctuate by 0.01-0.1 degrees between readings. A spoof that returns perfectly stable values (exactly beta: 45.00 every single time) doesn't match real hardware. It's like a random number generator that produces "4, 4, 4, 4, 4" — technically possible, statistically implausible.
I've tested several antidetect browsers against this. Most fired no events at all on mobile profiles. One fired events with null values. For a deeper comparison, see our JustBrowser vs Multilogin 2026 comparison.
Guess which one passed detection scripts. (I'll admit I was skeptical of "synthetic sensor noise" as a concept until I saw the alternative: immediate flagging.)
Step 3: The Ambient Light Sensor API
This one's less common but shows up in sophisticated detection stacks:
if ('AmbientLightSensor' in window) {
const sensor = new AmbientLightSensor();
sensor.addEventListener('reading', () => {
console.log('Illuminance:', sensor.illuminance); // lux value
});
sensor.start();
}
The API reports ambient light levels in lux. Useful for adapting screen brightness. But also a fingerprint signal — indoor lighting (100-500 lux), outdoor shade (1000-5000 lux), direct sunlight (10000+ lux).
Most desktop browsers don't support this API or return permission errors. Most mobile browsers support it but require HTTPS and sometimes user permission. The detection angle: if a profile claims mobile but AmbientLightSensor is undefined (or never fires readings), that's a consistency gap.
Chrome on desktop: 'AmbientLightSensor' in window returns false
Chrome on Android: returns true (though readings may require permission)
Some detection scripts just check whether the API exists, not whether it returns data. A desktop antidetect profile spoofing Android should probably define AmbientLightSensor in the window object — but if it does, now it needs to handle permission flows and return plausible data. Partial spoofing is often worse than none.
Step 4: Pointer and Touch Event Inconsistencies
This is where things get granular. And honestly? Where most detection scripts live in 2026, because it's so stupidly simple to implement.
document.addEventListener('pointerdown', event => {
console.log('Pointer type:', event.pointerType); // 'mouse', 'touch', or 'pen'
console.log('Width:', event.width); // contact geometry
console.log('Height:', event.height);
console.log('Pressure:', event.pressure); // 0.0 to 1.0
console.log('Tilt X:', event.tiltX); // pen angle
console.log('Tilt Y:', event.tiltY);
});
Desktop Chrome with a mouse: pointerType: 'mouse', pressure: 0.5 (always 0.5 for mouse down), width: 1, height: 1, no tilt values.
Mobile Chrome with touch: pointerType: 'touch', pressure: varies (based on finger pressure if supported), width and height reflect finger contact size (often 20-40 pixels), tilt values may populate.
If your antidetect profile claims to be mobile but every PointerEvent has pointerType: 'mouse', width/height of 1, and constant pressure — you're clearly on desktop. Detection scripts capture a handful of click events and analyze the metadata. No visual tell on your end. You click a button. The script checks whether the click looks like a finger or a mouse.
This one genuinely frustrates me. It's such a basic check, yet I see people lose accounts to it constantly. We covered similar issues with timezone and geolocation spoofing mismatches — same principle, different signals.
TouchEvent is even more specific:
document.addEventListener('touchstart', event => {
const touch = event.touches[0];
console.log('Client X:', touch.clientX);
console.log('Client Y:', touch.clientY);
console.log('Radius X:', touch.radiusX); // horizontal radius of touch
console.log('Radius Y:', touch.radiusY); // vertical radius
console.log('Rotation angle:', touch.rotationAngle); // contact ellipse rotation
console.log('Force:', touch.force); // pressure
});
Desktop Chrome doesn't fire TouchEvents at all by default (unless you enable touch emulation in DevTools — which has its own detection signals). Real mobile touch events have radiusX and radiusY values reflecting finger size, force reflecting pressure, rotationAngle if the touch contact is elliptical.
A sophisticated detection script: listen for both PointerEvents and TouchEvents. On a real mobile device, both fire. On a spoofed mobile profile running on desktop hardware — usually only PointerEvents fire, and they have mouse characteristics.
Step 5: Testing Your Profiles
Here's a quick diagnostic you can run in any browser console:
// Battery API check
if (navigator.getBattery) {
navigator.getBattery().then(b => {
console.log('Battery API:', {
charging: b.charging,
level: b.level,
chargingTime: b.chargingTime,
dischargingTime: b.dischargingTime
});
});
} else {
console.log('Battery API: not supported');
}
// Sensor API checks
console.log('DeviceOrientation:', 'ondeviceorientation' in window);
console.log('DeviceMotion:', 'ondevicemotion' in window);
console.log('AmbientLightSensor:', 'AmbientLightSensor' in window);
// Pointer characteristics (interact with page to trigger)
let pointerSample = null;
document.addEventListener('pointerdown', e => {
if (!pointerSample) {
pointerSample = {
type: e.pointerType,
width: e.width,
height: e.height,
pressure: e.pressure
};
console.log('Pointer sample:', pointerSample);
}
}, { once: true });
console.log('Click anywhere to capture pointer data...');
Run this on your antidetect profile. Compare the output to what a real device of that type would report.
For mobile profiles:
- Battery should show realistic mobile values (not
chargingTime: Infinityunless explicitly claiming plugged-in) - DeviceOrientation should fire events with non-null values
- Pointer events should show
pointerType: 'touch'with width/height > 1
For desktop profiles:
- Battery API returning desktop values is expected
- No DeviceOrientation events or null values is normal
- Pointer events with
pointerType: 'mouse'is correct
The problem is mismatch. Claiming iPhone, returning desktop battery. Claiming Windows laptop, firing orientation events. Either direction flags you.
Step 6: Why JavaScript Overrides Don't Work
Someone in a forum last month posted a "fix" — a Tampermonkey script that overrides navigator.getBattery() to return fake mobile values. Looked clever. I even tried it myself, briefly. Doesn't work.
Three reasons (and I wish I'd known this before wasting an afternoon on it):
Timing analysis. Native API calls complete in ~0.1ms. JavaScript wrapper functions add ~5-10ms of overhead. Detection scripts measure how long getBattery() takes to resolve. A promise that resolves in 8ms instead of 0.1ms is obviously wrapped.
Prototype integrity. The Battery Manager object returned by getBattery() has a specific prototype chain. Wrapped objects have different prototypes or missing internal slots. CreepJS checks this explicitly.
Worker isolation. Your Tampermonkey script injects into the main thread. Service Workers and dedicated Workers call the real API. Same Worker-vs-main-thread trick that exposes extension-based canvas spoofing. (We covered this in the CreepJS walkthrough — the lies detector specifically checks cross-context consistency.)
The actual fix requires intercepting these APIs at the browser engine level — C++ modifications in Chromium that return per-profile values before JavaScript even touches them. That's what a native antidetect browser does. And it's why extension wrappers and JavaScript overrides fail against modern detection. The WebGL fingerprinting deep-dive explains the same native-vs-extension gap for GPU detection.
JustBrowser spoofs the hardware surface it covers at the C++ level — hardwareConcurrency, deviceMemory, cpuPerformance tier, screen/DPR, platform and UA-CH — so a desktop profile stays internally consistent. Battery, sensor and pointer APIs are not spoofed; keep profiles on desktop identities so those APIs report values consistent with a desktop. Because the values are set in the engine rather than by a script, there is no JavaScript override for a detector to spot.
Common Errors and Fixes
Error: Battery API returns desktop values on mobile profile
Cause: Your antidetect browser doesn't modify Battery Status API responses based on profile configuration.
Fix: Switch to an antidetect browser with native battery spoofing, or — and I know this sounds defeatist — avoid mobile profiles entirely if your current tool doesn't support it. There's no JavaScript-level fix that survives detection. For ad verification workflows, this matters especially when testing mobile ad renders.
Error: DeviceOrientation events not firing on mobile profile
Cause: Desktop hardware doesn't have accelerometer/gyroscope. Your browser isn't synthesizing events.
Fix: Use an antidetect browser that generates synthetic sensor events for mobile profiles. Or restrict your mobile profiles to platforms that don't check orientation (rare in 2026).
Error: Pointer events show mouse characteristics on mobile profile
Cause: You're using a physical mouse on desktop hardware. The antidetect browser isn't intercepting PointerEvent metadata.
Fix: Native-engine antidetect browsers can override pointer event properties at the Chromium level. Extension-based tools can't. If you need mobile profiles that survive pointer analysis, you need a native fork.
Error: Profile passes individual tests but still gets flagged
Cause: Cross-signal consistency check. Battery says desktop, orientation says mobile, pointer says mouse — the combination is impossible on real hardware.
Fix: Every signal needs to tell the same story. If you're claiming iPhone, battery must be mobile, orientation must fire events, pointer must be touch, AmbientLightSensor should exist. This is why per-profile device assignment matters — and why tools that let you toggle individual parameters without ensuring consistency create detectable profiles. (I have opinions about those tools. Strong ones.)
Next Steps
If you're running mobile profiles for any serious purpose — multi-accounting, ad verification, OSINT collection — these APIs need to be on your checklist.
-
Audit current profiles against the diagnostic script above. Document what each profile returns for battery, orientation, and pointer characteristics.
-
Compare against real devices. If you have access to actual mobile hardware, run the same diagnostic and compare. The delta shows your detection surface.
-
Test against detection services. CreepJS checks some of these signals. FingerprintJS Pro checks others. Run both — we covered this in our CreepJS test walkthrough. For conversion tracking on tested funnels, JustAnalytics helps monitor without adding fingerprint surface.
-
Re-test after updates. Both your antidetect browser and platform detection systems update constantly. A profile that passed in May might fail in July. Monthly spot-checks aren't paranoid — they're operational hygiene.
The sensor APIs are lower-profile than canvas or WebGL. They don't get the marketing attention. But in 2026, they're in production detection stacks at every major platform. The antidetect browsers that handle them pass. The ones that don't?
Well. You saw what happened to that Galaxy S24 profile. Forty-seven days of warm-up, gone because I assumed "the obvious stuff" was enough. It wasn't. Probably won't make that mistake again.
Frequently Asked Questions
Does the Battery Status API still work in 2026?
On Chromium-based browsers, yes. Firefox removed it in 2016. Safari never implemented it. But Chrome, Edge, Brave, and Chromium forks still expose navigator.getBattery() — which returns charging state, battery level, and estimated charge/discharge time. Antidetect browsers built on Chromium inherit this API unless they explicitly patch it out. If you're spoofing a mobile device on a plugged-in desktop, the mismatch is detectable.
How do platforms use DeviceOrientation for fingerprinting?
DeviceOrientation reports alpha (compass), beta (tilt forward/back), and gamma (tilt left/right) from a device's accelerometer and gyroscope. Desktop browsers either don't fire this event or return null values. Mobile browsers return real sensor data. If your antidetect profile claims to be an iPhone but never fires DeviceOrientation events — or fires them with desktop-like null values — platforms can detect the inconsistency.
What's the difference between PointerEvent and TouchEvent detection?
TouchEvent is mobile-specific (touchstart, touchmove, touchend). PointerEvent unifies mouse, touch, and pen input but includes a pointerType property that distinguishes them. A desktop Chrome fires PointerEvents with pointerType: 'mouse'. A mobile Chrome fires them with pointerType: 'touch'. If your profile claims mobile but only generates mouse pointer events, that's a mismatch. Real touch events also have properties like radiusX, radiusY, and rotationAngle that mouse events don't have.
Can antidetect browsers spoof sensor APIs reliably?
Extension-based antidetect browsers can't — sensor APIs are hardware-level and JavaScript overrides either break functionality or create timing inconsistencies. JustBrowser profiles are desktop identities (Windows, macOS or Linux), and on desktop these APIs report desktop-consistent values on their own — there is nothing to fake. We do not patch the battery or sensor APIs and we do not synthesize mobile sensor data, because a desktop identity has no need of either. The engine patches canvas, WebGL, audio, font and screen; sensors are not on that list, and a tool claiming synthetic orientation or battery events for a desktop profile is describing a leak rather than a defence.
Try JustBrowser
Native Chromium antidetect browser — not extension-based. Real C++ engine patches at the canvas / WebGL / audio / font / screen layer, so 40+ identity parameters are genuine, not faked. REST API for Playwright, Puppeteer, Selenium. $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
Related Posts
Ready to manage multiple accounts?
Seven days free, then $9.99/month — one plan, everything included.