Find animation opportunities
Most apps do not need more animation. They need it in a few places that carry meaning, and nowhere
else. Your job is to find those places in the user's code, say plainly what should stay still or
be toned down, and back each recommendation with a real interaction from the 60fps library.
Motion here means visual motion AND haptics: both are part of how an app feels.
Do not change any code in this pass. The deliverable is a short report.
1. Take stock of the motion that is already there
Before looking for gaps, search the code for what the app already does: withAnimation,
.animation(, .transition, matchedGeometryEffect, matchedTransitionSource,
contentTransition, symbolEffect, phaseAnimator, keyframeAnimator, sensoryFeedback,
UIImpactFeedbackGenerator, UIView.animate, and any reduce motion checks. On a big app this is a long list: group it by file and read the
haptics and transitions first, they are where most problems hide.
Then say which kind of app this is:
- Little motion. Most of the value is new opportunities.
- Already rich. Say so at the top. The value is now the few gaps, motion that is broken, and motion that is overdone. Do not recommend what the app already does.
Note the app's tone from its existing motion and design: calm and restrained, or bold and playful. Every recommendation must match it.
2. Decide the scope
If the user named a screen or a feature, look only there. Otherwise cover the screens a user sees
most: the root view and whatever it presents (tabs, overlays, sheets, the flows they open), plus
widgets, Live Activities and other extensions, which are easy to forget. Widgets cannot run their
own animations: the only motion there is a content transition when the timeline changes (rolling
digits, a crossfade), so never suggest more. Read the state that drives
a view too (@Observable models, view models, the code that assigns new data), not only the view:
the fix is often where the state changes.
Skip views nothing constructs and code paths that cannot run today (for example behind a flag that is always on). If one matters later, mention it once and label it as not reachable yet.
Report at most 10 findings, counting the table and the fixes together (the leave still list does not count). A short table is fine when the real value is in the fixes. When there are more candidates, rank by how much the moment means against how often it happens: rare and meaningful first, frequent and minor last.
3. Find the moments
Moments worth motion:
- Waiting. A network call or slow work with only a spinner, or nothing at all.
- Success and failure. A save, send, purchase or completion that ends silently, or an error that just resets with no feedback.
- Things appearing and leaving. Rows inserted, deleted or reordered, cards dismissed, items added.
- Where did it go. A thumbnail that opens to detail, anything where continuity helps the user keep their place.
- Choice and state. Toggles, selection, tabs, segmented controls that snap with no feedback.
- Numbers that change. Balances, counts, timers, progress.
- Empty and first-run states. The first thing a new user sees.
- Direct manipulation. Drags, swipes and long presses that should follow the finger.
Also check the motion that exists:
- Broken. A
.transitionwhose state change is not insidewithAnimation, so it never plays. An animation attached to the wrong value. A reduce motion check read once instead of from@Environment(\.accessibilityReduceMotion), so it misses changes while the app is open. - Overdone. A success haptic or a celebration on something routine (every refresh, every poll).
Search for every
.successhaptic: if the app saves it for one big moment, any other use is a finding. A long animation on a path the user takes many times a day. - Fallback that removes meaning. A reduce motion branch that makes a meaningful change snap with nothing at all, where a crossfade would keep the meaning. Existing motion with no fallback at all is a fix too.
For each finding note the file and line where the change should happen (usually where the state is set), and the view it shows up in if that is a different file.
4. Decide what to leave still
Say this out loud in the report. It is half the value.
Leave it still when:
- It repeats. Actions done many times a day (typing, scrolling a feed, a tab bar tapped fifty times, data that refreshes every few seconds) wear out any animation. Keep these instant or plain.
- People are reading. Long text, settings, data tables, forms. Motion pulls the eye away.
- It is in the way. Never make the user wait for an animation to finish before they can act. Anything longer than about 300 ms on a frequent path is too long.
- The system already does it. Navigation pushes, sheets, alerts, keyboard, system sign in. Restyling them usually makes the app feel less native, not more.
- It is decoration. A loop or flourish that says nothing about state.
- The screen already has a moment. One memorable motion per screen. Two compete.
Every recommendation needs a Reduce Motion fallback: a crossfade, or no motion.
5. Find a real reference for each opportunity
For every opportunity in the table, call 60fps_search_shots. Describe the MOTION and the moment,
plus the app's tone, in plain words: "a calm ring that fills once to mark a milestone" works,
"achievement" does not. Do not lead with the surface name (widget, settings, cell): it pulls in
shots about that surface instead of the motion.
The search always returns something and its scores barely separate good matches from poor ones, so
judge every result yourself by its title and summary. Prefer results whose intensity and mood
match the app's tone. If the first search misses, try once more with different words (add mood
words like calm, subtle, playful). If nothing fits after that, write "No close match" rather than
stretching, and describe the motion in words instead. Fixes need no search, and neither does a
haptic-only change. A partial fit is fine when you say which part to take: "the row slide only, not the
confetti".
Only fetch more when the user picks one. Then 60fps_get_motion_breakdown gives the timing and
60fps_get_motion_code the SwiftUI to adapt (see the motion-reference skill).
6. Report
Lead with the answer in two or three sentences, including whether the app already moves well. If a fix is severe (content invisible, a broken flow), say it in the lead, not only in the fixes. If a non-motion fact changes a recommendation (the screen a tap should open does not exist yet), add one line under the table. Then one table, most valuable first:
| Where | Moment | What to do | Reduce Motion | Shots |
|---|---|---|---|---|
CheckoutView.swift:84 |
Pay ends with no feedback | Morph the button into a check, then dismiss | Crossfade to the check | Shot title, take the check only |
Then, only if there are any, Fix or tone down: broken or overdone motion, each with file, line and a one-line fix. Then Leave these still, each with a one-line reason.
End by asking which one to build, and point out any that are a one-line fix. Write plainly: short sentences, common words, no hype. Never use em dashes or en dashes.
Not for
Android, Flutter, React Native or web apps: say the library is iOS only and give general advice without shots. Performance or frame rate problems are a different question.
