A sunflower in my pocket

contents
  1. Why do these coins look embossed?
  2. And the color that shifts with the light?
  3. How to lay out the coins
  4. One coin at a time
  5. Where 137.5° comes from
  6. Giving the pattern room to grow
  7. The curves are in the flower too
  8. Checking against the card itself
  9. Back to the order screen
  10. Try it on the card
  11. Keep exploring
Nature · sunflower
Center of a sunflower; tiny green florets form curves that cross among the yellow ones
Interface · Apple Cash
Sunflower: photo by Esdras Calderan, Sunflower (Helianthus annuus), CC BY 2.0, unaltered.Card: Apple's original video, muted, cropped only on display. See it in full.

What makes an interface feel well made? It’s almost never one thing. It’s dozens of details nobody notices on their own, but that add up to the feeling that somebody cared.

That’s what I admire about Apple’s design, and it’s why I lost hours staring at the Apple Cash (opens in a new tab) card, the digital card in the iPhone’s Wallet that holds the money you send and receive in Messages. Deep down, it’s a balance on a screen. Nobody needed it to be pretty. Still, it reacts when you tilt your phone, and it rides around in a lot of pockets next to the house keys, never asking for attention. Not in mine, though: Apple Cash only exists in the United States, and I got hooked on it through Apple’s videos.

Tilt the phone and color slides across the coins. Every coin has a little arrow cut into it, and the empty spaces between them draw a flower. I was sure it was a lotus. Wrong flower: it was a sunflower, like the one up there next to the card.

Trying to rebuild that card, I kept collecting questions. They’re what this post answers:

  • Does this pattern on the card have a name? And why does it exist?
  • How do you reproduce the iridescent shine of a CD on a computer, with WebGL?
  • How do you scatter the coins exactly like the reference image? Do they follow a function?
  • What does a sunflower have to do with a payment card? And why does nature use this pattern?
  • And did Apple really use that rule, or did I just want to see a flower? Am I losing it?

A heads-up first: I don’t know if anyone at Apple was thinking about a sunflower, or what tools they used. What follows is my own reconstruction, built on other people’s work, not reverse engineering.

Get a little closer and details show up that I had seen without really noticing:

Close-up of the coins and arrows on the Apple Cash card

Look at the arrows: they don’t all point up. On the left they lean left, in the middle they go straight up, on the right they fan out to the right. Each one points away from the center, following the fan of the design. And as they climb, the coins shrink into tiny dots until the pattern fades into black, with no edge marking where it ends.

None of this is needed to use the card. But it’s this kind of thing, added up, that makes it feel well made.

Looking is one thing; wanting to build it is another. In early 2026 I was building the referral program for rafobase and saw the perfect excuse to try reproducing that card. Months later, designing the order detail screen for Nutools, Nuture’s operating system, I thought the screen was kind of dull and decided to add a small detail. Small and completely overengineered: it took the whole afternoon.

Along the way I went through the physics of light, a video game technique, a bit of botany and a lot of code. I didn’t start from scratch: Heshan Wijesinghe had already recreated the card and open-sourced everything. I stood on his shoulders, brought the effect to the web, swapped the fixed positions for coins generated in code, and then went the other way: I measured a screenshot of the card to see if the sunflower rule showed up there too.

Why do these coins look embossed?

The rafobase version was built almost entirely on top of Heshan’s work. He wrote up how he recreated Apple Cash in Flutter (opens in a new tab), and I borrowed his textures and the core of the effect for my web adaptation.

In his write-up, he says he started with gradients and got stuck trying to control the curves, especially around the arrow. That’s what led him to a technique from games: normal mapping.

The outline of a coin shows the problem. The rim looks rounded, and around the arrow the surface changes slope. When the light moves, each patch has to respond differently. A highlight that just swept across the whole circle would wipe those details out.

In the demo, watch the light pass over the arrow’s outline. Then take control, park the light somewhere, and pick No relief to compare without moving the light.

ONE COIN, THE SAME LIGHT

