completefrontend
Open lab My progress
THE JAVASCRIPT FIELD GUIDE

Device & platform APIs

Use browser device APIs responsibly: clipboard, permissions, geolocation, notifications, and audio or video controls that depend on user trust.

By the end you can
  • 01
    Ask at the right timeCheck that the browser has a feature, and ask for permission only after the person clicks.
  • 02
    Handle a noShow a helpful message or another way for each permission state and geolocation error, instead of treating no as a failure.
  • 03
    Control sound and videoUse play() and its promise, media events, volume, and playbackRate on audio and video elements.

Device APIs need the person's trust

An API is a set of objects and functions that the browser gives your code. So far, your JavaScript has changed data, the page, forms, storage, and network requests. Device APIs reach further, into the person's own device. With them, a page can:

  • copy text to the clipboard, the place where copied text waits until you paste it, and read it back;
  • ask where the device is (this is called geolocation);
  • show notifications, the small messages that pop up from the operating system;
  • play sound and video.

These are useful. They also touch the person's privacy and attention, so the browser guards them. Good code follows the same steps every time:

  1. Check that this browser has the feature.
  2. Say why the page needs it.
  3. Wait until the person clicks or acts.
  4. Handle a "no".
  5. Keep another way to finish the task.

This lesson uses events from Introduction to events and promises from Promises. It also uses a Blob from the Files & Blobs lesson. A Blob is a chunk of raw data, like a small file made in code.

Real-life analogyPermissions are like a hotel key card

A hotel does not give every guest a key to every door. Each card opens only the doors it was made for. Browser permissions work the same way. Each feature needs its own yes, and the person can always say no.

In real life: Your card opens your room
In JavaScript: Allowing location lets the site use location
In real life: Other doors stay locked
In JavaScript: Allowing location does not allow notifications or clipboard reads
In real life: You can turn down the card
In JavaScript: The person can choose denied, or ignore the question
In real life: Staff say what the card is for
In JavaScript: A good page says why it asks, right when you want the feature
Ask only after a click

Every experiment in this lesson starts when you click a button, paste text, or press a play control. Nothing asks for the clipboard, your location, notifications, or sound when the page loads.

Secure pages and feature checks

TRUST FIRST

Most browsers allow these APIs only in a secure context. That means a page that came over HTTPS, or from localhost while you build on your own computer. HTTPS stops other people on the network from changing the page on its way to you. Without it, an attacker could change a page that asks for your location. No library or framework can skip this rule; the browser decides.

Browsers also differ in which APIs they have. This matters in React. React often builds the page first on a server, and the server has no navigator, no window, no permissions, and no speakers. So check for browser features only in the browser:

  • after the page has loaded there (React calls this after mount), or
  • inside a click handler.

If the server guesses one answer and the browser finds another, React sees two different pages. It then shows a warning called a hydration mismatch.

The step-through below is a small model of a good permission request. It shows the order: name the feature, see that the person has not decided yet, wait for a click, and say why before the real request.

Step through a careful permission request
Step 0 of 5Ready
Your turn: follow the blue line

Pick a feature. Guess which messages print, then step through.

Running in
  1. script
Next: line 1
const state = "prompt"; if (state === "prompt") {  console.log("wait for a click before requesting " + feature);} console.log("explain why the app needs " + feature);
CallStoreChangeResultBlue line: runs next. Ran: already executed.
Recent returnsNothing yet. Start with the blue line.
Choose the feature the page will ask for.
A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.

A feature check asks a smaller question than a permission does: "Does this browser have this function?" Check before you turn a button on. The code below uses a made-up browser object in place of the real navigator, so it gives the same answers everywhere.

Check that a feature exists before using itPop out in the code editor (opens in a new tab)JavaScript
const browser = {  mediaDevices: { getUserMedia() {} },  vibrate: true,  getBattery: false,}; console.log(typeof browser.mediaDevices?.getUserMedia === "function");console.log(browser.vibrate === true);console.log(typeof browser.getBattery === "function");

Line 7 uses ?. (optional chaining). If mediaDevices were missing, it would give undefined instead of an error. typeof gives the type of a value as text, so typeof x === "function" is true only for a function. In plain words, line by line:

  1. Line 7 prints true. getUserMedia is a function, so the page could ask for the microphone.
  2. Line 8 prints true. In this model, vibrate is true.
  3. Line 9 prints false. getBattery is false, not a function, so battery info is missing.
