Spin-the-Wheel (Fortune Wheel) Implementation in Mobile Apps
Fortune wheel is a gamification element with recognizable mechanics. User pulls; wheel spins, decelerates with mounting suspense, stops on prize. Simple idea, but details matter: wheel rotation physics, accuracy stopping on the right sector, visual feedback on win determine if this feels like "casino" or "broken counter."
Rotation Physics
Wheel should behave like real physical object with inertia. Not just "spin N seconds then stop"—decelerate exponentially, simulating friction.
Basic model:
// Angular velocity decreases each frame
angularVelocity *= decelerationFactor // 0.96–0.98 for slow deceleration
// CADisplayLink updates angle each frame
currentAngle += angularVelocity * dt
wheelLayer.transform = CATransform3DMakeRotation(currentAngle, 0, 0, 1)
// Stop at minimum velocity
if abs(angularVelocity) < 0.01 { stopAndSnap() }
decelerationFactor is key. 0.99 gives long deceleration (feels heavy), 0.95 gives fast (feels light). Tune to brand feel.
Precise Stop on Target Sector
Fair randomization + accurate stop on specific sector—not contradictory. Algorithm:
- Determine winning sector before spin starts (server logic or client with seed)
- Compute target stop angle:
targetAngle = sectorMidpoint + randomOffset(±halfSectorAngle) - Adjust
decelerationFactorso wheel completes full rotations and stops attargetAngle
Formula for decelerationFactor given initialVelocity and targetAngle:
let totalRotation = currentAngle + (Double(minFullRotations) * 2 * .pi) + targetAngle
// Solve geometric series: sum = v0 / (1 - factor)
decelerationFactor = 1 - initialVelocity / totalRotation
This hides predetermination—wheel looks random but stops where needed.
Snap Animation on Stop
Small "bounce" at end strengthens reality feel. Implement via CASpringAnimation:
let snapAnimation = CASpringAnimation(keyPath: "transform.rotation.z")
snapAnimation.fromValue = currentAngle
snapAnimation.toValue = snappedAngle
snapAnimation.stiffness = 200
snapAnimation.damping = 15
snapAnimation.initialVelocity = angularVelocity
Visual Feedback on Win
-
Haptic feedback:
UINotificationFeedbackGenerator.notificationOccurred(.success)on iOS;VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE)on Android - Lottie confetti: separate overlay above wheel, plays after stop
-
Sector highlight:
CALayerwithshadowOpacityanimation 0 → 1 → 0 three times (blinking)
Implementation by Platform
iOS: CADisplayLink for physics + CALayer or UIView rotation. Sectors drawn via CAShapeLayer with UIBezierPath.
Android: ValueAnimator with custom TimeInterpolator (logarithmic deceleration) + custom View with Canvas.drawArc.
Flutter: AnimationController with custom Simulation (inherit SpringSimulation or custom) + CustomPainter drawing sectors.
React Native: Animated.Value with useNativeDriver: true + react-native-svg for sector graphics, or ready package react-native-wheel-pick.
Timeline: 2–3 days includes physics, stop logic, visual feedback, and integration with server prize logic.