What shows up on screen

The coin's outline doesn't change.

The relief it stands for · section A–A

AAgreen arrows = normals

The light moves on its own. Drag it to take over; on a keyboard, use Tab and ← →.

The normals change direction at the edges. The lighting follows those curves.

The green arrows in the cross-section are the normals: directions perpendicular to each little piece of the surface. On the rounded rim they tilt; on the flat part they all point the same way. Make them all parallel and the cutout is still a coin, but the light no longer reveals the curves.

A normal map (opens in a new tab) stores those directions in an image. The red, green and blue channels hold the three components of each arrow. That’s why the colors below look so strange: they’re data the program reads to compute the lighting.

Normal map by Heshan Wijesinghe, reused in rafobase

Normal map by Heshan Wijesinghe, from the holographic-spiral (opens in a new tab) project. Shown here at reduced size.

Heshan built this in Blender, combining the geometry’s normals with a height map to form the arrow’s bevel. He also made the mask that cuts out the shape. It’s the kind of work that disappears in the final result, and I reused all of it.

These are two different pieces of information: the mask decides where the coin shows up; the normal map says how the surface responds to light. The shader, the program that computes the color of each pixel on the graphics card, uses those directions in its math. The light moves, the map image stays exactly the same, and the result changes.

You can think of it as an equation: mask times map, plus light, equals coin. Below, the sun spins on its own and only the last image changes. Tap any point on any image to read what the map stores there, or drag the sun to put the light wherever you want.

Two still images, one moving light

Maskwhere there's a coin

Normal mapwhere each point faces

Lightdrag the sun

Cointhe only one that changes

the map stores rgb(22, 128, 199)the surface tilts 56° to the leftso it gets 82% of the light
The sun goes around on its own; dragging it takes over. These are teaching maps of my own, different from Heshan's textures shown in the article.

In the demo, the mask and the surface slopes are stored once; moving the light only recomputes the color. Here’s the core in JavaScript, taken from the demo’s model:

const normal = normalFromSlope(samples[i], samples[i + 1], relief);
const { diffuse, specular } = lighting(normal, light);

for (let c = 0; c < 3; c++) {
  pixels[p + c] = Math.round(255 * Math.sqrt(
    clamp((color[c] / 255) ** 2 * diffuse + specular, 0, 1)
  ));
}

samples[i] and samples[i + 1] hold the slopes; samples[i + 2] controls the mask’s transparency. The lighting function uses the dot product to measure how much the normal points toward the light: the more aligned they are, the brighter it gets. It returns the diffuse and specular parts.

A normal map doesn’t add any volume to the geometry. It lights a flat surface as if it had those small slopes. One image holding relief that stays convincing while the light wanders around, with no new texture needed for each position. A good old video game trick.

And the color that shifts with the light?

The relief explains part of the look. What’s missing is the color that drifts across the surface, like what you see when you tilt a CD.

That change of color with angle has a name: iridescence. On a CD, the microscopic tracks, packed tight and evenly spaced, act as a diffraction grating.

To see what that does, think of light as a wave. White light mixes many wavelengths, each tied to a color. When it hits a repeating structure, those waves overlap: in some directions they reinforce each other, in others they cancel out. That’s interference. Since the result depends on wavelength, each color peaks in a different direction. Tilting the disc changes which colors reach your eye. Alan Zucconi does a great job explaining the link between surface structure, diffraction and color (opens in a new tab).

Watch one wave slide against the other, then pause and try other spots. When a peak meets a trough of the same height, the sum at that point is zero. When the peaks line up, they add up.

When two waves meet

Wave AWave B

Sum of both · amplitude 2.00× that of one wave

Two waves with the same frequency and amplitude, in an ideal model. The graphs show amplitude, not brightness. This explains interference, but on its own it doesn't simulate a diffraction grating or white light.

The graph adds two waves of the same frequency. Shifting the second one only changes its phase; the first stays put:

export function waveSum(x, phase) {
  return Math.sin(x) + Math.sin(x + phase * Math.PI / 180);
}