The made-up browser object has getUserMedia as a function, vibrate set to true, and getBattery set to false. The three checks print true, true, and false.in the browser objectcheck printsgetUserMedia()truevibrate: truetruegetBattery: falsefalse
Each check asks one question about the browser object. getUserMedia and vibrate are there, so they print true; getBattery is not a function, so it prints false.

Having a function is not the same as having permission. The function can exist, and the person can still say no.

Clipboard API: copy, paste, and read

INTERACTIVE

The Clipboard API lives at navigator.clipboard. Its most common use is copying text after a click, with writeText().

writeText() returns a promise. A promise is a value that later says "done" (it resolves) or "failed" (it rejects). So show "Copied" only after it resolves, and show the real error if it rejects.

Some browsers let a page write to the clipboard only right after the person clicks or presses a key. So call writeText() directly inside the click handler, not later.

Real-life analogyThe clipboard is like a tray that all apps share

Your page does not own the clipboard. Other apps use it too. So the browser makes your page show that the person really asked to put something there, or to take something from it.

In real life: Put a note on the tray
In JavaScript: navigator.clipboard.writeText(text)
In real life: Pick up what is on the tray
In JavaScript: navigator.clipboard.readText()
In real life: The person hands you the note
In JavaScript: A paste event with event.clipboardData
Clipboard lab: copy, paste, and read
Copy, paste, and read the clipboardPop out in the code editor (opens in a new tab)JavaScript
const text = messageInput.value;await navigator.clipboard.writeText(text);status.textContent = "Copied!"; pasteBox.addEventListener("paste", (event) => {  status.textContent = event.clipboardData.getData("text");}); const pasted = await navigator.clipboard.readText();
Your real clipboardChecking in the browser
Current field text(empty)
Try it yourself

Click Copy text, paste into the field, or try Read clipboard.

Every clipboard action starts from your click or paste. The lab checks for the Clipboard API only after the page loads in your browser.

The lab has two ways to read text:

  • The paste field. You choose Paste yourself, so the browser fires a paste event. The event carries the pasted text in event.clipboardData.
  • The Read clipboard button. It calls readText(), which asks the browser for whatever is on the clipboard right now. That is more private, so most browsers show a question or a small Paste menu first.

Check a permission without asking

DASHBOARD

A permission is the person's yes or no for one feature on one site. navigator.permissions.query() reads the current answer without showing the person a question. That helps you:

  • turn a button on or off;
  • show a helpful note next to a feature;
  • build a dashboard like the one below.

Not every browser knows every permission name. An unknown name can throw a TypeError. So the code catches that error, and you still check the feature itself.

Permissions dashboard: check without asking
Check several permissionsPop out in the code editor (opens in a new tab)JavaScript
const names = ["geolocation", "notifications", "camera"]; for (const name of names) {  try {    const status = await navigator.permissions.query({ name });    render(name, status.state);    status.addEventListener("change", () => render(name, status.state));  } catch (error) {    render(name, "unsupported");  }}
States in your browser0 checked
Reset cleared the list. Reload the lesson to check again.
Try it yourself
Checking for the Permissions API

This dashboard calls permissions.query() after the page loads. It never asks you for anything. It only shows what the browser is willing to tell.

The list mixes names on purpose. Some browsers throw a TypeError for names they don't know.

Each row below is one state that query() can report. Read across to see what it means and what your page should do.

What each permission state means for your page
StateWhat does it mean?What should the page do?
grantedThe person said yes, so this site may use the feature.Use the feature, and still show clear buttons to stop it.
promptThe person has not decided yet, or the browser won't tell you the answer in advance.Wait until the person clicks the feature. Then ask.
deniedThe person or the browser said no.Don't ask again. Say how to change the setting, and offer another way.
Unknown nameSome browsers throw a TypeError for permission names they don't know.Catch the error, and check the feature itself another way.
Three permission states. granted: use the feature, no new question. prompt: wait, and ask after a click. denied: don't ask again, offer another way.granteduse itno new questionpromptwaitask after a clickdenieddon't askoffer another way
granted means use the feature. prompt means ask only after a click, and denied means stop asking and offer another way, like typing a city.

A denied answer should change what the page offers. The page should not ask again and again. The code below keeps the task useful: when location is denied, the person types a city instead.

Offer another way after a noPop out in the code editor (opens in a new tab)JavaScript
const permission = "denied";const message = permission === "granted"  ? "Use device location"  : "Let the person type a city"; console.log(message);

