Shimmer OTP Input

  • Skia
  • Reanimated

A code field whose focused cell is drawn by a shader rather than styled with a border. One SKSL pass measures how far each pixel sits from the cell's outline, and the ring, the travelling highlight and the outer glow all come from that single number.

A ring from a distance field

sdRoundRect returns the signed distance to the cell outline: negative inside, positive outside, zero on the edge. Taking the absolute value and running it through a smoothstep either side of half the border width leaves a band hugging the edge from both directions, anti-aliased by a 1.5px feather instead of by stroking a path.

float2 q = abs(p) - b + r;return length(max(q, 0.0)) + min(max(q.x, q.y), 0.0) - r;// …float aa = 1.5;float ring = smoothstep(u_border * 0.5 + aa, u_border * 0.5 - aa, abs(d));

A highlight that travels the diagonal

The sweep runs along (uv.x + uv.y) * 0.5, which is a corner-to-corner coordinate, and fract scrolls it with the clock. A smoothstep picks the middle of each cycle and cubing it tightens the result from a soft gradient into a glint. Turn the shimmer off and the ring still breathes between 0.72 and full on a slow sine.

float axis = (uv.x + uv.y) * 0.5;            // 0..1 diagonal coordinatefloat sweep = fract(axis - u_time * 0.6);    // travels corner-to-corner over timefloat band = smoothstep(0.5, 0.0, abs(sweep - 0.5) * 2.0); // bright at the band centreband = pow(band, 3.0);                       // tighten into a crisp highlightfloat pulse = 0.72 + 0.28 * (0.5 + 0.5 * sin(u_time * 3.2));float lit = mix(pulse, max(pulse * 0.55, band), u_shimmer);

A glow that stays outside the cell

step(0.0, d) zeroes the halo wherever a pixel is inside the box, so it never tints the digit. The falloff also has to be gone before the host's clip: a plain exponential still carried 17% alpha at the edge of a narrow cell, which turned the soft halo into a visible rectangle. It is now faded against the same 0.35-cell-width pad the clip uses.

float glow = step(0.0, d) * exp(-d * 0.10) * 0.5;float glowPad = u_size.x * 0.35;glow *= 1.0 - smoothstep(glowPad * 0.6, glowPad, d);float ringA = ring * lit;float a = clamp(ringA + glow, 0.0, 1.0) * u_focus;

Tuning

  • 6
  • #2D9CFF
  • On
<ShimmerOtpInput params={{ length: 6, focusColor: "#2D9CFF", shimmer: true }} fireSignal={0} />