ProVibration Plugin for NativePHP Mobile#
A haptic vibration plugin for NativePHP Mobile with support for iOS Core Haptics and Android VibrationEffect. Provides a clean PHP facade and JavaScript module for triggering device vibrations with configurable duration, intensity, and sharpness.
Features#
- Trigger haptic vibrations with fine-grained control over duration, intensity, and sharpness
- Check device haptic support at runtime
- Cancel running vibrations
- Event dispatching when vibrations complete
- Works on both iOS (Core Haptics) and Android (VibrationEffect)
Installation#
composer require jvdluk/pro-vibration
If this is your first NativePHP plugin, publish the plugins provider:
php artisan vendor:publish --tag=nativephp-plugins-provider
Then register the plugin:
php artisan native:plugin:register jvdluk/pro-vibration
This adds \Jvdluk\ProVibration\ProVibrationServiceProvider::class to your plugins() array in NativePluginsServiceProvider.
You can verify the registration with:
php artisan native:plugin:list
Usage#
use Jvdluk\ProVibration\Facades\ProVibration; if (ProVibration::hasHaptics()) { ProVibration::vibrate(duration: 500, intensity: 0.8);}
API Reference#
vibrate(int $duration, float $intensity, ?float $sharpness = null): bool#
Triggers a haptic vibration on the device. The vibration runs asynchronously and a VibrationCompleted event is dispatched when it finishes.
ProVibration::vibrate( duration: 500, // milliseconds (1-5000) intensity: 0.8, // vibration strength (0.0-1.0) sharpness: 0.5, // haptic sharpness (0.0-1.0, iOS only, ignored on Android));
| Parameter | Type | Range | Description |
|---|---|---|---|
$duration |
int |
1 - 5000 |
Duration in milliseconds |
$intensity |
float |
0.0 - 1.0 |
Vibration strength |
$sharpness |
float|null |
0.0 - 1.0 |
Haptic sharpness (iOS only, ignored on Android) |
Returns: true if the vibration was acknowledged by the device.
hasHaptics(): bool#
Checks whether the device supports haptic feedback.
if (ProVibration::hasHaptics()) { // Device supports haptics}
Returns: true if the device supports haptic feedback, false otherwise.
cancelVibration(): bool#
Cancels any currently running vibration.
ProVibration::cancelVibration();
Returns: true if the cancellation was acknowledged.
pattern(array $steps = []): VibrationPatternBuilder#
Creates a vibration pattern builder. Steps can be passed as an array, or added fluently.
// Fluent builderProVibration::pattern() ->vibrate(100, 0.8, 0.5) ->pause(50) ->vibrate(200, 1.0) ->play(); // Array-basedProVibration::pattern([ ['duration' => 100, 'intensity' => 0.8, 'sharpness' => 0.5], ['pause' => 50], ['duration' => 200, 'intensity' => 1.0],])->play();
Returns: A VibrationPatternBuilder instance. Call ->play() to execute the pattern.
VibrationPatternBuilder Methods
| Method | Description |
|---|---|
vibrate(int $duration, float $intensity, ?float $sharpness = null) |
Add a vibration step |
pause(int $duration) |
Add a pause between vibrations (ms) |
fromArray(array $steps) |
Load steps from an array definition |
play() |
Execute the pattern on the device. Returns bool |
toArray() |
Get the pattern steps as an array |
preset(string\|VibrationPreset $preset): VibrationPatternBuilder#
Creates a pattern from a built-in preset. Returns a builder, so you can extend it.
use Jvdluk\ProVibration\Enums\VibrationPreset; // Using stringProVibration::preset('success')->play(); // Using enumProVibration::preset(VibrationPreset::Error)->play(); // Extend a presetProVibration::preset('success') ->pause(100) ->vibrate(50, 0.3) ->play();
Available Presets
| Preset | Pattern | Use Case |
|---|---|---|
success |
Short tap, pause, medium tap | Confirming a completed action |
error |
Three strong buzzes | Signalling a failure |
warning |
Medium buzz, pause, lighter buzz | Non-critical alert |
notification |
Single quick tap | Incoming notification |
double_click |
Two quick taps | Selection or toggle feedback |
Examples#
Quick tap feedback#
// Short, sharp tap for button pressesProVibration::vibrate(duration: 50, intensity: 0.6);
Success confirmation#
// Medium vibration to confirm an actionProVibration::vibrate(duration: 200, intensity: 0.8, sharpness: 0.7);
Error / warning alert#
// Longer, stronger vibration to signal something went wrongProVibration::vibrate(duration: 500, intensity: 1.0, sharpness: 0.3);
Gentle background notification#
// Subtle vibration for a non-urgent notificationProVibration::vibrate(duration: 100, intensity: 0.3, sharpness: 0.1);
Custom heartbeat pattern#
ProVibration::pattern() ->vibrate(60, 0.9, 0.8) ->pause(100) ->vibrate(80, 1.0, 0.6) ->pause(400) ->play();
SOS pattern#
// Three short, three long, three shortProVibration::pattern() ->vibrate(50, 0.8)->pause(50) ->vibrate(50, 0.8)->pause(50) ->vibrate(50, 0.8)->pause(150) ->vibrate(150, 0.8)->pause(50) ->vibrate(150, 0.8)->pause(50) ->vibrate(150, 0.8)->pause(150) ->vibrate(50, 0.8)->pause(50) ->vibrate(50, 0.8)->pause(50) ->vibrate(50, 0.8) ->play();
Data-driven pattern (e.g. from config)#
$steps = config('haptics.checkout_success'); ProVibration::pattern($steps)->play();
Full Livewire component example#
use Jvdluk\ProVibration\Facades\ProVibration;use Jvdluk\ProVibration\Events\VibrationCompleted;use Livewire\Component;use Native\Mobile\Attributes\OnNative; class HapticButton extends Component{ public bool $supportsHaptics = false; public ?string $status = null; public function mount(): void { $this->supportsHaptics = ProVibration::hasHaptics(); } public function tap(): void { ProVibration::vibrate(duration: 100, intensity: 0.7); $this->status = 'Vibrating...'; } #[OnNative(VibrationCompleted::class)] public function onVibrationCompleted(int $duration, float $intensity, ?float $sharpness = null): void { $this->status = "Done ({$duration}ms)"; } public function render(): mixed { return view('livewire.haptic-button'); }}
Events#
A VibrationCompleted event is dispatched when a vibration finishes playing. You can listen for it using the #[OnNative] attribute:
use Native\Mobile\Attributes\OnNative;use Jvdluk\ProVibration\Events\VibrationCompleted; #[OnNative(VibrationCompleted::class)]public function handleVibrationCompleted(int $duration, float $intensity, ?float $sharpness = null): void{ // Vibration finished - update UI, trigger next action, etc.}
The event provides the same parameters that were used to start the vibration.
A PatternCompleted event is dispatched when a pattern finishes playing:
use Native\Mobile\Attributes\OnNative;use Jvdluk\ProVibration\Events\PatternCompleted; #[OnNative(PatternCompleted::class)]public function handlePatternCompleted(int $totalDuration, int $stepCount): void{ // Pattern finished}
| Property | Type | Description |
|---|---|---|
$totalDuration |
int |
Total pattern duration in milliseconds |
$stepCount |
int |
Number of steps in the pattern |
JavaScript Usage#
A JavaScript module is included for frontend frameworks. Import the proVibration object and call methods directly:
import { proVibration } from '@jvdluk/pro-vibration'; const supported = await proVibration.hasHaptics(); if (supported) { // Single vibration await proVibration.vibrate(500, 0.8, 0.5); // Play a pattern await proVibration.playPattern([ { type: 'vibrate', duration: 50, intensity: 0.6, sharpness: 0.5 }, { type: 'pause', duration: 80 }, { type: 'vibrate', duration: 80, intensity: 0.8, sharpness: 0.7 }, ]); // Cancel any vibration or pattern await proVibration.cancelVibration();}
Vue + Inertia#
<script setup>import { ref, onMounted } from 'vue';import { proVibration } from '@jvdluk/pro-vibration'; const supportsHaptics = ref(false);const status = ref(null); onMounted(async () => { supportsHaptics.value = await proVibration.hasHaptics();}); async function tap() { status.value = 'Vibrating...'; await proVibration.vibrate(100, 0.7); status.value = 'Done';} async function strongPulse() { await proVibration.vibrate(500, 1.0, 0.3);}</script> <template> <div> <p v-if="status">{{ status }}</p> <button @click="tap" :disabled="!supportsHaptics">Tap Feedback</button> <button @click="strongPulse" :disabled="!supportsHaptics">Strong Pulse</button> </div></template>
React + Inertia#
import { useState, useEffect } from 'react';import { proVibration } from '@jvdluk/pro-vibration'; export default function HapticButton() { const [supportsHaptics, setSupportsHaptics] = useState(false); const [status, setStatus] = useState(null); useEffect(() => { proVibration.hasHaptics().then(setSupportsHaptics); }, []); async function tap() { setStatus('Vibrating...'); await proVibration.vibrate(100, 0.7); setStatus('Done'); } async function strongPulse() { await proVibration.vibrate(500, 1.0, 0.3); } return ( <div> {status && <p>{status}</p>} <button onClick={tap} disabled={!supportsHaptics}>Tap Feedback</button> <button onClick={strongPulse} disabled={!supportsHaptics}>Strong Pulse</button> </div> );}
Svelte + Inertia#
<script> import { onMount } from 'svelte'; import { proVibration } from '@jvdluk/pro-vibration'; let supportsHaptics = false; let status = null; onMount(async () => { supportsHaptics = await proVibration.hasHaptics(); }); async function tap() { status = 'Vibrating...'; await proVibration.vibrate(100, 0.7); status = 'Done'; } async function strongPulse() { await proVibration.vibrate(500, 1.0, 0.3); }</script><div> {#if status} <p>{status}</p> {/if} <button on:click={tap} disabled={!supportsHaptics}>Tap Feedback</button> <button on:click={strongPulse} disabled={!supportsHaptics}>Strong Pulse</button></div>
Platform Notes#
| Feature | iOS | Android |
|---|---|---|
| Duration | Supported | Supported |
| Intensity | Supported | Supported (mapped to amplitude) |
| Sharpness | Supported (Core Haptics) | Ignored |
| Cancel | Supported | Supported |
| VibrationCompleted event | Supported | Supported |
Support#
For questions or issues, email [email protected]
License#
Proprietary