Sounds, haptics and rumble
Games can play sounds and trigger vibration on your phone. You can turn either off in Options. Available effects depend on your device and browser. In a browser, sound may require a tap first. These features are free; Pro changes appearance and optional animation.
The library includes 17 original cues. Each pairs a short sound with a vibration pattern: tap, confirm, cancel, jump, boost, hit, explosion, pickup, countdown, ready, warning, win, lose, heartbeat, charge, dial and unlock. No stock recordings or system voices are used. Run the SDK's python3 scripts/build_feedback.py to reproduce the files.
Send feedback to one player
Use the developer-side ControllerHost, or these methods on an authenticated game-host Session. A phone cannot send output commands to another phone.
host.playFeedback(playerId, 'game.hit');
host.rumble(playerId, {low: 0.6, high: 0.2, durationMs: 180});
host.stopFeedback(playerId); // All sound and vibration on that controller.
Rumble lasts at most five seconds per request and replaces the current motor effect. A game that needs a sustained effect must renew it. Low and high are independent strengths from zero to one. Phone hardware commonly has one motor; the clients use the larger strength, with high affecting sharpness where supported. This does not imply a physical two-motor phone.
The scope of stopFeedback is all, sound or vibration. Disconnect, app backgrounding and preference changes stop effects. Requests that finish downloading after their play moment never play retroactively.
Supply your own cue
host.registerFeedbackCatalog([{
id: 'level.warning',
fallback: 'game.warning',
pattern: [
{atMs: 0, durationMs: 80, intensity: 0.5, sharpness: 0.7},
{atMs: 180, durationMs: 80, intensity: 0.5, sharpness: 0.4}
],
sound: {
url: '/sounds/warning.wav',
sha256: 'REPLACE_WITH_THE_64_CHARACTER_LOWERCASE_SHA256',
mime: 'audio/wav',
maxBytes: 48044
}
}]);
host.playFeedback(playerId, 'level.warning', {volume: 0.7});
Serve custom audio from the same origin as the controller endpoint. A developer can serve any original or licensed sound there; files on an unrelated CDN must be mirrored or proxied by that game's server. Clients reject redirects, URL credentials and foreign origins. They send no cookies or authorization headers for audio. A native companion's audio requests retain the certificate pin from its QR.
Use PCM16 WAV, mono or stereo, 8–48 kHz, at most five seconds and two MiB per file. Set maxBytes to the actual file size and calculate SHA-256 from the final bytes. These limits apply to received bytes and decoded format, not just HTTP headers. Serve the file as audio/wav.
A catalog may contain 32 custom IDs and 32 MiB of declared audio; its serialized metadata must fit within 14,000 bytes. Built-in IDs cannot be replaced. Each custom cue must name a valid built-in fallback. Custom haptic patterns accept 16 events within two seconds; each strength and sharpness must be finite and within zero to one.
Cache and fallback
Register a catalog after the player joins. Clients prefetch allowed sounds, verify hashes before decoding, and save them in a bounded cache. If custom audio is not ready, the built-in cue is used immediately. The download is for later requests. Cache identity includes the game origin, room, cue ID and hash. Native clients also include the QR certificate fingerprint. Changed hashes create new entries.
Persistent caches are limited to 32 MiB. The browser also has separate eight-MiB encoded and decoded memory caches for private browsing or storage failures. Players can clear downloaded sounds in Options. Downloads stop on backgrounding or disconnect; constrained-data settings suppress prefetch where the platform exposes them.
Platform behavior
| Client | Sound | Vibration |
|---|---|---|
| iOS app | Original WAV files through ambient audio; respects the silent switch and mixes with other audio | Core Haptics on supported hardware |
| Android app | WAV effects through game audio; uses media volume | VibrationEffect, amplitude when supported |
| Browser | Web Audio after a player gesture | Vibration API where supported; not available in iOS Safari |
All clients limit cues to a burst of 20, refilling at ten per second, and four simultaneous audio voices. Device settings may suppress output. Simulators establish message handling and playback setup, not the physical feel of vibration.
This API is game-to-phone feedback. An installed computer game sending native gamepad rumble is a separate OS-backend integration. Generic macOS HID, vJoy and Linux force-feedback forwarding are not yet verified; do not promise that every installed game's rumble reaches the phone.
Results and wire format
Declare output.feedback.v1; optional capabilities include output.audio and output.haptics. Older clients ignore these output messages. A catalog uses feedback-catalog with entries. Effects use feedback with mode: cue | rumble | stop and the corresponding fields above.
Optionally supply an id of up to 64 letters, digits, dots, underscores or dashes. A client can return a player event named feedback-result with that ID and played, fallback, unavailable or rate_limited. Results do not expose whether the player muted a channel and are not proof that a person heard or felt an effect. Omit IDs when a response is unnecessary.
Audio on the relay
A relay game host can upload a WAV to https://controller.plus/relay/api/rooms/{room}/audio with its temporary host bearer token and an X-Controller-Audio-SHA256 header. The returned sound descriptor can be used in a catalog. Downloads use a separate per-file capability, never the host token. Custom audio stays on the connected relay origin.