# Browser APIs — reference & live demos

Every typed browser wrapper, and the MDN calls from [Rask.Web](https://rask.sh/docs/guides/web-apis/) that replaced the rest, with a runnable
demo showing its C# source beside the live result.

‹ Back to [Browser APIs](https://rask.sh/docs/guides/browser-apis/)

## API reference — live demos

Every demo below runs live and identically on both transports. Each demo shows its C# source beside
the running result (some are device/permission-dependent and no-op in a headless or desktop browser —
try them on a phone). The WASM-only device APIs (Serial, USB, HID, Bluetooth) and the installation/PWA
APIs live in the [Mobile & PWA guide](https://rask.sh/docs/guides/pwa/).

### Storage & persistence

**localStorage / sessionStorage** — `await LocalStorage.SetItem(key, value)` and `await LocalStorage.GetItem(key)`, from [Rask.Web](https://rask.sh/docs/guides/web-apis/).


**`IIndexedDb`** — a persistent, asynchronous key/value store, far larger than localStorage and non-blocking. Holds
text (`Set`/`Get`) or raw bytes (`SetBytes`/`GetBytes`, stored as a real `Uint8Array`).


**Cookies** — `await Document.Cookie` reads every non-HttpOnly cookie as `"a=1; b=2"`, and
`await Document.SetCookie("theme=dark; path=/")` writes one, from [Rask.Web](https://rask.sh/docs/guides/web-apis/). In a secure context MDN's
async `CookieStore.Get` / `Set` / `Delete` reads and writes one cookie by name.


**Storage estimate** — `await Navigator.Storage.Estimate()` (`Quota` / `Usage` bytes), `Persisted()` and `Persist()`,
from [Rask.Web](https://rask.sh/docs/guides/web-apis/); the origin's storage budget, and asking for it to survive eviction.


### Environment & capabilities

**Navigator facts** — `await Navigator.OnLine`, `await Navigator.Language`, `await Navigator.UserAgent`, from [Rask.Web](https://rask.sh/docs/guides/web-apis/).


**Network quality** — `await Navigator.Connection.EffectiveType` (and `Downlink`, `Rtt`, `SaveData`), from [Rask.Web](https://rask.sh/docs/guides/web-apis/).


**Battery** — `await using var battery = await Navigator.GetBattery();` then `await battery.Level`, from [Rask.Web](https://rask.sh/docs/guides/web-apis/).


**Screen** — `await Screen.Width`, `await Screen.ColorDepth` and `await Window.DevicePixelRatio`, from [Rask.Web](https://rask.sh/docs/guides/web-apis/).


**Visual viewport** — `await Window.VisualViewport.Width` (and `Height`, `OffsetTop`, `Scale`), from [Rask.Web](https://rask.sh/docs/guides/web-apis/).


**Media queries** — `await Window.MatchMedia("(prefers-color-scheme: dark)").Matches`, from [Rask.Web](https://rask.sh/docs/guides/web-apis/).


**Page visibility** — `await Document.VisibilityState`, from [Rask.Web](https://rask.sh/docs/guides/web-apis/).


**`IViewTransitions`** — animate between the old and new DOM instead of the new one just appearing.

The one wrapper here you could not have written yourself. A same-document transition has to *wrap* the
DOM mutation, and the mutation is the framework's morph — there is no point in your code that sits
around it. Enabling routes the live runtime's own commit (diff apply and full-document apply, on both
hosts) through `document.startViewTransition`.

**Off by default**, and off is exactly the previous behaviour: the commit stays synchronous. Style it
with the standard `::view-transition-*` pseudo-elements; give an element a stable
`view-transition-name` and the browser morphs it between routes rather than cross-fading it, which is
what makes a shared header travel. `prefers-reduced-motion` is honoured for you — the animation is the
browser's own default, so there is no stylesheet of yours for the preference to switch off.

`IsActive()` is deliberately separate from what you set: a toggle can be on while nothing animates
because the browser lacks the API or the reader asked for less motion.

```csharp
await _viewTransitions.SetEnabled(true);
```

**Animations** — `await using var fade = await _box.Animate([new() { ["opacity"] = 0 }, new() { ["opacity"] = 1 }], 300)`
on a typed element ref, from [Rask.Web](https://rask.sh/docs/guides/web-apis/#on-an-element-ref); `await fade.Finished`, `await fade.Cancel()`, and
`await _box.GetAnimations()` lists what runs. Reduced motion is yours to honour: read
`await Window.MatchMedia("(prefers-reduced-motion: reduce)").Matches` first.

**Performance** — `await Performance.Now()`, a high-resolution monotonic clock, from [Rask.Web](https://rask.sh/docs/guides/web-apis/).


**Permissions** — `await Navigator.Permissions.Query(new() { Name = "geolocation" })`, from [Rask.Web](https://rask.sh/docs/guides/web-apis/); read
`State` before triggering a prompt.


### Location, sensors & input

**Geolocation** — `await Navigator.Geolocation.GetCurrentPosition(p => _where = p.Coords)`, from [Rask.Web](https://rask.sh/docs/guides/web-apis/).


**Live position** — `await Navigator.Geolocation.WatchPosition(p => …)`, from [Rask.Web](https://rask.sh/docs/guides/web-apis/); each fix runs the handler.


**Device orientation and motion** — `await Window.OnDeviceOrientation(e => _angle = e.Alpha, every: 100.Milliseconds)` and
`Window.OnDeviceMotion(e => …, every: …)`, from [Rask.Web](https://rask.sh/docs/guides/web-apis/#events-and-callbacks). `every:` throttles the
readings in the browser before they cross. On iOS, ask first with `await DeviceOrientationEvent.RequestPermission()`
(WASM, in the click).


**Gamepad** — `await Navigator.GetGamepads()`, then each pad's `Buttons` and `Axes`, and
`Window.OnGamepadConnected(e => …)`, from [Rask.Web](https://rask.sh/docs/guides/web-apis/); prefer WASM for twitch input.


**Vibration** — `await Navigator.Vibrate(200)`, from [Rask.Web](https://rask.sh/docs/guides/web-apis/) (mobile).


### Observers

MDN's own `IntersectionObserver`, `ResizeObserver` and `MutationObserver`, from [Rask.Web](https://rask.sh/docs/guides/web-apis/#events-and-callbacks):
`await IntersectionObserver.Create(entries => …)`, then `await observer.Observe(_ref)`. The handler gets the entries as
data and re-renders its component.

**IntersectionObserver** — notified when an element enters or leaves the viewport.


**ResizeObserver** — notified when an element's size changes.


**MutationObserver** — notified when an element's children or attributes change.


### Media, crypto & files

**Clipboard** — `await Navigator.Clipboard.WriteText("hi")` and `await Navigator.Clipboard.ReadText()`, from [Rask.Web](https://rask.sh/docs/guides/web-apis/).


**Speech synthesis** — `await using var u = await SpeechSynthesisUtterance.Create("Hello")`, then
`await SpeechSynthesis.Speak(u)` and `await SpeechSynthesis.Cancel()`, from [Rask.Web](https://rask.sh/docs/guides/web-apis/#keeping-an-object).


**Speech recognition** — dictation: `_rec = await SpeechRecognition.Create()`, `await _rec.OnResult(e => …)` reading
`e.Results[i][0].Transcript` and `e.Results[i].IsFinal`, then `await _rec.Start()` and `await _rec.Stop()`, from
[Rask.Web](https://rask.sh/docs/guides/web-apis/#where-a-browser-falls-short) (which falls back to `webkitSpeechRecognition` where the browser needs it).


**Media session** — `await Navigator.MediaSession.SetMetadata(await MediaMetadata.Create(new() { Title = "…" }))` and
`SetActionHandler(action, details => …)`, from [Rask.Web](https://rask.sh/docs/guides/web-apis/); now-playing metadata and hardware media keys.


**Crypto** — `await Crypto.RandomUUID()`, `await Crypto.GetRandomValues(new byte[16])` and
`await Crypto.Subtle.Digest("SHA-256", bytes)`, from [Rask.Web](https://rask.sh/docs/guides/web-apis/).


**File System Access** — `await Window.ShowOpenFilePicker(…)` *(WASM)*, then `await (await files[0].GetFile()).Text()`;
`ShowSaveFilePicker` and `CreateWritable()` save it back, from [Rask.Web](https://rask.sh/docs/guides/web-apis/) (Chromium-family).


**Origin private file system** — `await Navigator.Storage.GetDirectory()`, then
`GetFileHandle(name, new() { Create = true })`, from [Rask.Web](https://rask.sh/docs/guides/web-apis/); a private, persistent file tree the
app owns, with no picker. The right home for a local database file.


**`IWebAuthn`** — register and sign in with a passkey instead of a password.


**BroadcastChannel** — `await BroadcastChannel.Create("cart")`, then `PostMessage(msg)` and `OnMessage(e => …)`, from
[Rask.Web](https://rask.sh/docs/guides/web-apis/); messages between same-origin tabs (open this guide in a second tab to try it).


**Web Locks** — `await Navigator.Locks.Request(name, async lk => { … })` holds the named lock until the handler
returns; `new() { IfAvailable = true }` hands the handler `null` at once instead of waiting when it is held, and
`Query()` snapshots the held and waiting locks — from [Rask.Web](https://rask.sh/docs/guides/web-apis/). Open this guide in a second tab and
click "Hold" in both to watch one wait for the other.


**`IWebRtc`** — connect two browsers directly for peer-to-peer data. You supply the signaling (a WebSocket,
an HTTP endpoint, even a `BroadcastChannel` between two tabs); the wrapper handles the offer/answer exchange,
ICE, and data channels. Incoming messages and candidates arrive in **batches** — on the Server host each push
costs a WebSocket frame, so one push per message would end the session under load. The demo runs both peers
in one page, so signaling is a method call and everything else is real.


**`ISignaling`** — the relay two peers trade an offer, an answer and their ICE candidates over, for apps
that don't already have a channel of their own. Host it with `AddRaskSignaling()` + `MapRaskSignaling()`.
Peer ids are minted by the server, a message only reaches a peer in the sender's own room, and nothing is
ever echoed back to its sender. The demo joins the same room twice from one page, so you can watch the whole
exchange.


**Notifications and the app badge** — `await Notification.RequestPermission()` *(WASM, in the click)*, then
`await using var n = await Notification.Create("Title", new() { Body = "…" })`; `await Navigator.SetAppBadge(3)` and
`ClearAppBadge()`. All from [Rask.Web](https://rask.sh/docs/guides/web-apis/). A badge only shows on an installed PWA; on iOS it is numeric-only.


**`Shareable`** *(`Rask.Core` — all hosts)* — headless share: hand *your* element the `data-rask-share`
attribute and its click opens the OS share sheet, on every host including Server (the shared client fires
`navigator.share` in the click gesture, so the activation survives). For a code-driven share in a
WASM app, call `await Navigator.Share(…)` from [Rask.Web](https://rask.sh/docs/guides/web-apis/#what-only-webassembly-runs) instead.


**`Trigger.Gesture` + six typed triggers** *(`Rask.Core` — all hosts)* — headless gesture bridge: hand *your*
element the `data-rask-gesture` attribute and its click runs an activation-gated API in the gesture, so it works
on Server too, where the imperative service can't be injected. Ships `Trigger.Fullscreen`,
`Trigger.ScreenOrientation`, `Trigger.EyeDropper`, `Trigger.Install`, `Trigger.MediaCapture`, and
`Trigger.PictureInPicture`. See [Gesture bridge](https://rask.sh/docs/guides/browser-apis-sharing/#gesture-bridge--activation-gated-apis-on-the-server-host).
