The essentials
Quick reference
One focused task per row. Jump to the related section for complete, working examples.
| Use | Syntax | Examples |
|---|---|---|
| Check the transport context | if (!isSecureContext) throw new Error('Device access requires HTTPS') | View examples |
| Read geolocation permission state | const status = await navigator.permissions.query({ name: 'geolocation' }) | View examples |
| Observe a permission change | status.addEventListener('change', () => updateControls(status.state)) | View examples |
| Restrict location to this origin | Permissions-Policy: geolocation=(self) | View examples |
| Disable camera and microphone | Permissions-Policy: camera=(), microphone=() | View examples |
| Delegate location to one frame | <iframe
src="https://maps.example/"
allow="geolocation"
title="Store map">
</iframe> | View examples |
| Request one position | navigator.geolocation.getCurrentPosition(onPosition, onError, { timeout: 10000, maximumAge: 60000 }) | View examples |
| Watch position changes | const watchId = navigator.geolocation.watchPosition(onPosition, onError, options) | View examples |
| Stop watching location | navigator.geolocation.clearWatch(watchId) | View examples |
| Request camera and microphone | const stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true }) | View examples |
| Stop every media track | stream.getTracks().forEach(track => track.stop()) | View examples |
| List exposed media devices | const devices = await navigator.mediaDevices.enumerateDevices() | View examples |
| Request a display surface | const stream = await navigator.mediaDevices.getDisplayMedia({ video: true, audio: false }) | View examples |
| Detect stopped screen sharing | stream.getVideoTracks()[0].addEventListener('ended', handleShareEnded) | View examples |
| Request orientation access | const state = await DeviceOrientationEvent.requestPermission() | View examples |
| Listen for orientation changes | addEventListener('deviceorientation', handleOrientation) | View examples |
| Stop orientation updates | removeEventListener('deviceorientation', handleOrientation) | View examples |
| React to page visibility | document.addEventListener('visibilitychange', () => { if (document.hidden) stopSensitiveAccess() }) | View examples |
Location, cameras, microphones, screens, and motion sensors expose intimate data. Ask only after the user chooses a feature whose benefit is clear, request the minimum capability, and make refusal a complete product state rather than an exception page. A browser permission, a secure context, and Permissions Policy are independent gates; satisfying one does not satisfy the others. Handle revocation and hardware failure at use time, stop sensors promptly, and never infer that a permission-state query guarantees the next operation will succeed.
Step by step
Detailed examples
Treat support, secure context, policy, consent, and availability as separate gates
Most powerful device APIs are restricted to secure contexts, but HTTPS alone never grants access. Permissions Policy can disable or delegate a feature, the user agent may require express permission, the operating system may deny hardware access, and a device can disappear or already be busy. Permissions API query reads the current state for supported descriptors without prompting; descriptor support varies and query can reject. granted means a prompt is not presently required, not that the operation will succeed. prompt means the feature's own request may ask, not that calling query should ask. Permission grants can expire or be revoked, so handle the actual API result and observe changes only as a UI optimization.
async function prepareLocationButton(button) {
if (!isSecureContext || !navigator.geolocation) {
button.hidden = true;
return;
}
try {
const status = await navigator.permissions.query({ name: 'geolocation' });
const render = () => button.dataset.permission = status.state;
status.addEventListener('change', render);
render();
} catch {
button.dataset.permission = 'unknown';
}
} Set the top-level Permissions Policy before delegating to frames
The Permissions-Policy response header defines an upper bound for the response and descendant frames. An empty allowlist disables a feature; self permits the response's own origin; explicit serialized origins selectively widen access. Cross-origin frames generally do not receive powerful features by default, and an iframe allow attribute delegates only within the header's upper bound—it cannot override a header that already disabled the feature. Camera, microphone, geolocation, display-capture, accelerometer, gyroscope, and magnetometer use distinct policy names. Grant the smallest set to a trusted origin, give the frame a meaningful title, sandbox it where compatible, and remember that delegation merely allows that frame to ask the user.
Permissions-Policy: geolocation=(self "https://maps.example"), camera=(), microphone=(), display-capture=(), accelerometer=(), gyroscope=(), magnetometer=()
<iframe src="https://maps.example/store-locator" allow="geolocation" title="Store locator map"></iframe> Request the least precise location that solves the user's task
Geolocation is secure-context-only, permission-gated, and policy-controlled. In the current specification, acquisition waits until the document is visible. getCurrentPosition obtains one reading; watchPosition continues until clearWatch and can increase battery and privacy cost. Set timeout and maximumAge from the product's real freshness needs, and enableHighAccuracy only when coarse results are insufficient because it may consume more power or take longer. accuracy is an estimate in metres, not certainty, and the API does not reveal how a position was derived. Keep manual address, region, or map selection available, minimize retention, explain downstream sharing, and never log raw coordinates by default.
function locateStore() {
return new Promise((resolve, reject) => {
navigator.geolocation.getCurrentPosition(
position => resolve({
latitude: position.coords.latitude,
longitude: position.coords.longitude,
accuracyMetres: position.coords.accuracy
}),
error => reject(new Error(`Location unavailable (${error.code})`)),
{ enableHighAccuracy: false, timeout: 10000, maximumAge: 60000 }
);
});
} const options = { enableHighAccuracy: false, timeout: 15000, maximumAge: 30000 };
const watchId = navigator.geolocation.watchPosition(renderPosition, renderError, options);
function stopLocationWatch() {
navigator.geolocation.clearWatch(watchId);
}
addEventListener('pagehide', stopLocationWatch, { once: true }); Acquire only the media tracks needed and make capture visible
getUserMedia is exposed through navigator.mediaDevices in secure contexts and is gated separately for camera and microphone by permission and Permissions Policy. Request only the kinds and constraints the user selected; overly exact constraints can fail before a useful choice exists and may reveal capability information. A granted permission does not guarantee success because devices can be absent, unavailable, or fail constraints. enumerateDevices is also privacy-gated: the list is filtered by policy, and labels or stable identifiers may remain unavailable until capture is active or permission exists. Attach streams to intended elements, provide a persistent stop control and status indicator, and stop every track on cancellation, navigation, or teardown.
let cameraStream;
async function startCamera(video) {
cameraStream = await navigator.mediaDevices.getUserMedia({
video: { width: { ideal: 1280 }, facingMode: 'user' },
audio: false
});
video.srcObject = cameraStream;
await video.play();
}
function stopCamera(video) {
cameraStream?.getTracks().forEach(track => track.stop());
cameraStream = undefined;
video.srcObject = null;
} Start every display share from a direct user action
getDisplayMedia requires a secure context, transient user activation, express selection in the browser's chooser, and permission from the display-capture policy-controlled feature. The browser must let the user choose a display surface each time; an application cannot persist a granted state or use constraints to silently narrow the chooser before selection. Call it directly from a clearly labeled click, describe whether audio is requested, and expect InvalidStateError or NotAllowedError when activation, policy, or consent requirements are unmet. Listen for track ended because users can stop sharing through browser chrome. Avoid the hall-of-mirrors effect, warn before sharing an entire monitor, and never treat a captured screen as proof of identity or authorization.
document.querySelector('#share-screen').addEventListener('click', async () => {
const status = document.querySelector('#share-status');
try {
const stream = await navigator.mediaDevices.getDisplayMedia({ video: true, audio: false });
const [track] = stream.getVideoTracks();
track.addEventListener('ended', () => { status.textContent = 'Screen sharing stopped.'; });
document.querySelector('#screen-preview').srcObject = stream;
status.textContent = 'Screen sharing is active.';
} catch (error) {
status.textContent = error.name === 'NotAllowedError' ? 'Screen sharing was not allowed.' : 'Screen sharing is unavailable.';
}
}); Feature-detect motion permission and provide equivalent controls
Device orientation and motion expose sensor-derived data only in secure contexts and are controlled through accelerometer, gyroscope, and sometimes magnetometer permissions and policy features. The current specification defines static requestPermission methods, but deployed behavior varies, so test for the method and call it directly from user activation where present. Register listeners only after access is granted, remove them promptly, and expect null or reduced-precision values. Coordinate meanings differ from CSS transforms, and events are not a dependable compass without an appropriate absolute reference. Always provide keyboard, pointer, and explicit UI alternatives; motion-only interaction excludes users and accidental motion must be reversible.
async function enableOrientation() {
if (!isSecureContext || !('DeviceOrientationEvent' in window)) return false;
if (typeof DeviceOrientationEvent.requestPermission === 'function') {
const state = await DeviceOrientationEvent.requestPermission();
if (state !== 'granted') return false;
}
addEventListener('deviceorientation', handleOrientation);
return true;
}
function handleOrientation(event) {
if (event.beta == null || event.gamma == null) return;
renderTilt({ frontBack: event.beta, leftRight: event.gamma });
} Note: Invoke enableOrientation directly from the user's Enable motion button; do not call it on load.
Design denial, revocation, interruption, and cleanup as normal states
Users may dismiss a prompt, revoke a previous grant, end display capture, cover a camera, disconnect hardware, background the page, or let a temporary grant expire. Map DOMException names to useful product states without exposing raw diagnostics as blame, and never loop prompts after denial. Keep manual input and upload fallbacks available where possible. Stop media tracks, geolocation watches, and sensor listeners when the task, component, or page lifecycle ends; clear rendered frames and discard high-sensitivity data according to a documented retention policy. Visible indicators inside the application complement, but never replace, browser and operating-system capture indicators.
const active = { media: undefined, watchId: undefined, orientation: false };
function stopSensitiveAccess() {
active.media?.getTracks().forEach(track => track.stop());
active.media = undefined;
if (active.watchId !== undefined) navigator.geolocation.clearWatch(active.watchId);
active.watchId = undefined;
if (active.orientation) removeEventListener('deviceorientation', handleOrientation);
active.orientation = false;
}
document.addEventListener('visibilitychange', () => {
if (document.hidden) stopSensitiveAccess();
});
addEventListener('pagehide', stopSensitiveAccess); Sources and further reading
References
Authoritative documentation used to verify and expand this cheat sheet.
Help us improve
Found a typo or missing example?
Tell us what would make this cheat sheet clearer, more complete, or more useful.



