Chapter 12: The Shake Interaction
The hero moment. Shake the phone, feel the haptics, see a random memory appear.
This is the feature that makes GratitudeTree special. The user shakes their phone, feels a heavy haptic thud, and a random past journal entry animates into view — like reaching into a physical jar and pulling out a memory. It combines motion detection, haptics, random selection, and animation into one delightful interaction.
What You'll Build
- A shake detection system using the Capacitor Motion plugin
- A shake gesture algorithm that filters out noise
- A full-screen overlay that shows a random entry with animation
- Heavy haptic feedback on shake detection
- A "Shake Again" action and dismiss gesture
- A web fallback button for development and non-native contexts
What You'll Learn
- The DeviceMotion API and accelerometer data
- How to detect a shake gesture from raw motion data
- CSS keyframe animations for the reveal effect
- The
useCallbackanduseRefhooks for event handling - Cooldown patterns to prevent rapid re-triggers
Step 1: Install the Motion Plugin
npm install @capacitor/motion
npx cap sync
The Motion plugin provides access to the device's accelerometer — the sensor that detects movement and orientation.
If you get any terminal errors during this step, paste the full error output directly into Claude Code. It can read error messages and fix them faster than you can search Stack Overflow.
Step 2: Understand Shake Detection
A shake is a rapid back-and-forth motion. The accelerometer reports acceleration on three axes (x, y, z). We detect a shake by looking for acceleration that exceeds a threshold:
Normal movement: x: 0.1, y: 0.2, z: 9.8 (gravity alone)
Shake movement: x: 15.3, y: -12.7, z: 22.1 (much larger values)
The algorithm:
- Read accelerometer values
- Calculate total acceleration:
√(x² + y² + z²) - Subtract gravity (~9.8): anything above a threshold is a shake
- Require multiple threshold-exceeding samples within a time window
- Trigger the shake event with a cooldown to prevent rapid re-triggers
Step 3: Build the Shake Detection Hook
$ claude "Create a custom React hook at src/hooks/useShakeDetection.ts that uses Capacitor..."Key design decisions
Threshold of 30: Normal phone movement (walking, gesturing) produces accelerations of 10-15. A deliberate shake hits 25-40+. We use 30 to avoid false positives while being responsive enough to feel natural.
Cooldown of 1000ms: Without a cooldown, a single shake gesture (back-forth-back) would trigger multiple events. The cooldown ensures one shake = one reveal, and gives the animation time to complete.
useRef for timing: We use useRef instead of useState for lastShakeTime because we don't want re-renders when the timestamp updates. This is a performance pattern — refs are mutable without triggering renders.
useCallback for stability: The motion listener is registered once in useEffect. If handleMotion changed on every render, we'd need to tear down and recreate the listener. useCallback keeps it stable.
Step 4: Build the Shake Reveal Overlay
When a shake is detected, we show a full-screen card with a random entry.
$ claude "Create a ShakeReveal component at src/components/ShakeReveal.tsx that shows a fu..."The bounce-in animation uses a spring-like cubic bezier curve. The card starts small, overshoots slightly, bounces back, and settles. It feels playful and rewarding — exactly the vibe we want for a "jar reveal" moment.
Step 5: Wire Shake into App.tsx
Now connect the shake detection to the reveal overlay.
$ claude "Wire up the shake-to-discover feature in App.tsx. When a shake is detected, pick..."Step 6: Add a Web Fallback Button
Shaking doesn't work in the browser or Simulator. We need a tappable fallback for testing.
$ claude "Add a shake fallback button to the Header for web and Simulator testing. Show a ..."Now there's a sparkles icon in the header. Tapping it triggers the same reveal as shaking — useful for testing and for web users who can't shake their browser.
$ claude "I built a shake detection feature for my Capacitor iOS app. It works on real dev..."Step 7: Test the Shake Feature
On the web
- Start the dev server:
npm run dev - Create a few entries (you need at least one)
- Tap the sparkles icon in the header
- A card should animate in with a random entry
- Tap Shake Again — a different random entry should appear
- Tap the dark overlay or the X button to dismiss
On a physical device
- Build and deploy to a real iPhone (we'll cover this in Chapter 13)
- Physically shake the phone
- Feel the heavy haptic thud
- See the random entry card bounce in
- Shake again while the card is dismissed
The iOS Simulator can simulate a shake via Hardware → Shake Gesture (or Cmd + Ctrl + Z), but the Capacitor Motion plugin may not respond to simulated shakes. For reliable testing, use a physical device or the header button.
Step 8: Commit Your Progress
git add .
git commit -m "Add shake-to-discover: motion detection, random entry reveal, bounce animation"
git push
Checkpoint
Your app should now:
- Shaking the phone triggers a random entry reveal (physical device)
- The sparkles button in the header works as a web/Simulator fallback
- The reveal card has a bounce-in animation that feels playful
- Heavy haptic feedback fires on shake detection
- "Shake Again" picks a different random entry
- Tapping the overlay or X button dismisses the reveal
- The shake has a cooldown to prevent rapid re-triggers
What's Next
The hero feature is built. In the next chapter, we handle the iOS-specific details — safe area insets for the notch, keyboard behavior, app icons, and splash screens. These are the details that make your app feel truly native.
Follow @parvsondhi for build threads, tips, and updates on this tutorial.