# Vibration and sound - JavaScript bridge API

> Haptic feedback, the app's own click sound fired on your semantic events rather than every stray tap, and the background music track the creator chose at build time - getMusicVolume returns -1 when the app ships none, so a settings screen can hide the slider instead of showing a dead one.

- **Applies to:** AppMint
- **Source:** extracted from the app runtime and its per-method example files; this page is generated from them.
- **HTML:** https://freewebtoapk.com/docs/api/haptics

### `navigator.vibrate`

```js
navigator.vibrate(pattern)
```

**Example**

Vibrates the phone, using the normal web Vibration API. Android WebView's own `navigator.vibrate` never moves the motor; the app replaces it at document start so it really buzzes.

**Returns:** `true` when the request was accepted, `false` for bad input (a negative or non-number duration). **Needs:** turn on **Vibrate** in Step 4 (Access) when you build. AI-built apps get it switched on for you when the code vibrates.

```js
function buzz(pattern) {
  if ('vibrate' in navigator) navigator.vibrate(pattern);
}

document.getElementById('save').addEventListener('click', function () {
  buzz(30);                    // one short buzz, 30 ms
});

document.getElementById('wrong').addEventListener('click', function () {
  buzz([80, 40, 80]);          // buzz 80, pause 40, buzz 80
});

document.getElementById('alarm-off').addEventListener('click', function () {
  buzz(0);                     // 0 or [] stops a running vibration
});
```

**Notes:**
- The pattern is the web one: the first number is a **buzz**, then pause, buzz, pause ... (the app converts it for Android).
- If Vibrate was not turned on when you built, the call still returns `true` but the phone does not vibrate.
- Decimals are rounded down. In an array, negative numbers count as 0.
- In a desktop browser `navigator.vibrate` may be missing, so keep the `'vibrate' in navigator` check.

### `cancelVibrate`

```js
window.WebToApk.cancelVibrate()
```

Simple haptics for the page (navigator.vibrate and a pattern form). Both are ignored unless the build enabled the Vibrate permission.

_Described by its group, Vibration, rather than on its own._

**Example**

Stops a vibration that is still running. Most pages use the standard `navigator.vibrate(0)`, which calls this for you.

**Returns:** nothing. **Needs:** turn on **Vibrate** in Step 4 (Access) when you build.

The standard way:

```js
if ('vibrate' in navigator) navigator.vibrate(0);   // or navigator.vibrate([])
```

The raw call, for example to stop a long alarm pattern when the user taps "Dismiss":

```js
if (window.WebToApk && window.WebToApk.vibratePattern) {
  window.WebToApk.vibratePattern(JSON.stringify([1000, 500, 1000, 500, 1000]));
}

document.getElementById('dismiss').addEventListener('click', function () {
  if (window.WebToApk && typeof window.WebToApk.cancelVibrate === 'function') {
    window.WebToApk.cancelVibrate();
  }
});
```

**Notes:** Safe to call when nothing is vibrating.

### `getMusicVolume`

```js
window.WebToApk.getMusicVolume(): Int
```

Current level 0..100, or -1 when this app ships no background music.

**Example**

Reads the level of the app's looping background music, 0 to 100.

**Returns:** a number, synchronously: `0`-`100`, or `-1` when this app was built without a music file. **Needs:** a file chosen under **Background Music** in Step 3 (Integrate), under **Display Options**, when you build.

Show a music slider only when there is music:

```js
var slider = document.getElementById('music-slider');
var level = -1;
if (window.WebToApk && typeof window.WebToApk.getMusicVolume === 'function') {
  level = window.WebToApk.getMusicVolume();
}

if (level < 0) {
  slider.parentNode.hidden = true;          // no music in this app: hide the setting
} else {
  slider.value = level;
  slider.addEventListener('input', function () {
    window.WebToApk.setMusicVolume(parseInt(slider.value, 10));
  });
}
```

**Notes:** Returns the level set by the build slider or by your last `setMusicVolume` call. When another app or a call takes the audio, the music ducks or pauses by itself; the number you read does not change.

### `playClick`

```js
window.WebToApk.playClick()
```

Plays the app's tap sound on demand, so a page can fire it on its OWN semantic events (a button press) instead of on every stray tap - which is what a native app actually does, and the reason clickSoundMode can be "off" while this still works. A no-op only when the app was built WITHOUT tap sound (enableClickSound off). In clickSoundMode "off" - no automatic every-tap sound - an explicit call still plays: that mode exists for apps that sound on their own events. Otherwise it plays the creator's configured feedback.

**Example**

Plays the app's tap sound once, so your own buttons can click like a native app.

**Returns:** nothing. **Needs:** the **Tap Sound** switch in Step 3 (Integrate), under **Display Options**, when you build (on by default). With the switch off, this does nothing.

