webdevpuneet.com

Mobile Biometric Unlock Screen — Simulated Fingerprint Scan with Real Retry Logic

Mobile Biometric Unlock Screen · Mobile · Plain HTML, CSS & JS · Live preview

Copy & paste

Source Code

<div class="demo">
  <div class="phone">
    <div class="notch"></div>
    <div class="screen" id="screen">
      <div class="lock-top">
        <span class="lock-time">9:41</span>
        <span class="lock-date">Thursday, August 27</span>
      </div>

      <div class="lock-mid">
        <button class="scan-ring" id="scanBtn" aria-label="Scan fingerprint to unlock">
          <svg id="fpIcon" width="34" height="34" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"><path d="M12 3a7 7 0 0 0-7 7c0 2 .5 3 1 4M12 3a7 7 0 0 1 7 7c0 3-1 5-1 5M9 21c-.5-1-1-3-1-5a4 4 0 0 1 8 0c0 1 0 2-.5 3M12 7a5 5 0 0 0-5 5c0 2 .5 3 1 4M12 7a5 5 0 0 1 5 5c0 1.5-.3 3-1 4.5M12 11a1.5 1.5 0 0 0-1.5 1.5c0 2 .8 3.5 1.5 4.5"/></svg>
          <svg class="progress-ring" width="96" height="96" viewBox="0 0 96 96">
            <circle cx="48" cy="48" r="44" fill="none" stroke="#334155" stroke-width="3" />
            <circle id="progressCircle" cx="48" cy="48" r="44" fill="none" stroke="#34d399" stroke-width="3" stroke-linecap="round" stroke-dasharray="276.5" stroke-dashoffset="276.5" transform="rotate(-90 48 48)" />
          </svg>
        </button>
        <p class="scan-status" id="scanStatus">Tap to scan fingerprint</p>
      </div>

      <div class="lock-bottom">
        <span id="attemptsNote"></span>
      </div>
    </div>
  </div>
</div>

Extend with AI

Take This Snippet Further With AI

The AI prompt for this one is for Patreon supporters ($3 a month), along with a site with no ads. Supporters enter the passcode once and it is remembered in this browser.

· Become a supporter

What's included

Features

Progress ring animation driven by real elapsed time via performance.now(), not a fixed per-frame increment
requestAnimationFrame-based animation loop stays accurate regardless of the device's actual refresh rate
Genuinely randomized scan failure (~25%) models real biometric sensor read-failure rates, not an always-succeeds demo
Persistent failed-attempt counter that survives across scan attempts and resets only on success
Escalating guidance message — attempt count, then a passcode-fallback suggestion after repeated failures
Distinct visual states (idle, scanning, success, fail) with a shake animation specifically on failure
Circumference computed once from the SVG circle's actual radius, reused consistently for every progress update
Realistic phone lock-screen chrome (notch, time, date) for a believable mockup context

About this UI Snippet

Mobile Biometric Unlock Screen — A Scan Sequence With a Real Outcome, Not Just an Icon

Screenshot of the Mobile Biometric Unlock Screen snippet rendered live

Most lock-screen mockups show a static fingerprint icon with no actual interaction behind it. This one implements the full scan sequence a real biometric unlock goes through: a timed progress ring fills in as the "scan" runs, then resolves to either a success state or — realistically, since real sensors do occasionally fail to get a clean read — a failure state that increments a genuine attempt counter and eventually suggests falling back to a passcode.

The progress ring is driven by elapsed time, not a fixed-step counter

runScan() records a performance.now() timestamp at the start and, inside a requestAnimationFrame loop, computes pct = elapsed / duration on every frame — deriving progress from actual elapsed wall-clock time rather than incrementing a counter by a fixed amount per frame. This keeps the ring's fill rate consistent regardless of the device's actual frame rate; a lower-refresh-rate device gets fewer, larger per-frame progress jumps, while a high-refresh-rate device gets more, smaller ones, but the *total* animation duration stays accurate either way.

Circumference-based stroke-dashoffset, computed once and reused