Zucconi’s work is where Heshan found the reference for this part of the effect. He tested the shader in KodeLife, seeing every tweak in real time, before taking it to Flutter.

In the code I borrowed, the spectral_zucconi6 (opens in a new tab) function produces an approximation of the spectrum’s colors. The position of the focus and the orientation stored in the normal map both go into picking each point’s color. That way the rainbow band follows the coin’s details instead of sliding over them like a flat coat of paint.

It’s a visual approximation, not a physics simulation of a diffraction grating, and for a card that’s plenty. In my adaptation I kept that base, tweaked the lighting, and made the effect’s focus follow the mouse or the phone’s tilt, so the surface responds to whoever is holding it.

In the WebGL port shown below, this is the actual math in the fragment shader. The language here is GLSL, since this is the program that runs on the graphics card. The relationship between normal, focus and color was adapted from Heshan’s recreation; spectral_zucconi6 is Zucconi’s. colorDistance is an effect parameter, not a real wavelength:

vec3 normal = normalize(texture2D(normalMap, st).rgb * 2. - 1.);
normal.x = -normal.x;
vec2 delta = st - focus;
float distanceToFocus = length(delta);
vec3 direction = vec3(delta / max(distanceToFocus, .00001), 1.);
float incidence = dot(normal, direction);
float colorDistance = distanceToFocus - incidence * incidence * .5;
vec3 color = mix(spectral_zucconi6(colorDistance), vec3(1., 1., 1.25), .25);
float mask = texture2D(alphaMap, st).a;
vec3 background = mix(vec3(.065), vec3(.035), uv.y);
gl_FragColor = vec4(mix(background, color, mask), 1.);

Below, the video from Apple’s website sits next to the WebGL port, using Heshan’s textures. Both start moving when they scroll into view. Watch the edges of the coins and arrows. Then pause, or move the focus on the render: the video has its motion baked in, while the render recomputes the color as you move. The parameters and motion aren’t the same, so this compares behavior, not pixels.

The original and the rebuild, side by side

Apple Original video

Original file, with the phone cropped out. See the full video.

Rebuild Loading WebGL

Cash$226.78

Preparing the textures and the shader…

Move the mouse or drag. The color is recalculated every time the focus moves.

The comparison starts when it comes on screen, muted, and pauses when it leaves. The video doesn't follow the pointer; the render does. Moving the focus by hand pauses the animation.

Video: Apple Cash, Apple's website, cropped to the card. Render: a WebGL adaptation of Heshan Wijesinghe's work and textures, with the spectral function by Alan Zucconi. It's a visual rebuild, not Apple's code. The two move independently; their frames don't line up exactly.

There was work in the layout too. Heshan went from a Python script to Geometry Nodes in Blender, where he could tweak the angle between coins and control their size with a curve. He shared the code, the textures and the Blender file (opens in a new tab).

In the rafobase version, the positions came baked into those textures. I could play with the light, but not move a single coin. For rafobase, fine. For Nutools, that wasn’t going to work.

How to lay out the coins

So far, the credit goes almost entirely to Heshan; I’ve just explained what I learned from him. From here on, the path is mine.

On the Nutools order screen, the pattern would fill a small cutout in the right corner of a card, and this time I wanted to generate the coins in code.

I went with the most obvious idea: I looked at the reference and drew five concentric rings, with more coins in the outer ones. Relief and color following the mouse, everything in place:

My version of the card with the coins in rings

It looked nice. It didn’t look like the real thing. Too few coins, some too big, and obvious gaps between the rings. On Apple Cash I saw curves crossing the whole thing in both directions, the petals of that supposed lotus. In my rings, I saw rings.

The comparison below shows the ring version next to the spiral reconstruction that came later. On the right, besides the new arrangement, there are way more coins, smaller and closer together:

On the left, five rings with fewer coins; on the right, a denser reconstruction using Vogel's model

The clue had been in Heshan’s article all along: between one coin and the next, he rotated by −137.508°. I could have copied the number and moved on. But I wanted to understand what it had to do with the design.

