# Loyalty Punch-Card Stamp Animation in SwiftUI

> By Lawrence Arya, Founder & CEO of VP0. Published 2026-06-07. 6 min read.
> Source: https://vp0.com/blogs/loyalty-punch-card-stamp-animation-swiftui

Half a second of satisfaction is the whole format. Render the stamp as a fade and you built a spreadsheet with a logo.

**TL;DR.** A digital loyalty punch card lives on the stamp-landing moment, so the build is a glanceable slot grid plus a stamp animation that feels physical: a spring with overshoot on scale and rotation (low damping around 0.5), a slight permanent off-axis tilt per slot, and a medium haptic on landing, never a linear fade, with the completing stamp escalating into a reward celebration. The count is server-truth, so the animation plays on a confirmed stamp event, never an optimistic tap, and stamps are merchant-granted (staff scan, merchant QR, or verified order) rather than a farmable free button. Add Apple Wallet where it fits and respect Reduce Motion and VoiceOver. A free VP0 design supplies the punch-card and reward screens.

## What makes a punch card worth opening twice?

The stamp. A digital loyalty card lives or dies on the moment a stamp lands, because that half-second of satisfaction is the entire emotional payload of the format, the reason the paper version worked for a century. Get the stamp animation right and the card is a tiny dopamine machine people open to look at; render it as a number incrementing and you have built a spreadsheet with a coffee logo.

So the build is mostly two things done well: a **grid of slots** that reads its progress at a glance (7 of 10, the empty slots clearly the goal), and a **stamp landing animation** that feels physical, a thunk, not a fade. Everything else, the reward unlock, the card list, is straightforward; the animation is where the craft and the keyword actually live.

## How is the stamp landing built in SwiftUI?

