LEARNING OBJECTIVES ⌵
- Query hardware battery status asynchronously using
navigator.getBattery(). - Inspect and interpret
charging,level,chargingTime, anddischargingTimeproperties on theBatteryManagerinterface. - Listen to real-time power state transitions using
levelchangeandchargingchangeevents. - Implement an adaptive energy-saving UI mode that automatically downgrades CPU/GPU load when the device is low on battery.
- Understand why Firefox and Safari removed the Battery Status API due to hardware fingerprinting concerns.
📖 The Mental Model & Story (Intuitive Foundation)
Imagine driving an electric vehicle on a long cross-country road trip. When the battery drops below $15%$, the car's dashboard automatically enters "Eco-Mode": it dims the cabin displays, throttles air conditioning, and reroutes navigation to the nearest fast charger.
[ DEVICE HARDWARE BATTERY GAUGE ]
│
▼
+─────────────────────────────+
│ navigator.getBattery() │
│ (Resolves BatteryManager) │
+─────────────────────────────+
│
┌──────────────────┴──────────────────┐
▼ ▼
[ POWER TELEMETRY ] [ LIFECYCLE EVENTS ]
- .charging (true/false) - 'chargingchange'
- .level (0.0 to 1.0) - 'levelchange'
- .chargingTime (seconds) - 'chargingtimechange'
- .dischargingTime (seconds) - 'dischargingtimechange'
│ │
└──────────────────┬──────────────────┘
│
▼
+─────────────────────────────────────+
│ ADAPTIVE WEB ARCHITECTURE │
│ • Low Power (<20% & Discharging): │
│ - Throttle 60fps canvas to 30fps │
│ - Disable heavy CSS blur filters │
│ - Pause background polling │
│ • High Power (Charging / 100%): │
│ - Full 120fps animations & WebGL │
+─────────────────────────────────────+
The W3C Battery Status API exposes this same power intelligence to client-side web applications. Instead of blindly running intensive animations, WebGL shaders, or heavy background network polling, your app can gracefully adapt to the user's real-world power constraints.
Technical Deep Dive & Specifications
The BatteryManager Interface
Invoking navigator.getBattery() returns a Promise that resolves with a BatteryManager instance:
interface Navigator {
getBattery?(): Promise<BatteryManager>;
}
interface BatteryManager extends EventTarget {
readonly attribute boolean charging;
readonly attribute double chargingTime; // Seconds until 100% (or 0 / Infinity)
readonly attribute double dischargingTime; // Seconds until 0% (or Infinity)
readonly attribute double level; // 0.0 (0%) to 1.0 (100%)
attribute EventHandler onchargingchange;
attribute EventHandler onchargingtimechange;
attribute EventHandler ondischargingtimechange;
attribute EventHandler onlevelchange;
}
Telemetry Properties Matrix
| Property | Type | Range / Format | Behavior & Edge Cases |
|---|---|---|---|
level |
number |
0.0 to 1.0 |
1.0 represents $100%$, 0.5 represents $50%$. Updated in discrete steps by the OS. |
charging |
boolean |
true or false |
true if connected to AC/USB power, false if running on battery. |
chargingTime |
number |
Seconds or Infinity |
Seconds until battery is full. If fully charged, returns 0. If discharging or unable to calculate, returns Infinity. |
dischargingTime |
number |
Seconds or Infinity |
Seconds until battery is empty. If charging or unable to estimate, returns Infinity. |
The Four Reactive Event Listeners
+---------------------------------------------------------------------------------------------------+
| BATTERYMANAGER EVENT DISPATCH PIPELINE |
+---------------------------------------------------------------------------------------------------+
| |
| [ User Plugs In Charger ] ──────────────► 'chargingchange' (battery.charging = true) |
| |
| [ Charge Percentage Drops ] ────────────► 'levelchange' (battery.level: 0.85 -> 0.84) |
| |
| [ OS Recalculates Charge Time ] ────────► 'chargingtimechange' (battery.chargingTime updated) |
| |
| [ OS Recalculates Drain Time ] ─────────► 'dischargingtimechange' (battery.dischargingTime) |
| |
+---------------------------------------------------------------------------------------------------+
The Privacy & Fingerprinting Controversy
[!WARNING] Browser Support Status: In 2016, security researchers demonstrated that the combination of high-precision
level(e.g.0.562341) anddischargingTimecreated a unique short-term tracking fingerprint across incognito sessions. As a result:
- Mozilla Firefox: Removed the API in Firefox 52.
- Apple Safari / WebKit: Never implemented the API.
- Chromium (Chrome, Edge, Brave, Opera, Samsung Internet): Retained support, but rounds values (e.g.,
levelquantized to 2 decimal places) and restricts access in cross-origin iframes.Always test
if ('getBattery' in navigator)before calling!
💻 Interactive Code Playground
Starter Code
Line-by-Line Code Breakdown
- Lines 131–137: Formats raw seconds into human-readable hours and minutes strings (e.g.
2h 15m), safely guarding againstInfinity. - Lines 139–153: Converts the fractional
battery.level($0.0 \dots 1.0$) into an integer percentage and dynamically changes the battery bar color (Green $\to$ Amber $\to$ Red). - Lines 156–164: Checks
battery.chargingand toggles the charging lightning bolt icon. - Lines 169–178: Implements the Eco-Mode trigger: when the battery is $\le 20%$ and not charging, the application enters an energy-saving state.
- Lines 181–188: Evaluates
'getBattery' in navigatorto gracefully fail on unsupported browsers (Firefox, Safari). - Lines 195–198: Binds listeners to all four core battery lifecycle events (
chargingchange,levelchange,chargingtimechange,dischargingtimechange).
Expected Browser Render Output
🔋 Battery Status & Eco Engine
Real-time hardware power diagnostics and adaptive workload throttling.
┌───────────────────────┐
│ [████████████] 85% │ ▌ ⚡
└───────────────────────┘
POWER SOURCE BATTERY LEVEL
Plugged In (AC) 85%
TIME TO FULL TIME REMAINING
25 mins N/A
⚡ PERFORMANCE MODE: Normal operation.🏋️ Hands-On Exercise
🎯 The Challenge: Build an Adaptive WebGL / Canvas Frame Rate Governor
Instructions:
- Create a dynamic Canvas animation loop driven by
requestAnimationFrame. - Inspect the device battery state via
navigator.getBattery(). - If
battery.level <= 0.20 && !battery.charging:- Throttle the Canvas frame rate to 15 FPS.
- Display a "Power Saver: 15 FPS" indicator.
- If
battery.chargingorbattery.level > 0.20:- Run at full 60 FPS.
🏁 Starter Code Sandbox
⚠️ Common Pitfalls
- Assuming
dischargingTimeis Always a Number: When plugged into power or when the battery management microcontroller is calibrating,dischargingTimereturnsInfinity. Never divide by it without checkingNumber.isFinite(). - Neglecting Privacy Deprecation: Writing web apps that crash if
navigator.getBatteryis undefined will break completely for all Apple Safari and Mozilla Firefox users. Always check'getBattery' in navigator. - Over-Polling Battery Status: Never poll
getBattery()inside asetInterval(). It returns a singletonBatteryManagerthat pushes updates via event listeners (levelchange,chargingchange).
💡 Pro Tips
- Quantized Privacy Levels: Modern Chromium browsers intentionally round
battery.levelto the nearest $0.01$ (1%) or $0.05$ (5%) to prevent micro-entropy fingerprinting. - Combine Battery with Network Awareness: When both battery is $< 20%$ AND network is on metered connection (
navigator.connection.saveData === true), suspend all non-critical background fetch requests.
📌 Key Takeaways
navigator.getBattery()returns a Promise resolving to theBatteryManagerinterface.levelprovides battery percentage from0.0to1.0.chargingprovides a boolean indicating whether the device is receiving external power.- The four key events are
chargingchange,levelchange,chargingtimechange, anddischargingtimechange. - Firefox and Safari disabled this API to protect user privacy against persistent device fingerprinting.
- --