Up to then I’d been thinking in rings: pick a radius, fill the circle, move to the next. His rule suggested another way to build it: place one coin at a time, always turning by the same amount around the center.

One coin at a time

Watch the coins appear first. Then pick the 180° turn and dial the count back to one coin. Add the next ones, one at a time, and notice which directions they land in.

One coin at a time
Turn: 137.508°
Coin 60 is marked. The scale stays fixed: adding a coin doesn't move the earlier ones.

The first coin goes near the center. For the second, you turn halfway around and step out a little. Another half turn for the third, another small step out, and it lands in the same direction as the first.

With 180°, you get two rows. Place a thousand coins and they’ll keep alternating between the same two sides, leaving the rest of the space empty.

A third of a turn, 120°, opens a third direction, but it soon repeats itself: every three steps, one full turn. With 144°, the repeat takes five steps, which add up to two turns. Five rows.

This happens because those turns are simple fractions of a full circle: a half, a third, two fifths. After a few steps the math closes the loop and the coins return to directions already taken. To fill the disc, every new coin would need to find a different gap.

You can escape exact repetition by using an irrational fraction of a turn, one that can’t be written as a ratio of two whole numbers. But landing almost in the same direction is a problem too. A turn very close to 120° still draws three clearly visible rows for many steps; they just bend slowly over time.

This is where Heshan’s number starts to make sense. The turn of roughly 137.5° is a fraction of a circle that is especially hard to approximate with fractions of small numbers. It avoids those near-misses and helps the coins spread out. This same property is studied to understand how leaves are arranged on plants (opens in a new tab).

Where 137.5° comes from

This turn is called the golden angle because it comes from the golden ratio. The name sounds like aesthetics talk, but you can follow the whole construction.

Take a full turn and split it into two arcs, a bigger one and a smaller one. Look for the split where the full turn relates to the bigger arc the same way the bigger arc relates to the smaller one:

full turn / bigger arc = bigger arc / smaller arc

Before doing the math, try to find that split by eye. Drag the point on the circle and compare the two ratios. The button shows where they match.

Splitting one turn into two arcs
100.000°smaller arc · green
Full turn / larger arc = 360 / 260.000 ≈ 1.385
Larger / smaller arc = 260.000 / 100.000 ≈ 2.600

Drag the point. On a keyboard, use Tab and the arrow keys.

Move the split and compare the ratios. The golden split is the one where they match.

That’s the condition that defines the golden ratio. The ratio that satisfies the equation is about 1.618 and is usually written with the Greek letter φ. On a 360° circle, the two arcs measure about 222.492° and 137.508°. You can check:

360 / 222.492 ≈ 222.492 / 137.508 ≈ 1.618

The smaller arc is the golden angle. That’s where Heshan’s number comes from; the minus sign in his implementation just picks the direction of the turn.

The fun part is watching the ratio at work: repeating that turn changes how hundreds of coins share the space. In the demo, count, size and distances stay the same. Only the turn changes.

Watch the pattern shift as the turn wobbles around the golden angle. Then try 137° and 138°. Look at the empty spaces and notice the corridors that open up between the arms of the pattern.

How much can half a degree change?
It's always 360 points of the same size, at the same distances from the center. Only the turn changes.

Each of those values is only about half a degree off the golden angle. Sounds like nothing, but the error piles up with every new point. After a few hundred points, it’s a different pattern.

Giving the pattern room to grow

The turn picks the direction of the next coin. What’s left is the distance from the center.

If the distance always grows by the same amount, the coins get more spread out as the pattern grows. One step outward near the edge covers a much bigger band of area than the same step near the center.

To keep the fill even, the area has to grow along with the number of coins. Four times as many coins need four times the area, and that means twice the radius, because a circle’s area grows with the square of its radius.

That’s why a square root shows up in the formula. It makes the radius grow at just the right pace so that every new coin adds the same amount of area to the whole.