In plain words, line by line:

  1. Line 1 sets the state to "denied".
  2. Lines 2 to 4 pick a message. Only "granted" gets the device location; every other state gets line 4.
  3. Line 6 prints Let the person type a city.

query() gives back a PermissionStatus object. It fires a change event when the person changes a browser setting while your page is open.

Remember: prompt does not mean yes. It means "not decided yet". Ask later, when the person wants this feature.

Geolocation: ask where the device is

INTERACTIVE

navigator.geolocation.getCurrentPosition() asks where the device is. It takes three arguments:

  • a success callback (a function the browser calls later), which gets the position;
  • an error callback, which gets an error with a number in code;
  • an options object.

The position holds latitude, longitude, and accuracy. Accuracy is in meters: how far off the answer might be.

getCurrentPosition calls one of two functions. Success gets coords with latitude 19.076, longitude 72.8777, and accuracy 42. Error gets a code: 1 denied, 2 unavailable, or 3 timeout.getCurrentPosition()successlatitude: 19.076longitude: 72.8777accuracy: 42error1: denied2: unavailable3: timeout
The browser calls only one of your two functions. Success gets the position, with accuracy in meters; error gets a code from 1 to 3.

The options let you choose:

  • enableHighAccuracy: try for a more exact answer. It may be slower or use more battery.
  • timeout: how many milliseconds to wait before giving up.
  • maximumAge: how old a saved answer may be, in milliseconds.
Real-life analogyGeolocation is like asking a stranger where you are

You can ask politely, but you can't demand a perfect answer. Good apps plan for a refusal and for a rough answer.

In real life: They don't want to answer
In JavaScript: Error code 1: PERMISSION_DENIED
In real life: They point roughly down the street
In JavaScript: Coordinates plus an accuracy in meters
In real life: They take too long
In JavaScript: Error code 3: TIMEOUT
In real life: They repeat what they told you a minute ago
In JavaScript: maximumAge allows a saved answer
Geolocation lab: share or simulate
Ask for the positionPop out in the code editor (opens in a new tab)JavaScript
navigator.geolocation.getCurrentPosition(  (position) => {    const { latitude, longitude, accuracy } = position.coords;    show(formatCoordinates(latitude, longitude, accuracy));  },  (error) => show(mapGeolocationError(error.code)),  { enableHighAccuracy: true, timeout: 8000, maximumAge: 60000 },);
Location answerChecking in the browser

Click Share my location. If you'd rather not share, click Simulate Mumbai result.

Try it yourself

Click Share my location. If you'd rather not share, click Simulate Mumbai result.

The lab never saves or sends your coordinates. Simulate Mumbai result shows a success without sharing your real location.

This lesson rounds coordinates to two decimal places so they are easy to read. A real app should decide what it needs: an exact location, a rough area, or a city the person types. The lab never saves or sends your coordinates. If you don't want to share, use Simulate Mumbai result.

Notifications: ask only when the person wants them

CLICK ONLY

The Notifications API keeps its own permission in Notification.permission. It has one of three values: granted, denied, or default (not decided yet).

To ask, call Notification.requestPermission(). Call it only after the person chooses a notification feature, such as a "Remind me" button.

Notifications lab: ask on click
Ask for notificationsPop out in the code editor (opens in a new tab)JavaScript
if (Notification.permission === "default") {  const permission = await Notification.requestPermission();  if (permission === "granted") {    new Notification("Device APIs lab", { body: "Thanks for opting in." });  }}
Notification.permissionunknown

Check another way

Try it yourself

Click the button only if you want to test notifications. It asks for permission first.

Real apps usually send notifications from a service worker. Some platforms allow web notifications only for web apps you installed.

A page can show a test notification while it is open. Real apps usually send notifications from a service worker, a script that keeps running in the background, so messages arrive even when the page is not in front. Some platforms limit web notifications even more, for example to web apps that the person installed. When browsers differ like this, say "in most browsers", not "always".

Audio and video share one API

LIVE AUDIO

An <audio> or <video> element is an HTMLMediaElement. Both have the same tools:

  • methods: play() and pause();
  • properties: currentTime (seconds played), volume (0 to 1), and playbackRate (speed, where 1 is normal);
  • events: play, pause, timeupdate (the time moved on), and ended.
Real-life analogyA media element is like a TV remote

You send commands, and the TV tells you whether they worked. In the same way, play() gives you a promise, and media events keep your buttons and clock up to date.

