npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

capacitor-plugin-system-volume

v0.4.1

Published

Capacitor plugin for system volume + media routing: a native MPVolumeView slider and AirPlay button on iOS, and AudioManager volume + a Cast device picker on Android, so an on-screen slider stays in sync with the hardware buttons.

Readme

capacitor-plugin-system-volume

npm version License: MIT

All Contributors

A Capacitor plugin for system volume and media routing, native on each platform so an on-screen control stays in sync with the hardware volume buttons:

  • iOS — a stylable MPVolumeView slider and an AirPlay route button, overlaid on the webview. They're Apple's own controls, so the slider sets the OS output volume and moves with the hardware buttons — something an <input type="range"> can't do on iOS (WKWebView can't read or set system volume).
  • Android — AudioManager methods to read and set the system media volume (in sync with the hardware buttons) and a Cast device picker, which you wire to your own styled web controls. Android exposes these as plain APIs, so no native overlay is needed.
  • Web — no access to OS volume; every method rejects with unavailable (use a normal slider bound to your media element's own volume).

| Capability | iOS | Android | Web | | --- | --- | --- | --- | | Read volume — getVolume, volumeChange | ✅ | ✅ | — | | Set volume | native VolumeSlider overlay | setVolume | — | | On-screen volume UI | VolumeSlider (MPVolumeView) | your web slider + setVolume | — | | External-route UI | RoutePicker (AirPlay) | openRoutePicker + routeChange (Cast) | — |

Why

On iOS an HTML <audio>/<video> element's volume is a no-op inside WKWebView and there is no web API to touch system volume, so the only control that both reflects the hardware buttons and lets the user set volume is MPVolumeView — a native UIKit view. This plugin mounts it (and the AirPlay AVRoutePickerView) into the webview over a placeholder element and keeps it aligned as the page scrolls and resizes (the compositing technique @capacitor/google-maps uses for a native map).

Android is friendlier: AudioManager reads and sets the media-stream volume directly, and the Cast chooser can be opened programmatically — so there the plugin is just methods, and you render whatever slider and button suit your UI.

Install

npm install capacitor-plugin-system-volume
npx cap sync

Usage — iOS (native overlays)

Put an empty placeholder where each control should appear (the native control shows through it) and give it a width and height.

<div id="volume" style="width: 220px; height: 28px;"></div>
import { VolumeSlider } from 'capacitor-plugin-system-volume';

const slider = await VolumeSlider.create({
  id: 'main',
  element: document.getElementById('volume')!,
  style: {
    minimumTrackColor: '#B4FF39',
    maximumTrackColor: '#FFFFFF33',
    thumbColor: '#FFFFFF',
    thumbRadius: 16,
  },
});

// Reflect volume elsewhere in your UI, if you like.
await slider.setOnVolumeChangeListener((value) => {
  console.log('system volume is now', value); // 0..1
});

// Later, when the view is torn down:
await slider.destroy();

The RoutePicker wrapper mounts an AirPlay button the same way.

Usage — Android (your controls + plugin methods)

Android has no overlay: render your own slider and cast button and drive them with the plugin. getVolume/setVolume/volumeChange move the system media volume; openRoutePicker opens the Cast chooser; routeChange tells you when a device connects so the button can restyle.

import { CapacitorSystemVolume } from 'capacitor-plugin-system-volume';

// Volume: reflect and set the system media volume (0..1).
const { value } = await CapacitorSystemVolume.getVolume();
await CapacitorSystemVolume.setVolume({ value: 0.5 });
await CapacitorSystemVolume.addListener('volumeChange', ({ value }) => {
  // hardware buttons moved it — update your slider
});

// Cast: open the device picker, and track connection to style your button.
await CapacitorSystemVolume.addListener('routeChange', ({ connected, name }) => {
  // connected → highlight the button and show `name`
});
castButton.onclick = () => CapacitorSystemVolume.openRoutePicker();

The Cast picker uses the app's existing CastContext, so choosing a device drives whatever CastPlayer / handoff the app already has.

getVolume() is also exported standalone (iOS + Android):

import { getVolume } from 'capacitor-plugin-system-volume';
const { value } = await getVolume(); // 0..1

Host-app requirements

  • iOS: MPVolumeView reflects and controls system volume, which requires an active AVAudioSession. Apps that play audio already have one; a silent app may need a .playback (or .ambient) session for the slider to track real volume.
  • Android: openRoutePicker needs the Cast SDK configured — a CastOptionsProvider declared via the manifest OPTIONS_PROVIDER_CLASS_NAME meta-data. Without it, openRoutePicker rejects and routeChange never fires; volume still works.

Notes

  • The iOS Simulator does not render MPVolumeView (no real audio route) — test on a device.
  • iOS styling is limited to track colours and a thumb image, per what MPVolumeView exposes — not arbitrary CSS.

API

The VolumeSlider / RoutePicker wrappers are the everyday iOS surface; on Android you call the CapacitorSystemVolume methods (getVolume, setVolume, openRoutePicker, volumeChange, routeChange) directly. Below is the low-level bridge they build on, generated from the source JSDoc by @capacitor/docgen — run npm run docgen to regenerate it.

Low-level bridge interface. Most callers use the {@link VolumeSlider} wrapper, which binds these methods to a DOM element and keeps the native frame synced.

create(...)

create(options: { id: string; rect: VolumeSliderRect; style?: VolumeSliderStyle; devicePixelRatio?: number; }) => Promise<void>

Mount a native volume slider bound to the element at rect.

| Param | Type | | ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | options | { id: string; rect: VolumeSliderRect; style?: VolumeSliderStyle; devicePixelRatio?: number; } |


createRoutePicker(...)

createRoutePicker(options: { id: string; rect: VolumeSliderRect; style?: RoutePickerStyle; }) => Promise<void>

Mount a native AirPlay route button bound to the element at rect.

| Param | Type | | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | | options | { id: string; rect: VolumeSliderRect; style?: RoutePickerStyle; } |


destroy(...)

destroy(options: { id: string; }) => Promise<void>

Tear down the overlay (slider or route button) with this id.

| Param | Type | | ------------- | ---------------------------- | | options | { id: string; } |


setStyle(...)

setStyle(options: { id: string; style: VolumeSliderStyle; }) => Promise<void>

Restyle an existing slider.

| Param | Type | | ------------- | --------------------------------------------------------------------------------------- | | options | { id: string; style: VolumeSliderStyle; } |


setRoutePickerStyle(...)

setRoutePickerStyle(options: { id: string; style: RoutePickerStyle; }) => Promise<void>

Restyle an existing route button.

| Param | Type | | ------------- | ------------------------------------------------------------------------------------- | | options | { id: string; style: RoutePickerStyle; } |


onResize(...)

onResize(options: { id: string; rect: VolumeSliderRect; }) => Promise<void>

| Param | Type | | ------------- | ------------------------------------------------------------------------------------ | | options | { id: string; rect: VolumeSliderRect; } |


onDisplay(...)

onDisplay(options: { id: string; rect: VolumeSliderRect; }) => Promise<void>

| Param | Type | | ------------- | ------------------------------------------------------------------------------------ | | options | { id: string; rect: VolumeSliderRect; } |


onScroll(...)

onScroll(options: { id: string; rect: VolumeSliderRect; }) => Promise<void>

| Param | Type | | ------------- | ------------------------------------------------------------------------------------ | | options | { id: string; rect: VolumeSliderRect; } |


getVolume()

getVolume() => Promise<{ value: number; }>

Read the current system output volume, 0–1. iOS and Android.

Returns: Promise<{ value: number; }>


setVolume(...)

setVolume(options: { value: number; }) => Promise<void>

Set the system output volume, 0–1. Android only — on iOS the OS forbids programmatic volume changes, so use the native {@link VolumeSlider} overlay there instead. Rejects unavailable on web/iOS.

| Param | Type | | ------------- | ------------------------------- | | options | { value: number; } |


openRoutePicker()

openRoutePicker() => Promise<void>

Open the native Cast device chooser (or, when already casting, the controller/disconnect dialog) — the Android counterpart of the AirPlay button. Discovery and the session are shared with the app's CastContext, so picking a device drives the app's own Cast handoff. Android only; rejects unavailable on web/iOS (use the {@link RoutePicker} overlay on iOS).


addListener('volumeChange', ...)

addListener(eventName: 'volumeChange', listenerFunc: (data: { value: number; }) => void) => Promise<PluginListenerHandle>

Fires whenever the system output volume changes — the hardware buttons, Control Center / quick settings, or a drag on the native slider. Value is 0–1. iOS and Android.

| Param | Type | | ------------------ | -------------------------------------------------- | | eventName | 'volumeChange' | | listenerFunc | (data: { value: number; }) => void |

Returns: Promise<PluginListenerHandle>


addListener('routeChange', ...)

addListener(eventName: 'routeChange', listenerFunc: (data: { connected: boolean; name: string; }) => void) => Promise<PluginListenerHandle>

Fires when the selected media route changes — i.e. casting starts or stops. connected is true while a Cast (or other external) route is active, and name is that device's name (empty when playing locally). Lets a web cast button style itself and show the target. Android only.

| Param | Type | | ------------------ | --------------------------------------------------------------------- | | eventName | 'routeChange' | | listenerFunc | (data: { connected: boolean; name: string; }) => void |

Returns: Promise<PluginListenerHandle>


Interfaces

VolumeSliderRect

The rectangle a native overlay should occupy, in CSS pixels.

| Prop | Type | | ------------ | ------------------- | | x | number | | y | number | | width | number | | height | number |

VolumeSliderStyle

Appearance for the native volume slider. Colours are CSS hex strings (#RRGGBB or #RGB). The native side renders the track and thumb from these, so match them to your app's accent to blend the Apple control into your UI.

| Prop | Type | Description | | ----------------------- | ------------------- | ----------------------------------------------------------------------------------- | | minimumTrackColor | string | Filled portion of the track (left of the thumb). Defaults to the system tint. | | maximumTrackColor | string | Unfilled portion of the track (right of the thumb). Defaults to a translucent grey. | | thumbColor | string | The draggable thumb. Defaults to white. | | thumbRadius | number | Thumb diameter in points. Defaults to 16. |

RoutePickerStyle

Appearance for the native AirPlay route button. Colours are CSS hex strings.

| Prop | Type | Description | | --------------------- | ------------------- | ------------------------------------------------------------------------------------ | | tintColor | string | The AirPlay glyph when no external route is active. Defaults to the system tint. | | activeTintColor | string | The glyph when a route (AirPlay/Bluetooth) is active. Defaults to the system accent. |

PluginListenerHandle

| Prop | Type | | ------------ | ----------------------------------------- | | remove | () => Promise<void> |

Maintainers

| Maintainer | GitHub | | ---------- | ----------------------------------------- | | pjaudiomv | pjaudiomv |

Contributors

Thanks goes to these wonderful people (emoji key):

This project follows the all-contributors specification. Contributions of any kind welcome!

License

MIT