Watch the two discs grow. The lines split each one into four parts of equal area. Notice how many points pile up in the middle when the radius grows linearly.

Same disc. Two ways to grow.

Radius proportional to n

In the center (¼ of the area): 40 points

Radius proportional to √n

In the center (¼ of the area): 20 points

Same points, turn, size, and outer radius. Only the distance rule changes.

The four bands have the same area. With the square root, each one gets the same number of points (or close to it, because of rounding).

Put that growth together with the golden angle and you get the model Helmut Vogel published in 1979 (opens in a new tab) to reproduce the arrangement of a sunflower head.

Go back to the photo at the top. In the center you can follow small green shapes along curves that run in both directions and cross each other. To make those curves easier to see, I generated points with the model and connected neighbors in three different ways:

Three patterns generated with Vogel's model, with paths of 8, 13 and 21 points highlighted in different colors

These lines are guides drawn over a geometric model, not measurements of the sunflower photo.

The center of a sunflower is made of many tiny florets, and that’s where the seeds develop. Vogel’s model approximates this arrangement by placing one point at a time. In my reconstruction, each point becomes a coin. For coin number n:

distance from center = c · √n
angle                = n · 137.508…°

The c sets the scale: the bigger it is, the farther apart all the coins get. The square root handles the relationship between count and area; the angle handles the directions.

To go from distance and angle to screen coordinates, sine and cosine come in. The demos’ function generates positions on a disc of radius 1; on the card, I just scale that disc and choose where the center goes:

export const PHI = (1 + Math.sqrt(5)) / 2;
export const GOLDEN_ANGLE = 360 / (PHI * PHI);

export function pattern(count, angle = GOLDEN_ANGLE, law = 'sqrt', capacity = count) {
  return Array.from({ length: count }, (_, i) => {
    const n = i + 1;
    const fraction = n / capacity;
    const r = law === 'linear' ? fraction : Math.sqrt(fraction);
    const theta = n * angle * Math.PI / 180;
    return { n, r, theta, x: r * Math.cos(theta), y: r * Math.sin(theta) };
  });
}

count says how many points have appeared so far; capacity fixes the size of the finished pattern, so older points don’t move while the animation adds new ones. The linear option only exists for the demo’s comparison. On the card, it’s the square root.

This is where the project turned into something else for me. The pattern I wanted to copy from a card followed a rule that also describes a flower. And in a few lines of code, I could understand every piece of the math, play with the values and see the result right away.

The curves are in the flower too

In the model, points that are consecutive in the numbering are separated by that turn of almost 138°. But neighbors in the pattern can be many positions apart in the numbering. Follow those neighbors and the spirals appear.

In the demo, watch a path get traced. Then change the jump from eight to thirteen to twenty-one. The points don’t move; what changes is which neighbors we connect.

Finding the curves between the points
The highlighted path links 1 → 14 → 27 → 40 → 53… The jumps land on points that come back around to nearby directions. The lines are guides added to the model.

Those numbers come from the Fibonacci sequence, where each number is the sum of the two before it. The ratio between two consecutive numbers gets closer and closer to the golden ratio. In the pattern, that helps explain why certain jumps find points in nearby directions and form families of curves.

You can look for these curves on a pine cone too. In the photo below, start at the tip and follow the scales around it. The relief helps you see how each piece fits between its neighbors:

Pine cone seen from the tip, with overlapping scales forming curved paths around its axis

Photo by Richard Flink, Fibonacci’s Cone (opens in a new tab), CC BY-SA 2.0 (opens in a new tab). Unmodified.

On a pine cone, the spirals wrap around an elongated surface; on a sunflower, they spread across a disc. This arrangement of plant parts, like leaves, flowers and scales, is called phyllotaxis. The chapter on phyllotaxis in The Algorithmic Beauty of Plants (opens in a new tab), by Przemysław Prusinkiewicz and Aristid Lindenmayer, builds exactly that bridge between observing shapes and finding models to draw them, from flowers to pine cones and pineapples.