In real life: Press Play
In JavaScript: Call audio.play() and wait for its promise
In real life: The TV won't start with sound
In JavaScript: The promise can reject with NotAllowedError
In real life: Change the volume or speed
In JavaScript: Set volume or playbackRate
In real life: The TV shows what happened
In JavaScript: Listen for play, pause, timeupdate, and ended
audio.play() returns a promise. If it resolves, the code logs play() resolved. If the browser blocks it, it rejects and the code logs the error name, NotAllowedError.audio.play()resolvesplay() resolvedNotAllowedErrorrejects
play() gives back a promise. If it resolves, line 5 logs play() resolved; if the browser blocks the sound, line 7 logs the error name, often NotAllowedError.
Media lab: play a sound, watch the promise and events
Play, then change volume and speedPop out in the code editor (opens in a new tab)JavaScript
const audio = document.querySelector("audio"); try {  await audio.play();  log("play() resolved");} catch (error) {  log(error.name);} audio.volume = 0.4;audio.playbackRate = 1.25;audio.addEventListener("timeupdate", updateClock);
Sound filemade in this page
Volume0.35
Playback rate1×
Current time0.00s
Event log
  1. The page makes a short sound file after it loads.
Try it yourself

currentTime is 0.00s. The page makes a short sound file after it loads.

The page makes a tiny WAV sound file in code, so nothing is downloaded. A video element has the same methods and events.

To use the camera or microphone, call navigator.mediaDevices.getUserMedia(). It needs a secure page and the person's permission. So ask from a button, and stop each track (one stream of sound or video) when you no longer need it. You can't run the code below here, because its result depends on a real microphone and the browser's question.

Ask for the microphone after a clickJavaScript
async function askForMicrophone() {  if (!navigator.mediaDevices?.getUserMedia) {    throw new Error("Microphone capture is unavailable here.");  }  return navigator.mediaDevices.getUserMedia({ audio: true });} button.addEventListener("click", async () => {  try {    const stream = await askForMicrophone();    status.textContent = "Microphone ready.";    stream.getTracks().forEach((track) => track.stop());  } catch (error) {    status.textContent = error instanceof Error ? error.name : "Microphone unavailable.";  }});

In plain words, line by line:

  1. Line 2 checks that getUserMedia exists. If not, line 3 throws an error.
  2. Line 5 asks for sound only: { audio: true }.
  3. Line 8 waits for a click on the button.
  4. Line 10 waits for the person's answer.
  5. Line 12 stops every track, so the microphone turns off again.

Vibration is a small extra signal, and many devices don't have it. Check with "vibrate" in navigator first, and respect people who turn off motion. Battery info is missing even more often: navigator.getBattery() may not exist, or may be blocked. Never make the main task depend on the battery level.

Vibration and battery are extrasJavaScript
if ("vibrate" in navigator) {  navigator.vibrate(200);} if ("getBattery" in navigator) {  const battery = await navigator.getBattery();  console.log(Math.round(battery.level * 100) + "%");} else {  console.log("Battery status is unavailable.");}

In plain words, line by line:

  1. Lines 1 to 3 vibrate for 200 milliseconds, but only if vibrate exists.
  2. Lines 5 to 7 read the battery and print its level as a percent.
  3. Line 9 prints a message when battery info is missing.

The media lab makes a tiny WAV sound file inside the page. It plays it from a Blob URL, a short address that points to data made in the page, so nothing is downloaded.

The Play button matters because of autoplay rules. In most browsers, starting sound without a click makes play() reject with NotAllowedError. A video element works the same way.

Where you'll use this

SORTER

Real tasks start as plain sentences, not API names: "copy the invite link", "show nearby classes", "remind me when the export is done", or "slow down the video". For each task below, ask what it touches. Then ask what permission or click it needs.

Which browser API fits the task?
  • Copy an invite link after the person presses Share
  • Let a learner paste a coupon code into a field
  • Find workshops near the reader’s current city
  • Show “about 30 m accuracy” beside a map dot
  • Remind someone when a long export finishes
  • Ask once before sending calendar reminders
  • Play a short chime made in the page
  • Let a learner slow a tutorial video to 0.75×
Try it yourself
0 of 8 correct

Choose the API for each real task.

Choose a category for every card. You can change an answer at any time; Reset clears them all.
Questions to ask before every request
  • Does the app still work if the person says no?
  • Can this start from a click, a paste, or a play control?
  • Does this browser have the API, on a secure page?
  • Which error or permission state will the page show?

Common misconceptions