```js
function clickSound() {
  if (window.WebToApk && typeof window.WebToApk.playClick === 'function') {
    window.WebToApk.playClick();
  }
}

document.querySelectorAll('button, .tab').forEach(function (el) {
  el.addEventListener('click', clickSound);
});
```

**Notes:** What you hear depends on the **Tap sound** choice:
- **Follow phone setting** (recommended): the system click. It is silent when the user turned off "Touch sounds" in the phone's settings. That is on purpose.
- **Always play on every tap**: the app's own click, always audible. Every tap on the page clicks too, so you usually do not need `playClick()` in this mode.

Call it from a real button press, not from a timer.

### `setMusicVolume`

```js
window.WebToApk.setMusicVolume(percent: Int)
```

Set the background-music level from the page, 0..100. The wizard slider decides how the app STARTS; this is how an app gives its own users a music control, which any game with a settings screen is expected to have. Clamped in [BackgroundMusic.setVolume], so a page passing 500 or -1 cannot hand MediaPlayer a gain outside the range it accepts. The level is REMEMBERED: the next launch starts at it, not at the wizard's level. A no-op when the app was built without a track - there is nothing to set, and the honest answer is [getMusicVolume] returning -1 so the page can hide its slider rather than show one that does nothing.

**Example**

Changes the level of the app's looping background music, 0 to 100, for example from your game's settings screen.

**Returns:** nothing. **Needs:** a file chosen under **Background Music** in Step 3 (Integrate), under **Display Options**, when you build. Without music this does nothing (check `getMusicVolume()` for `-1`).

A mute button - the app remembers the level for the next launch:

```js
function hasMusic() {
  return !!(window.WebToApk && window.WebToApk.getMusicVolume) && window.WebToApk.getMusicVolume() >= 0;
}

if (!hasMusic()) document.getElementById('mute').hidden = true;

document.getElementById('mute').addEventListener('click', function () {
  if (!hasMusic()) return;
  // clamped to 0..100 by the app, and kept for the next launch
  window.WebToApk.setMusicVolume(window.WebToApk.getMusicVolume() > 0 ? 0 : 40);
});
```

**Notes:** The level you set is saved: the next launch starts at it instead of the level from the build slider (until the user clears the app's data). `0` makes the music silent but it keeps playing.

### `vibrate`

```js
window.WebToApk.vibrate(durationMs: Long)
```

Simple haptics for the page (navigator.vibrate and a pattern form). Both are ignored unless the build enabled the Vibrate permission.

_Described by its group, Vibration, rather than on its own._

**Example**

Vibrates the phone once for the given number of milliseconds. Most pages use the standard `navigator.vibrate(ms)`, which calls this for you.

**Returns:** nothing. **Needs:** turn on **Vibrate** in Step 4 (Access) when you build. Without it the call does nothing.

The standard way (works the same in a browser):

```js
if ('vibrate' in navigator) navigator.vibrate(50);
```

The raw call:

```js
document.getElementById('like').addEventListener('click', function () {
  if (window.WebToApk && typeof window.WebToApk.vibrate === 'function') {
    window.WebToApk.vibrate(40);      // 40 ms
  }
});

// 0 (or any value <= 0) stops a running vibration
if (window.WebToApk && window.WebToApk.vibrate) window.WebToApk.vibrate(0);
```

**Notes:** Pass a whole number. The strength is the phone's default; for stronger/weaker taps use `Capacitor.Plugins.Haptics.impact` or `Capacitor.Plugins.AppwrightHaptics.play`. For a buzz-pause-buzz pattern use `vibratePattern`.

### `vibratePattern`

```js
window.WebToApk.vibratePattern(patternJson: String)
```

Vibrates an on/off pattern, e.g. "[100,50,100]" = buzz 100ms, pause 50, buzz 100. Backs navigator.vibrate(array); prefer the standard API. No-op unless the creator enabled the Vibrate permission.

**Example**

Vibrates an on/off pattern, given as a JSON string. Most pages use the standard `navigator.vibrate([...])`, which calls this for you.

**Returns:** nothing. **Needs:** turn on **Vibrate** in Step 4 (Access) when you build. Without it the call does nothing.

The standard way:

```js
if ('vibrate' in navigator) navigator.vibrate([100, 50, 100]);   // buzz, pause, buzz
```

The raw call takes the same web-style pattern as a **JSON string**:

```js
function pattern(list) {
  if (window.WebToApk && typeof window.WebToApk.vibratePattern === 'function') {
    window.WebToApk.vibratePattern(JSON.stringify(list));
  }
}

pattern([200]);                     // one 200 ms buzz
pattern([100, 50, 100, 50, 300]);   // short, short, long
pattern([]);                        // empty list stops a running vibration
```

**Notes:** The first number is a **buzz**, then pause, buzz, pause ... (web order). The app adds Android's leading "wait" for you, so do not put a `0` first yourself: `[0, 100]` means "buzz 0 ms, pause 100 ms" and you feel nothing. Negative numbers count as 0. The pattern plays once.