CIRCUMFERENCE = 2 * Math.PI * 44 is calculated once from the SVG circle's actual radius attribute, and every progress update sets strokeDashoffset as CIRCUMFERENCE * (1 - pct) — the same stroke-dasharray/dashoffset ring-fill technique used elsewhere in this library's loaders, applied here to represent scan progress specifically rather than a generic loading state.

A genuinely randomized failure path, not just a happy-path demo

finishScan() resolves with Math.random() > 0.25 — a real ~25% chance of failure on every scan attempt, deliberately modeling that biometric sensors don't have perfect read rates in practice. A failed scan triggers a distinct visual state (a red icon with a brief shake animation) and increments a persistent failCount that survives across scan attempts until a success resets it back to zero.

The attempt counter changes its own message as it climbs

Below three failed attempts, the note explicitly states how many tries remain before a passcode fallback would be required ("Attempt 2 of 3…"); at three or more, the message itself changes to suggest using a passcode instead — modeling the realistic security UX pattern where a device nudges toward a fallback authentication method after repeated biometric failures, rather than letting a user retry an unreliable scan indefinitely with no escalation.

Step by step

How to Use

  1. 1
    Tap the fingerprint icon to run a scanThe ring fills over about 1.1 seconds, then resolves to a success or failure state.
  2. 2
    Try scanning several timesAbout 1 in 4 attempts fails — watch the attempt counter and its message change as failures accumulate.
  3. 3
    Adjust the scan durationChange the duration constant (in milliseconds) inside runScan() to make the simulated scan faster or slower.
  4. 4
    Adjust the simulated failure rateChange the 0.25 threshold in finishScan() — a higher number means a lower failure rate, and vice versa.
  5. 5
    Change the passcode-fallback thresholdUpdate the failCount >= 3 check in finishScan() to require more or fewer failed attempts before suggesting a passcode.

Real-world uses

Common Use Cases

Auth Flow Mockups
Show off a biometric unlock flow, including its realistic failure and retry path, in a portfolio or app pitch.
PROTOTYPE
Security UX Prototyping
Prototype and test copy/messaging for a real biometric-to-passcode fallback flow before implementation.
Design System Reference
A concrete reference implementation of a time-based progress ring animation using requestAnimationFrame.
EDUCATION
Teaching requestAnimationFrame Timing
A clean example of driving animation progress from elapsed time rather than a naive frame counter.
Related: Live Caption Overlay
See the Live Caption Overlay for a related mobile pattern worth pairing with this one.
Related: Mobile Search Filters Screen
See the Mobile Search Filters Screen for a related mobile pattern worth pairing with this one.
Related: Mobile Voice Message Recording Screen
See the Mobile Voice Message Recording Screen for a related mobile pattern worth pairing with this one.

Got questions?

Frequently Asked Questions

requestAnimationFrame doesn't guarantee a fixed interval between calls — it varies with the device's actual refresh rate and current load. Computing pct = elapsedTime / totalDuration keeps the animation's real-world duration accurate regardless of how many or how few frames actually get scheduled during that time.

No — finishScan() uses Math.random() > 0.25, giving each attempt roughly a 25% chance of failing, deliberately modeling the reality that biometric sensors occasionally fail to get a clean read, rather than presenting an unrealistic always-succeeds demo.

The failCount variable persists across attempts (resetting only on a successful scan) and the status message changes once it reaches 3 or more, suggesting the user fall back to a passcode instead of continuing to retry — mirroring how real devices handle repeated biometric failures.

It uses the standard SVG stroke-dasharray/stroke-dashoffset technique: the circle's circumference is computed once from its radius, and stroke-dashoffset is set to circumference * (1 - progress) on every animation frame, so the ring visually fills as progress increases from 0 to 1.

Yes — change Math.random() > 0.25 to always return true (e.g. replace it with true), though keeping some randomized failure is what makes the retry-counter and passcode-fallback messaging logic actually demonstrable.

No — this is a purely visual and interaction simulation intended for mockups and prototyping. A production implementation of real biometric authentication would use the WebAuthn API rather than a randomized JavaScript simulation like this one.