PITFALLS
  • "One permission covers everything." Each feature has its own permission, usually per site. Allowing location does not allow notifications.
  • "prompt means I can use it." It means the person has not decided yet. Ask only when they choose the feature.
  • "permissions.query tells me everything." Browsers know different names, and an unknown name can throw. Check the feature too.
  • "Geolocation is exact." The answer comes with an accuracy in meters. It can also be old, missing, or denied.
  • "play() always starts the sound." Its promise can reject because of autoplay rules, missing data, or a file type the browser can't play.
  • "Reading the clipboard is the same as a paste." In a paste, the person chose to hand you the text. readText() takes it without that step, so browsers guard it more.

Practice exercises

5 EXERCISES
Exercise 1 · Warm-upMatch the error code to its message

Guess the exact text this geolocation error function prints.

Starter codePop out in the code editor (opens in a new tab)JavaScript
function messageFor(code) {
  if (code === 1) return "Permission denied";
  if (code === 2) return "Position unavailable";
  if (code === 3) return "Timeout";
  return "Unknown";
}
console.log(messageFor(3));

Answer, then press Check. Spacing and letter case don’t matter.

    Exercise 2 · PracticeRound the coordinates

    Guess what the code prints for the Mumbai coordinates.

    Starter codePop out in the code editor (opens in a new tab)JavaScript
    function rounded(lat, lon) {
      return lat.toFixed(2) + ", " + lon.toFixed(2);
    }
    console.log(rounded(19.076, 72.8777));

    Answer, then press Check. Spacing and letter case don’t matter.

      Exercise 3 · PracticeHandle a denied permission

      Guess the message for the denied state.

      Starter codePop out in the code editor (opens in a new tab)JavaScript
      function shouldAsk(state) {
        return state === "prompt" ? "ask on click" : "do not prompt";
      }
      console.log(shouldAsk("denied"));

      Answer, then press Check. Spacing and letter case don’t matter.

        Exercise 4 · PracticeCopy text from a click

        Write the missing code for a Copy button, in your editor or the console. Keep the copy inside the click handler.

        Starter codePop out in the code editor (opens in a new tab)JavaScript
        button.addEventListener("click", async () => {
          // copy inviteLink here
        });
          Exercise 5 · ChallengeShow a message when sound is blocked

          Fix this function so a blocked play() shows a message on the page.

          Starter codePop out in the code editor (opens in a new tab)JavaScript
          async function startAudio(audio, status) {
            await audio.play();
            status.textContent = "Playing";
          }

            Check your understanding

            7 QUESTIONS
            Device APIs quiz · 7 questionsScore: first tries count
            1. Question 1 of 7Where do most browsers allow these device APIs?

              Choose an answer to see the explanation.

            2. Question 2 of 7What does this code print for Mumbai?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              function formatCoordinates(latitude, longitude, accuracy) {
                return latitude.toFixed(2) + ", " + longitude.toFixed(2) + " (about " + Math.round(accuracy) + " m accuracy)";
              }
              console.log(formatCoordinates(19.076, 72.8777, 28.6));

              Choose an answer to see the explanation.

            3. Question 3 of 7Which geolocation error code means the time ran out?

              Choose an answer to see the explanation.

            4. Question 4 of 7What should happen before calling Notification.requestPermission()?

              Choose an answer to see the explanation.

            5. Question 5 of 7What does play() give back on an audio or video element?

              Choose an answer to see the explanation.

            6. Question 6 of 7What does this code print?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              function permissionBadge(state) {
                return state === "granted" ? "Allowed" : state === "denied" ? "Blocked" : state === "prompt" ? "Ask on use" : "Check another way";
              }
              console.log(permissionBadge("unsupported"));

              Choose an answer to see the explanation.

            7. Question 7 of 7A Paste button reads the clipboard text after a click. Which API does it use?

              Choose an answer to see the explanation.

            Key takeaways

            • Most device APIs work only on secure pages (HTTPS or localhost). Check that the browser has the feature before you use it.
            • Ask for a permission only after the person clicks, never when the page loads.
            • Clipboard reads, geolocation, notifications, and media playback can all fail in normal use. Plan for it.
            • Geolocation gives an accuracy in meters and has three error codes. Never assume the answer is exact.
            • HTMLMediaElement runs both audio and video, with a promise from play(), properties, and events.

            Final definition.
            Device APIs let a page use parts of the person's device, like the clipboard, location, notifications, and sound. The browser guards them, so good code checks for the feature, asks at the right moment, and still works when the answer is no.

            Up next: Web Workers.

            CompleteFrontend Clear concepts. Working examples.