Accessing Native Features from a Web Game
The bridge between your web game and native device features works through plugins, which are packages that provide JavaScript APIs backed by platform-specific native code. Each wrapper framework has its own plugin format and ecosystem, but the patterns are similar: install the plugin, import the JavaScript API, and call methods that the plugin translates into native system calls. The native code runs on a separate thread, so these calls do not block your game's render loop.
Add Haptic Feedback
Haptic feedback is the single most impactful native feature for game feel. Modern smartphones contain precision vibration motors (Apple's Taptic Engine, Android's vibration APIs) that can produce a range of tactile sensations, from subtle taps to heavy thuds. Adding haptics to game events creates a physical connection between the player and the game that no amount of visual or audio polish can replicate.
In Capacitor, install @capacitor/haptics and import the Haptics object. The API provides three primary methods: Haptics.impact() for collision-style feedback with light, medium, and heavy intensity options, Haptics.notification() for success, warning, and error patterns, and Haptics.vibrate() for a general vibration with configurable duration. Calling Haptics.impact({ style: 'heavy' }) when the player takes damage, or Haptics.impact({ style: 'light' }) when collecting a coin, adds substantial perceived quality with minimal code.
In Tauri, the haptics plugin (tauri-plugin-haptics) provides similar functionality on mobile platforms. The API differs slightly but covers the same patterns: impact feedback at various intensities, notification patterns, and custom vibration sequences. On desktop platforms, haptic feedback is not available (desktop computers do not have vibration motors), so your code should check for availability before calling haptic methods, or use a wrapper function that silently no-ops on unsupported platforms.
Design your haptic patterns thoughtfully. Overusing vibration makes the game feel buzzy and annoying rather than responsive. Reserve heavy impacts for significant events like boss hits, level completions, or critical failures. Use light impacts for frequent events like footsteps, pickups, and UI button presses. Let players disable haptics in your settings menu because some players find any vibration distracting.
Implement Local Save Data
Browser games typically store save data in localStorage or IndexedDB, both of which have limitations. localStorage is synchronous and capped at 5 to 10 megabytes depending on the browser. IndexedDB is asynchronous and has higher limits but can be cleared when the user clears browser data. Neither provides true persistent storage that survives cache clearing, and neither supports cloud backup through the platform's native backup system.
Native file system access through the wrapper solves all of these problems. Your save data goes into the app's dedicated data directory, which persists across updates, is included in iOS/Android backups, and has no practical size limit. On iOS, files in the app's Documents directory can sync to iCloud if the user has it enabled. On Android, files are included in Google's auto-backup system.
In Capacitor, the @capacitor/filesystem plugin provides methods for reading, writing, and deleting files. Use Filesystem.writeFile() with the Directory.Data location to write save data, and Filesystem.readFile() to load it. For simple key-value data like settings and preferences, @capacitor/preferences provides a simpler API that wraps platform-native storage (NSUserDefaults on iOS, SharedPreferences on Android).
In Tauri, the tauri-plugin-fs plugin provides comprehensive file system access. Use the app data directory for save files, which Tauri resolves to the platform-appropriate location automatically. The Rust backend can also handle file operations directly through custom commands, which is useful if you need to process or validate save data before writing it.
Implement autosave functionality that writes save data at regular intervals and on key game events (level completion, checkpoint reached, important item acquired). Also save when the app goes to background, because on mobile the OS may terminate the app without warning if memory is needed. Listen for the appStateChange event (Capacitor) or the window-close-requested event (Tauri) to trigger a final save before termination.
Set Up Push Notifications
Push notifications re-engage players who have not opened your game recently. Effective game notifications include: energy/lives refilled alerts, daily reward reminders, friend activity updates for social games, limited-time event announcements, and turn notifications for asynchronous multiplayer games. The key is relevance. Notifications that provide genuine value increase retention, while spammy notifications drive uninstalls.
Both iOS and Android use different underlying services for push delivery. iOS uses Apple Push Notification Service (APNs), and Android uses Firebase Cloud Messaging (FCM). Most games use FCM as the unified backend because it handles both platforms, sending to APNs on iOS and directly to Android devices.
In Capacitor, the @capacitor/push-notifications plugin handles registration, permission requests, and notification receipt. On first launch, request notification permission with PushNotifications.requestPermissions(), then register for remote notifications with PushNotifications.register(). The registration callback provides a device token that you send to your server for later targeting. Handle incoming notifications with the pushNotificationReceived and pushNotificationActionPerformed listeners.
In Tauri, push notification support on mobile is available through community plugins. The implementation follows the same pattern: request permission, register for notifications, capture the device token, and handle incoming notifications. Configure your server-side push infrastructure (Firebase, Amazon SNS, or a custom solution) to store device tokens and send targeted notifications based on game events.
Always provide an in-game toggle for notification preferences. Let players choose which notification types they want (gameplay alerts, social updates, promotional messages) and respect their choices on the server side. Games that send excessive or irrelevant notifications receive negative reviews and higher uninstall rates.
Integrate In-App Purchases
In-app purchases (IAP) through the platform payment systems let players buy virtual currency, cosmetic items, level packs, ad removal, or premium upgrades using their stored payment method. The purchase flow is a single tap with Face ID or fingerprint confirmation, which converts significantly better than web payment forms that require entering card details.
For Capacitor, the capacitor-purchases plugin (powered by RevenueCat) or community IAP plugins handle the cross-platform complexity. You define your products in App Store Connect (iOS) and Google Play Console (Android), configure the plugin with your product identifiers, and present purchase options in your game's UI. The plugin manages the purchase flow, receipt validation, and subscription status tracking across both platforms with a single JavaScript API.
Both Apple and Google take a commission on IAP revenue: 30% for the first year of revenue, dropping to 15% for qualifying small businesses (under $1 million annual revenue on Apple, under $1 million on Google). This commission is mandatory for digital goods and services sold through apps distributed on their stores. Physical goods and cross-platform subscriptions have some exemptions, but virtual game items always fall under the commission structure.
Implement receipt validation on your server to prevent purchase fraud. The wrapper plugin provides the purchase receipt, which you verify against Apple's or Google's validation servers before granting the purchased content. Client-side validation alone is not sufficient because the receipt data can be manipulated on jailbroken or rooted devices.
Control Screen Orientation and Display
Most games need a locked screen orientation. A landscape puzzle game that suddenly rotates to portrait when the player tilts their phone is disorienting. Both Capacitor and Tauri provide plugins and native configuration options for controlling orientation.
In Capacitor, the @capacitor/screen-orientation plugin lets you lock to landscape or portrait programmatically. Call ScreenOrientation.lock({ orientation: 'landscape' }) when entering gameplay and optionally unlock for menus or settings screens. You can also set the default orientation in the native project's configuration files (Info.plist for iOS, AndroidManifest.xml for Android) so the correct orientation applies immediately at launch.
Status bar visibility affects how much screen space your game has. On iOS, hiding the status bar gives your game an additional 44 to 59 pixels of vertical space depending on the device. On Android, hiding both the status bar and the navigation bar (immersive mode) provides the maximum drawable area. Both Capacitor and Tauri provide status bar plugins that let you show, hide, and style the status bar from JavaScript.
Handle the display notch and camera cutouts on modern phones by respecting safe area insets. CSS environment variables env(safe-area-inset-top), env(safe-area-inset-bottom), env(safe-area-inset-left), and env(safe-area-inset-right) tell you how much space the hardware cutouts consume. Position important game UI elements (health bars, score displays, control buttons) inside the safe area so they are not obscured by the notch or rounded corners.
Handle Audio Sessions
Mobile operating systems manage audio through a session system that determines how different apps' audio interacts. By default, starting audio in a WebView may pause the user's background music, which can be unwanted for casual games where players listen to their own music while playing.
On iOS, audio session configuration determines whether your game's audio mixes with other apps, ducks (lowers the volume of) background audio, or takes exclusive audio focus. For games with important sound effects but optional music, configure the session to mix with background audio so players can listen to Spotify while playing. For narrative games or music games where your audio is essential, take exclusive focus to pause background playback.
Handle audio interruptions gracefully. Phone calls, alarms, Siri activation, and other system events temporarily interrupt your game's audio. Listen for interruption events and pause your game when audio focus is lost, then resume when focus returns. On iOS, the AVAudioSession interruption notification handles this. Both Capacitor and Tauri can forward these events to JavaScript through custom plugins or by hooking into the native lifecycle callbacks.
The Web Audio API's AudioContext state also matters. On mobile, the AudioContext starts in a "suspended" state and must be resumed in response to a user gesture (tap). Even after the initial unlock, the context can be interrupted by system events. Implement an audio manager in your game that monitors the AudioContext state, handles unlocking on first interaction, and manages resume after interruptions. This single component eliminates the most common audio bugs in mobile wrapped games.
Start with haptic feedback and local save data because they have the highest impact-to-effort ratio. Add push notifications and in-app purchases only when your game's engagement model or revenue strategy specifically needs them. Every native feature you add increases maintenance complexity across platforms, so integrate only what provides genuine value to the player experience.