← All posts

The Layer Underneath · Post 4

Nobody noticed. That was the point.

Flutter chapter · July 2026 · ~6 min read

A while back I built an app end to end — every screen, front to back, including its payment gateway. That's the line that reads well on a résumé.

The part I'd rather talk about is a colour transition nobody ever mentioned.

The fifteen-minute version

The home page had two tabs. Both equally important — neither one is the "main" tab, you move between them constantly. So the obvious build: tap a tab, swap the content, update the button colour. Blue becomes white, white becomes blue. Fifteen minutes, done.

And it would have shipped. Nobody would have pointed a finger at it. Nobody files a bug that says the tab switch is fine. That's exactly the trap — the fifteen-minute version isn't wrong. It's just dead.

What I built instead

Instead of letting the colours swap, I interpolated them.

LERP — linear interpolation — is one of the least glamorous ideas in graphics. Give it a start, an end, and a position t between 0 and 1, and it hands back the value in between. At t = 0 you're blue. At t = 1 you're white. At t = 0.5 you're at the exact midpoint nobody has a name for. Run t from 0 to 1 over a couple of hundred milliseconds and you don't have two states any more — you have a movement.

That's the whole difference. A snap is a cut: two states with nothing between them, so the eye reads it as one thing being replaced by another. An interpolation is a movement: every frame in between genuinely exists, so the eye tracks it as the same thing changing. Same destination. Completely different feeling.

Instant the fifteen-minute version
Interpolated 220ms, eased
10× slower — same transition, both directions
Click both. The first one works — you couldn't file a bug against it. The second is the one you don't notice. At full speed the interpolation is invisible, which is the entire point; slow it down and you can watch the blue drain into white and the white fill into blue. (This demo uses CSS transitions, so the browser interpolates for you. In Flutter I was reaching for Color.lerp and a curve.)

Four hours for a fifteen-minute task

It took me more than four hours, and I want to be accurate about where they went: I had never built an animation before. Not this one — any. So most of that time wasn't craft, it was reading: what a curve is, what an interpolation is, which widget drives it, why a value changes over time at all. The four hours were tuition.

The dialling-in — the curve, then the exact number of milliseconds — was the smaller part at the end. Though it does matter: too fast and it may as well have snapped, because the movement is there but nothing registers. Too slow and the interface stops feeling responsive and starts feeling like it's making you wait. There's a narrow band where motion stops being something you see and becomes something you feel, and the only way to find it is to keep changing the number.

The relevant part isn't the four hours, it's what happened afterwards: every animation after that one was quick. I'd bought the concepts once. The second one wasn't a four-hour job, or a one-hour job — it was just work. That's the actual shape of the cost, and it's why the first one being slow isn't much of an argument against doing it.

Nobody noticed

And then — nothing. No one said a word. Nobody mentioned the tabs, nobody asked why they felt different, nobody sent a message saying nice transition.

It's clearly there, and nobody knows why. That's the way it should be.

That silence was the correct result. If someone had noticed it — actually stopped and looked at the animation — I'd have overdone it. Motion that calls attention to itself has stopped doing its job and started performing. The goal was never for anyone to see it. The goal was for the app to feel alive while they were busy doing something else.

It's the same thing I wrote about in the first post — the apps I admire most do their hardest work without ever letting you notice. This was the first time I got to be on the other side of that, building the thing instead of admiring it.

The gateway and the transition

I integrated the payment gateway on that same app. Money moves through it, it has to be right, and it's the thing that sounds substantial when you list it out.

But it's binary. It works or it doesn't. It had a spec, a test, and a definition of done, and when it works nobody feels anything — they just paid. There's a correct answer and you either reached it or you didn't.

The transition had none of those. No spec, no ticket, no failure state. Skipping it would have cost nothing anyone could name, which is exactly what makes it the easier thing to drop and the more interesting thing to work on. Those are two genuinely different kinds of work, and I think the second kind is underrated mostly because it never shows up as a line item.

Intentional cuts both ways

None of this is an argument for animating more. Motion that decorates is noise — it costs frames, it costs attention, and it makes an app feel slower the moment it stops earning its place. The discipline isn't adding movement, it's deciding.

The clearest version of that: some people have motion turned down at the system level, because animation makes them ill or makes an interface harder to follow. Both platforms will tell you — and the browser will too. Taking motion seriously means honouring that, and shipping the instant version to the people who asked for it. The demo above already does. Intentional has to include when not to, or it's just taste with extra steps.

The fifteen-minute version would have shipped and nothing bad would have happened. No one was waiting on the transition, no one asked for it, and no one would have known it was missing. That's the honest situation around most of this kind of work — there's no forcing function, so it comes down to whether you feel like doing it.

I did it because I wanted the thing to feel finished. That's the whole reason; I don't have a better one. It cost four hours once and it hasn't cost that since.

Next in The Layer Underneath — still to come: shipping a product end to end, and the call that dropped the instant it connected.
← Back to all posts

The story and the experiences are mine — the writing is Claude's.