---
title: Browser features in your app
description: "Camera, microphone, and location ask the browser for permission, and a chat host can block that prompt before it shows. A file picker asks for nothing, so it always works."
sidebar:
  label: Browser features in your app
  order: 3
---

## Which features work where

| Feature     | Standalone app URL            | Rendered inline in a chat                           |
| ----------- | ----------------------------- | --------------------------------------------------- |
| Camera      | Works, once you claim the app | Blocked: the chat host denies the permission prompt |
| Location    | Works, once you claim the app | Blocked: the chat host denies the permission prompt |
| Microphone  | Works, once you claim the app | Blocked: the chat host denies the permission prompt |
| File picker | Works                         | Works                                               |

**Camera, location, and microphone.** Claim the app, then use these features at its standalone URL. Chat hosts block the permission prompt. Charming shows a link to the standalone app when needed.

**File picker.** It needs no manifest import or claim, so it works in both hosts. See [Data storage](../capabilities/data-storage).

Camera and microphone streams are device API results, not external audio or video files. The capability import controls browser permission at the standalone URL. It does not add a CSP `media-src` origin or make uploaded media playable inline.

## Build so it works in every host

Declare the matching capability import, call the normal Web API, and handle denial. Do not build a second permission message. For a file picker, use a normal file input and the storage API.

## Technical

The table above is the host contract. The [full build reference](https://charm.ing/docs/llms-full.txt) lists the exact capability imports.

### Copy this prompt for your agent

```text
Add a browser feature to my Charming app: camera, location, or
microphone. Read
https://charm.ing/docs/guides/browser-features.md first. Work out
which one I need from what I'm asking for, and build it so it falls
back cleanly when the permission is blocked. Ask me if it isn't
obvious which feature I want.
If you haven't built a Charming app before, read
https://charm.ing/docs/build-mcp.md (or build-http.md for the HTTP
API) for the app skeleton first.
```

### How an agent performs this job

Declare `charming:browser/camera@1.0`, `charming:browser/geolocation@1.0`, or `charming:browser/microphone@1.0`, then call the matching Web API. A file picker needs no import.

### The contract

Camera, location, and microphone require a claimed app and its standalone URL. Charming's inline viewer uses an opaque frame without the per-app capability origin, so these delegated permissions stay blocked in chat. Use an Open in web fallback.

## Related

- [Data storage](../capabilities/data-storage)
- [Limits](../capabilities/limits)
- [How Charming works](../concepts/how-charming-works)
- [External files and CSP](../technical-reference/external-files-and-csp)
- [Docs home](..)

Found a bug or need a feature? [Tell us](/docs/capabilities/feedback) with `submit_feedback` or `POST /app/{id}/feedback`. Your feedback shapes what we build next.