As a [SwiftUI spring](https://developer.apple.com/documentation/swiftui/animation/spring(response:dampingfraction:blendduration:)) with overshoot, on scale and rotation, never a linear fade. The recognizable feel is a stamp pressed down: it arrives at about 2.2x size and rotated a touch off-axis, then settles to 1x, exactly like a real rubber stamp hitting paper. The shape of it:

```swift
Image(systemName: "star.fill")
    .scaleEffect(stamped ? 1.0 : 2.2)
    .rotationEffect(.degrees(stamped ? -8 : 12))
    .opacity(stamped ? 1 : 0)
    .animation(.spring(response: 0.35, dampingFraction: 0.5), value: stamped)
```

Three details turn that into the real thing. A **low damping fraction** (around 0.5) gives the overshoot that reads as impact, where a high damping value feels like the stamp gently floating down, wrong for the metaphor. A **slight permanent rotation** at rest (each stamp settling a few degrees off-axis, ideally varying per slot) mimics hand-stamping and avoids the sterile perfect-grid look. And a **haptic on landing** ([`UIImpactFeedbackGenerator`](https://developer.apple.com/documentation/uikit/uiimpactfeedbackgenerator), medium) is half the feeling, because the thunk is as much felt as seen, the same one-spring-plus-haptic craft as [the iMessage bubble physics](/blogs/imessage-reply-bubble-physics-swiftui/).

The completing stamp, the one that fills the last slot, earns extra: a bigger celebration, the card flipping or glowing, the reward revealing. That escalation is the whole point of the progression, the same reward-moment discipline that makes [the kids letter-tracing canvas](/blogs/kids-tracing-letters-canvas-ui-react-native/) work, where finishing has to feel like an event.

## What does honest loyalty state require?

Server-truth on the count, because a punch card is a promise about value. The stamp count is owned by the backend, not the device, so the animation plays on confirmation of a real stamp event, never optimistically on tap, since a stamp that animates in and then vanishes when the server disagrees is worse than a half-second delay. And the earning mechanism has to be tamper-resistant: a stamp granted by the merchant (a staff scan of the customer's code, or the customer scanning a merchant QR) rather than a button the customer taps freely, or the card is worthless the moment one customer notices the self-serve button.

| Earning method | How it works | Why it is honest |
| --- | --- | --- |
| Merchant scans customer | Staff scans the customer's loyalty QR at checkout | Stamp tied to a real transaction |
| Customer scans merchant | Customer scans a per-location merchant QR | Harder to farm than a free tap |
| Auto from purchase | Backend stamps on a verified order | No scan, fully server-side |

The reward redemption mirrors it: redeeming is a confirmed event (staff-validated or one-time code), the reward visibly "spent," and the card resetting to a fresh cycle, never a reward the customer can claim repeatedly. This is the same disclosed-value discipline as any [loyalty points tracker](/blogs/loyalty-points-tracker-ui-clone/); the punch card is just the most tactile expression of it.

## What completes the card?

Wallet-grade polish and the small honesties. The card should feel like an object: a real visual identity per merchant, the progress legible from the lock screen if you add a widget, and Apple Wallet integration where it fits (a [PassKit](https://developer.apple.com/documentation/passkit) loyalty pass is the natural home, with the in-app card the richer version). Respect the platform: Reduce Motion swaps the stamp spring for a calm fade (the stamp still lands, just without the bounce), VoiceOver announces "7 of 10 stamps," and the reward state is unmistakable to someone who never sees the animation.

The screens, the punch-card grid, the card wallet, the reward-unlocked moment, the earn-stamp scanner, come as a free [VP0](https://vp0.com) design, so an agent builds the spring-and-haptic stamp engine onto a real loyalty UI with the server-truth count already in the model.

## Key takeaways: a loyalty punch-card stamp

- **The stamp animation is the product**: a spring with overshoot on scale and rotation, not a fade; the thunk is the dopamine.
- **Low damping plus a slight resting rotation plus a haptic** is what reads as a real stamp pressed onto paper.
- **The completing stamp escalates**: the last slot triggers a real celebration and the reward reveal.
- **Count is server-truth, earning is merchant-granted**: animate on a confirmed stamp event, never an optimistic free tap.
- **Wallet-grade polish and platform respect**: per-merchant identity, Apple Wallet where it fits, Reduce Motion and VoiceOver covered.

## Frequently asked questions

**How do I build a loyalty punch-card stamp animation in SwiftUI?** Animate the stamp as a spring with overshoot on scale and rotation (low damping around 0.5), add a slight permanent off-axis rotation and a medium haptic on landing so it reads as a real stamp, and escalate the final stamp into a reward celebration. A free VP0 design supplies the punch-card grid and reward screens to build the engine onto.

**Why does my stamp animation feel cheap?** Almost always because it fades or uses high damping, which floats the stamp gently down instead of pressing it. Use a low damping fraction for overshoot, animate scale and rotation together, add a slight resting tilt, and fire a haptic on landing; the impact is what carries the satisfaction.

**Should the stamp animate when the user taps?** No: the stamp count is server-truth, so animate only on a confirmed stamp event from the backend, never optimistically on tap. A stamp that lands and then disappears when the server disagrees is worse than a brief delay, and a free customer-tappable stamp makes the card worthless.

**How do customers earn stamps without cheating the system?** Tie each stamp to a real transaction: staff scanning the customer's loyalty QR, the customer scanning a per-location merchant QR, or an automatic backend stamp on a verified order. A button the customer taps freely is farmable and destroys the card's value the moment anyone notices.

**Should a loyalty punch card use Apple Wallet?** Where it fits: a Wallet loyalty pass is a natural home and surfaces progress on the lock screen, with the in-app card as the richer, animated version. Treat Wallet as the glanceable shadow and the app as the full experience, and keep both reading from the same server-owned count.

## Frequently asked questions

### How do I build a loyalty punch-card stamp animation in SwiftUI?

Animate the stamp as a spring with overshoot on scale and rotation (low damping around 0.5), add a slight permanent off-axis rotation and a medium haptic on landing so it reads as a real stamp, and escalate the final stamp into a reward celebration. A free VP0 design supplies the punch-card grid and reward screens to build the engine onto.

### Why does my stamp animation feel cheap?

Almost always because it fades or uses high damping, which floats the stamp gently down instead of pressing it. Use a low damping fraction for overshoot, animate scale and rotation together, add a slight resting tilt, and fire a haptic on landing; the impact is what carries the satisfaction.

### Should the stamp animate when the user taps?

No: the stamp count is server-truth, so animate only on a confirmed stamp event from the backend, never optimistically on tap. A stamp that lands then disappears when the server disagrees is worse than a brief delay, and a free customer-tappable stamp makes the card worthless.

### How do customers earn stamps without cheating the system?

Tie each stamp to a real transaction: staff scanning the customer's loyalty QR, the customer scanning a per-location merchant QR, or an automatic backend stamp on a verified order. A button the customer taps freely is farmable and destroys the card's value the moment anyone notices.

### Should a loyalty punch card use Apple Wallet?

Where it fits: a Wallet loyalty pass is a natural home and surfaces progress on the lock screen, with the in-app card as the richer animated version. Treat Wallet as the glanceable shadow and the app as the full experience, both reading from the same server-owned count.

---
*Published on the [VP0 Journal](https://vp0.com/blogs). Free to read, index and cite with attribution.*