So yes, the pattern on the card has a name. In plants, the arrangement is called phyllotaxis; the rule that draws the coins is a Vogel spiral.

The model is a geometric approximation. Real sunflowers have irregularities, and the spirals don’t always match Fibonacci numbers. Those variations were documented in a study of sunflowers grown by volunteers (opens in a new tab).

Watching a plant grow raises another question: how does this arrangement form? Christophe Godin, Christophe Golé and Stéphane Douady discuss how the position of organs already forming limits the space for the next ones (opens in a new tab). In their account, the organization shows up gradually, out of those local interactions during growth.

So why would plants do this? The short answer is space. Each new bud tends to show up wherever there’s the most room left between the ones already there. Repeat that many times and this way of filling space settles on a turn close to the golden angle, the same one that keeps the coins from almost-overlapping. On a sunflower head, that means lots of seeds and very little wasted gap.

And there’s an experiment that takes the question out of plants entirely. In 1992, Douady and Couder reproduced phyllotaxis patterns in a physical experiment and in a simulation (opens in a new tab). The Fibonacci-related patterns emerged on their own, through self-organization, as new elements entered the system.

The same idea shows up at wildly different scales: a point entering a pattern, an order emerging over the course of growth, curves you can see in a flower. The formula stopped being just a way to place coins.

Checking against the card itself

Heshan got to the angle, and Vogel’s model reproduces a sunflower. But the question I wanted to answer was a different one: does Apple’s card back this up? Can you pull the rule back out of an image?

I started with what the image had to offer: pixels. I separated light regions from dark ones, filled the arrow hole in the middle of each coin and found the center of each isolated shape. The script found 284 candidate shapes. That’s not 284 coins: some were letters on the card, others were coins cut off or covered. After dropping detections outside the dome and the letters of “VISA”, I had 267 points to work with.

The segmentation ran in Python with OpenCV: I marked the pixels whose highest RGB channel was above 120, filled the arrow holes so they wouldn’t break a coin into pieces, and separated the connected shapes. The math that turns each region into a point, translated to TypeScript, looks like this. The radius is that of a circle with the same area as the shape, an estimate, not the exact edge:

type Region = { centerX: number; centerY: number; area: number };

function coinCandidates(regions: Region[]) {
  return regions
    .filter(({ area }) => area > 3 && area < 5000)
    .map(({ centerX, centerY, area }) => ({
      x: centerX,
      y: centerY,
      radius: Math.sqrt(area / Math.PI),
    }));
}

I sorted those centers by distance from the middle of the base. Plotting that distance against the square root of the visible points’ order, an almost linear relationship showed up. First clue straight from Apple: the number of coins grows roughly with the area they cover. But the order of the coins I can see isn’t each coin’s original number in the sequence, because the crop hides part of the pattern. The straight line alone doesn’t prove Vogel.

The harder question was still open: how much does the pattern turn from one coin to the next? The search swept angles from 130° to 145°, in 0.5° steps. For each angle, I tested both turning directions and fitted the center, spacing and starting rotation, comparing the generated points with the measured centers. The script’s score, in TypeScript:

type Point = readonly [number, number];

function nearestDistanceSquared([x, y]: Point, points: Point[]) {
  let best = Infinity;
  for (const [px, py] of points) {
    const dx = x - px;
    const dy = y - py;
    best = Math.min(best, dx * dx + dy * dy);
  }
  return Math.min(best, 625); // 25 pixels squared
}

function fitScore(measured: Point[], generated: Point[]) {
  if (!measured.length || !generated.length) return Infinity;
  const average = (from: Point[], to: Point[]) =>
    from.reduce((sum, point) => sum + nearestDistanceSquared(point, to), 0) / from.length;

  return average(measured, generated)
    + average(generated, measured)
    + 1.5 * Math.abs(generated.length - measured.length);
}

The comparison runs both ways because a hypothesis that nails a few coins but invents a bunch of others should lose points too. Each distance is capped at 25 pixels, so cut-off or hidden coins don’t dominate the error.

I didn’t hand the search 137.508° as the answer. It found more than one plausible fit: one near 137.5° and another near 139.5°. That’s the finding. The screenshot is consistent with a Vogel spiral, but it doesn’t carry enough information to pin down the angle. Heshan’s hypothesis passes the test; you just can’t say, from a partial image, that it’s the only one.

So no, I’m not losing it: the flower is there. I just can’t prove it’s exactly the same one.

I also measured the area of each shape to estimate coin sizes. Grouped by distance from the center, they grow toward the outer part of the dome and shrink again near the edge. I turned that trend into a curve for my render.

In the experiment below, you can follow each step over the screenshot, change the parameters and see where the reconstruction gets close and where it falls apart.

From screenshot to hypothesis

The script picked out the bright pixels, filled in the hole in each arrow, and measured the center and area of every isolated shape. Each circle below marks a detection, not a confirmed coin.

267 points used · 17 candidates thrown out

The first count found 284 candidate shapes. Thirteen are outside the estimated dome; four more are letters from “VISA” picked up as blobs. Even after that filter, the list is still a rough measurement of the image.

A reading of Apple's own card, separate from Heshan's textures and code. The image lets you test a geometric hypothesis, but it can't reliably recover the parameters used in the original.

I didn’t get a coin-for-coin match, and I didn’t need one. The measurements became a ruler for tuning the reconstruction, alongside eyeballing the comparison.

Back to the order screen

With Vogel, I started generating the pattern directly in the shader. The first attempt had too much spacing: around sixty coins in the cutout, all a bit sparse. With the measurements, I got to about 170 visible coins.

The card version with the Vogel pattern and tuned spacing

Then I went back to what had hooked me in the first place: the details. The relief had to hold up at that size. The light started following the mouse with a slight delay, so the motion felt smooth. On the left edge, the pattern fades out before it fights the text for space.

Some choices changed to fit the screen. The smallest coins ended up proportionally bigger than the reference’s, because the arrow vanished when I shrank them further. And the colors got tied to the payment status: green for approved, red for declined. The effect started repeating information the screen already showed, instead of just decorating it.

The pattern in the declined payment version

The fade near the text, for example, is just this. Each coin’s opacity depends on its horizontal position x: on the left, near the message, it drops; on the right, it reaches 1. The smooth curve avoids a hard band between the two areas:

const t = clamp((x - 280) / 180, 0, 1);
ctx.globalAlpha = 1 - fade / 100 * (1 - t * t * (3 - 2 * t));

And then there’s the care nobody sees. Once the light catches up with the pointer, the animation stops asking for new frames. People who prefer reduced motion get a still pattern. Without WebGL, the same layout is drawn in SVG.

Try it on the card

Now you can put it all together. The light sweeps across the card once when it scrolls into view. After that, move the light, shrink the coins and turn off the fade near the text. Watch what happens to the arrow and to how readable the message is. Try the declined state too.

This sandbox was built for the article, using the coin and the Vogel model from the demos. It isolates the finishing touches; the iridescence was left out.

Now, play with the finish

EXPERIMENT · WALLET

Payment approvedAll good here.

The light takes a short trip when it comes on screen. Then drag it around; on a keyboard, use Tab and the arrows. With reduced motion, it responds instantly.

A teaching sandbox of my own: Vogel's distribution and the coin from the first demos. The lighting is simplified, without Apple Cash's iridescence. The light sweeps across the card once; then move it and compare legibility, size, and softness. The card doesn't make payments.

A whole afternoon on one detail of an order screen. Almost nobody will notice the coins, but whoever opens that screen will feel that someone cared. Which is exactly what Apple’s card did to me.

Along the way, I learned why a CD changes color, how a video game fakes relief, and what rule describes the seeds of a sunflower. The card never made it into my pocket, but now I know there’s a sunflower inside it. And on the Nutools order screen, there’s another one.

If you leave here wanting to chase down one of these references, or to mash together things from different fields into a detail nobody asked for, this post did its job.

Keep exploring