Learn to create a futuristic vault login system with a dummy fingerprint scan and secure access portal using HTML, CSS, and JavaScript step by step.
Table of Contents
Security is a key part of web design today. Many apps use biometrics like fingerprint or face recognition for real authentication. But in this tutorial, we will not create a real biometric system. Instead, we will design a dummy fingerprint scanner – a futuristic UI simulation of a vault login system using only HTML, CSS, and JavaScript.
Note: This fingerprint scanner is only a dummy for design purposes. It does not provide real biometric security.
By the end of this guide, you’ll have a sci-fi-inspired login portal that looks like a vault access screen with a glowing fingerprint effect and access granted animation.
Prerequisites
Before you start, make sure you have:
- Basic knowledge of HTML, CSS, and JavaScript
- A code editor (VS Code, Sublime Text, Atom, or Notepad++)
- A browser (Chrome, Edge, or Firefox) to test the project
- Basic idea of CSS animations and styling
Source Code
Step 1 (HTML Code):
To get started, we first need to create a basic HTML file. In this file, we will include the main structure for our vault login. Let's break down the HTML code:
1. Document Setup
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!DOCTYPE html>→ Defines HTML5 document.- lang="en" → Page language is English.
- meta charset="UTF-8" → Supports all characters (English + special).
- X-UA-Compatible → Ensures compatibility with old Internet Explorer.
- viewport → Makes site mobile responsive.
2. External Fonts & Icons
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
- Loads Google Fonts (Inter).
- Loads Font Awesome icons (lock, user-shield, cog, etc.).
3. Page Title & CSS
<title>Futuristic Vault Login - Fingerprint Scan & Access Portal</title>
<link rel="stylesheet" href="styles.css">
- title → Shown on browser tab & SEO.
- styles.css → External stylesheet for design.
4. Preloader (Vault + Fingerprint UI)
<div class="preloader">
<div class="preloader-content">
A preloader screen that shows vault + fingerprint scanner before content loads.
a) Vault Door Assembly
<div class="vault-perspective-container">
<div class="vault-door-assembly">
<div class="vault-frame">
<div class="vault-interior"></div>
<div class="bolt-receptacle top"></div>
<div class="bolt-receptacle right"></div>
<div class="bolt-receptacle bottom"></div>
<div class="bolt-receptacle left"></div>
</div>
<div class="vault-door">
<div class="hinge top"></div>
<div class="hinge bottom"></div>
- Creates a 3D vault door effect with bolts, hinges, and frame.
- Will later animate open when access is granted.
b) Vault Dial / Handle
<div class="vault-dial-container">
<div class="vault-dial">
<div class="vault-handle">
<div class="handle-spoke"></div>
<div class="handle-spoke"></div>
<div class="handle-spoke"></div>
<div class="handle-hub"></div>
</div>
</div>
</div>
- Creates a vault handle/dial (like a bank safe).
5. Security Panel (Fingerprint Scanner)
<div class="security-panel">
<div class="panel-header">
<span>BIOMETRIC AUTHENTICATION</span>
</div>
- Side panel with Biometric Authentication title.
a) Fingerprint Scanner SVG
<div class="fingerprint-scanner">
<svg class="fingerprint-svg" viewBox="0 0 24 24">
<g class="fingerprint-lines">
<path d="M17.81 4.47c..."/>
</g>
<g class="success-mark">
<path d="M6 12 l4 4 l8 -8" />
</g>
</svg>
<div class="scan-line"></div>
</div>
- A fingerprint icon (SVG paths).
<g class="success-mark">→ Checkmark appears when scan succeeds.- .scan-line → Animated scanning line effect.
b) Status Text
<div class="status-display">
<span class="status-text">Awaiting Scan</span>
</div>
- Shows text like Awaiting Scan → Scanning → Access Granted.
6. Vault Instructions
<div class="vault-instructions">
<p>Press and Hold to Scan</p>
</div>
- Instructions for the user.
7. Main Website Content
<div class="main-content">
<div class="content-container">
- This is the real content that shows after access is granted.
a) Welcome Animation
<div class="welcome-animation">
<i class="fas fa-lock-open success-icon"></i>
<h1>ACCESS GRANTED</h1>
<p>Secure vault system disengaged</p>
</div>
- Lock icon + “ACCESS GRANTED” animation when fingerprint scan passes.
b) Content Card with Features
<div class="content-card">
<h2>Premium Secure Portal</h2>
<p>Your sensitive content is now accessible...</p>
<div class="features-grid">
<div class="feature">
<i class="fas fa-user-shield"></i>
<h3>Security</h3>
<p>Manage your protection settings</p>
</div>
...
</div>
</div>
- Features Grid → 4 features (Security, Documents, Settings, Logout).
8. External JS Libraries
<script src="https://cdnjs.cloudflare.com/ajax/libs/tone/14.7.77/Tone.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.11.5/gsap.min.js"></script>
- Tone.js → Sound effects (e.g., scan beep, vault unlock).
- GSAP → Animations (vault door opening, scan line movement, etc.).
9. Custom JavaScript
<script src="script.js"></script>
- Controls the fingerprint scan, animations, and access granted logic.
Step 2 (CSS Code):
Once the basic HTML structure of the vault login is in place, the next step is to add styling to the page using CSS. Let's break down the CSS code:
1. Global Reset
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
- Removes default browser margins and paddings.
- Uses box-sizing: border-box so padding & border are included in element width/height.
2. Body Styling
body {
font-family: 'Inter', sans-serif;
overflow-x: hidden;
background-color: #f8fafc;
color: #334155;
}
- Sets font to Inter (fallback sans-serif).
- Prevents horizontal scrolling (overflow-x: hidden).
- Light gray background with dark gray text.
3. Preloader Screen
.preloader { ... }
.preloader-content { ... }
- Full-screen loading screen before vault unlock.
- Centered flexbox layout with smooth transitions.
- z-index: 9999 ensures it stays on top.
4. Security Panel
.security-panel { ... }
.panel-header { ... }
.scanner-area { ... }
.status-display { ... }
- Small card showing the fingerprint scanner + status.
- Dark gradient with shadow for 3D effect.
- .status-display shows “Success” or “Failed” messages.
- .scanning.fail ~ .status-display → turns text red on failed scan.
5. Fingerprint Scanner
.fingerprint-scanner { ... }
.fingerprint-lines { ... }
.scan-line { ... }
.success-mark { ... }
- Scanner is interactive (cursor: pointer).
- Uses SVG for fingerprint lines.
- .scan-line → blue glowing horizontal line that animates scanning.
- .success-mark → green checkmark after successful scan.
- .scanning.fail → fingerprint turns red & shakes (error).
6. Vault 3D Door
.vault-perspective-container { ... }
.vault-door-assembly { ... }
.vault-frame { ... }
.vault-interior { ... }
- 3D vault door effect using perspective & transform-style: preserve-3d.
- Circular metallic frame + interior gradient.
- Bolt receptacles around edges (top, right, bottom, left).
- .vault-door → rotates open when unlocked (.is-unlocked).
- Uses pseudo-elements (::before, ::after) for the front/back face of the door.
7. Vault Hinges & Dial
.hinge { ... }
.vault-dial-container { ... }
.vault-dial { ... }
.vault-handle { ... }
.handle-spoke { ... }
.handle-hub { ... }
- Hinges on the left side to make the door realistic.
- Center dial with rotating handle (3 spokes + hub) like a safe.
- .vault-handle.active → glowing pulse animation.
8. Main Content (After Unlock)
.main-content { ... }
.content-container { ... }
.welcome-animation { ... }
.content-card { ... }
.features-grid { ... }
- Hidden initially (opacity: 0; transform: scale(0.95)).
- Fades in (.visible) after unlocking.
- Cards and features grid with hover effects.
- Clean, modern design with subtle shadows and rounded corners.
9.Animations
@keyframes pulse-handle { ... }
@keyframes shake { ... }
- pulse-handle → glowing golden handle effect.
- shake → fingerprint shakes left-right when scan fails.
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Inter', sans-serif;
overflow-x: hidden;
background-color: #f8fafc;
color: #334155;
}
.preloader {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: #f8fafc;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 15px;
z-index: 9999;
transition: background-color 1s ease-out, opacity 0.5s ease-in-out;
overflow: hidden;
}
.preloader-content {
width: 100%;
max-width: 600px;
display: flex;
align-items: center;
justify-content: center;
transition: transform 1.5s ease-in, opacity 1s ease-in,
justify-content 0.8s ease-in-out;
}
/* Security Panel */
.security-panel {
width: 200px;
background: linear-gradient(145deg, #374151, #1f2937);
border-radius: 8px;
border: 1px solid #cbd5e1;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
padding: 15px;
display: flex;
flex-direction: column;
align-items: center;
gap: 15px;
}
.panel-header {
font-size: 9px;
font-weight: 600;
color: #9ca3af;
letter-spacing: 1px;
}
.scanner-area {
width: 100px;
height: 100px;
background: #111827;
border-radius: 8px;
padding: 10px;
box-shadow: inset 0 2px 10px rgba(0, 0, 0, 0.5);
}
.status-display {
width: 100%;
background: #111827;
color: #34d399;
font-size: 14px;
text-align: center;
padding: 8px;
border-radius: 4px;
box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.5);
transition: color 0.3s;
}
.scanning.fail ~ .status-display {
color: #ef4444;
}
.vault-instructions {
font-size: 10px;
font-weight: 500;
color: #111827;
letter-spacing: 0.5px;
}
/* Fingerprint Scanner */
.fingerprint-scanner {
width: 100%;
height: 100%;
cursor: pointer;
position: relative;
border-radius: 4px;
overflow: hidden;
}
.fingerprint-svg {
width: 100%;
height: 100%;
}
.fingerprint-lines {
fill: #2563eb;
clip-path: polygon(0% 0%, 100% 0%, 100% 0%, 0% 0%);
}
.scan-line {
position: absolute;
top: 0;
left: -10%;
width: 120%;
height: 2px;
background: #2563eb;
box-shadow: 0 0 10px #2563eb;
opacity: 0;
}
.success-mark {
fill: none;
stroke: #10b981;
stroke-width: 2;
stroke-linecap: round;
stroke-linejoin: round;
opacity: 0;
}
.scanning.fail .fingerprint-lines {
fill: #ef4444;
animation: shake 0.5s;
}
/* Vault Styles */
.vault-perspective-container {
perspective: 1000px;
display: none;
}
.vault-door-assembly {
position: relative;
width: 220px;
height: 220px;
transform-style: preserve-3d;
}
.vault-frame {
width: 100%;
height: 100%;
background: #374151;
border-radius: 50%;
border: 1px solid #4b5563;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3), inset 0 0 10px rgba(0, 0, 0, 0.4);
position: absolute;
}
.vault-interior {
position: absolute;
top: 10px;
left: 10px;
right: 10px;
bottom: 10px;
background: radial-gradient(circle, #222 0%, #000 100%);
border-radius: 50%;
box-shadow: inset 0 0 20px rgba(0, 0, 0, 0.8);
}
.bolt-receptacle {
position: absolute;
background: #cbd5e1;
box-shadow: inset 0 2px 5px rgba(0, 0, 0, 0.2);
}
.bolt-receptacle.top {
top: 5px;
left: 50%;
transform: translateX(-50%);
width: 30px;
height: 10px;
border-radius: 5px;
}
.bolt-receptacle.right {
right: 5px;
top: 50%;
transform: translateY(-50%);
width: 10px;
height: 30px;
border-radius: 5px;
}
.bolt-receptacle.bottom {
bottom: 5px;
left: 50%;
transform: translateX(-50%);
width: 30px;
height: 10px;
border-radius: 5px;
}
.bolt-receptacle.left {
left: 5px;
top: 50%;
transform: translateY(-50%);
width: 10px;
height: 30px;
border-radius: 5px;
}
.vault-door {
position: absolute;
width: 100%;
height: 100%;
background: radial-gradient(circle, #4b5563 0%, #1f2937 100%);
border-radius: 50%;
transform-origin: left;
transform-style: preserve-3d;
box-shadow: 5px 0 15px rgba(0, 0, 0, 0.3);
transition: transform 1.5s cubic-bezier(0.65, 0, 0.35, 1);
}
.vault-door::before {
content: '';
position: absolute;
inset: 0;
border-radius: 50%;
background: radial-gradient(circle, #4b5563 0%, #1f2937 100%);
backface-visibility: hidden;
}
.vault-door::after {
content: '';
position: absolute;
inset: 0;
border-radius: 50%;
background: #111827;
transform: rotateY(180deg);
backface-visibility: hidden;
}
.vault-door.is-unlocked {
transform: rotateY(-120deg);
box-shadow: 25px 0 50px rgba(0, 0, 0, 0.2);
}
.hinge {
position: absolute;
width: 18px;
height: 40px;
background: #374151;
border-radius: 4px 0 0 4px;
border: 1px solid #4b5563;
box-shadow: -2px 0 5px rgba(0, 0, 0, 0.1);
}
.hinge.top {
top: 50px;
}
.hinge.bottom {
bottom: 50px;
}
.vault-dial-container {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 150px;
height: 150px;
background: radial-gradient(circle, #1f2937, #111827);
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
box-shadow: inset 0 3px 8px rgba(0, 0, 0, 0.1);
}
.vault-dial {
width: 120px;
height: 120px;
background: linear-gradient(145deg, #4b5563, #1f2937);
border-radius: 50%;
border: 1px solid #6b7280;
display: flex;
justify-content: center;
align-items: center;
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.1);
position: relative;
}
.vault-handle {
width: 100%;
height: 100%;
position: absolute;
cursor: pointer;
transition: transform 0.5s ease-in-out;
}
.vault-handle.active {
animation: pulse-handle 1.5s infinite;
}
.handle-spoke {
position: absolute;
top: 50%;
left: 50%;
width: 60px;
height: 10px;
background: linear-gradient(to right, #fcd34d, #fbbf24);
border-radius: 5px;
border: 1px solid #f59e0b;
transform-origin: 5px 5px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
}
.handle-spoke:nth-child(1) {
transform: translate(-5px, -5px) rotate(0deg);
}
.handle-spoke:nth-child(2) {
transform: translate(-5px, -5px) rotate(120deg);
}
.handle-spoke:nth-child(3) {
transform: translate(-5px, -5px) rotate(240deg);
}
.handle-hub {
position: absolute;
top: 50%;
left: 50%;
width: 25px;
height: 25px;
background: radial-gradient(circle, #fbbf24, #f59e0b);
border-radius: 50%;
border: 1px solid #d97706;
transform: translate(-50%, -50%);
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
}
/* Main content */
.main-content {
text-align: center;
padding: 30px;
opacity: 0;
transform: scale(0.95);
transition: opacity 0.8s ease-out 0.5s, transform 0.8s ease-out 0.5s;
max-width: 800px;
margin: 0 auto;
}
.main-content.visible {
opacity: 1;
transform: scale(1);
}
.content-container {
display: flex;
flex-direction: column;
gap: 30px;
}
.welcome-animation {
display: flex;
flex-direction: column;
align-items: center;
gap: 10px;
margin-bottom: 20px;
}
.success-icon {
font-size: 48px;
color: #10b981;
margin-bottom: 10px;
}
.welcome-animation h1 {
font-size: 28px;
font-weight: 600;
color: #1e293b;
}
.welcome-animation p {
color: #64748b;
font-size: 16px;
}
.content-card {
background: white;
border-radius: 12px;
padding: 30px;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
text-align: center;
border: 1px solid #e2e8f0;
}
.content-card h2 {
font-size: 24px;
font-weight: 600;
color: #1e293b;
margin-bottom: 15px;
}
.content-card p {
color: #64748b;
font-size: 16px;
margin-bottom: 30px;
}
.features-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
gap: 20px;
margin-top: 20px;
}
.feature {
background: #f8fafc;
border-radius: 8px;
padding: 20px;
transition: all 0.3s ease;
border: 1px solid #e2e8f0;
}
.feature:hover {
transform: translateY(-3px);
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
border-color: #cbd5e1;
}
.feature i {
font-size: 24px;
color: #3b82f6;
margin-bottom: 15px;
}
.feature h3 {
font-size: 18px;
font-weight: 600;
color: #1e293b;
margin-bottom: 8px;
}
.feature p {
color: #64748b;
font-size: 14px;
margin-bottom: 0;
}
/* Animations */
@keyframes pulse-handle {
0% {
filter: brightness(1);
}
50% {
filter: brightness(1.2) drop-shadow(0 0 8px #fcd34d);
}
100% {
filter: brightness(1);
}
}
@keyframes shake {
10%,
90% {
transform: translate3d(-1px, 0, 0);
}
20%,
80% {
transform: translate3d(2px, 0, 0);
}
30%,
50%,
70% {
transform: translate3d(-4px, 0, 0);
}
40%,
60% {
transform: translate3d(4px, 0, 0);
}
} Step 3 (JavaScript Code):
Finally, we need to create a function in JavaScript. Let's break down the JavaScript code:
1. Wait until DOM is Ready
document.addEventListener('DOMContentLoaded', () => {
- Ensures the code runs only after HTML is fully loaded.
- All DOM elements are available to query safely.
2. Grab Important DOM Elements
const preloader = document.querySelector('.preloader');
const vaultDoor = document.querySelector('.vault-door');
const scanner = document.querySelector('.fingerprint-scanner');
const handle = document.querySelector('.vault-handle');
const mainContent = document.querySelector('.main-content');
...
- Selects UI parts:
- Preloader screen
- Vault door & handle
- Fingerprint scanner
- Status text
- Success mark (checkmark)
- Main content (hidden until unlock)
3. State Variables
let scanComplete = false;
let isUnlocked = false;
let scanTimeline;
- scanComplete → becomes true once scanning succeeds.
- isUnlocked → becomes true once the vault is open.
- scanTimeline → GSAP animation reference (so it can be stopped).
4. Audio Synths (Dummy Sound Effects)
Using Tone.js to create futuristic sounds:
const successSynth = new Tone.PolySynth(...); // Success chime
const boltSynth = new Tone.MetalSynth(...); // Lock bolt sound
const doorSynth = new Tone.NoiseSynth(...); // Door opening sound
const scannerSynth = new Tone.AMSynth(...); // Scanning sound
const failSynth = new Tone.Synth(...); // Failure beep
- Each plays different sounds during interactions.
5. Event Listeners
scanner.addEventListener('mousedown', startScan);
scanner.addEventListener('mouseup', cancelScan);
handle.addEventListener('click', handleHandleTurn);
- Fingerprint scanner → starts scanning on press, cancels on release.
- Vault handle → clickable after scan success to open vault.
6. Start Scan
function startScan(e) {
if (scanComplete || isUnlocked) return;
scannerSynth.triggerAttack('C2'); // Start scan sound
statusText.textContent = 'Scanning...';
scanner.classList.add('scanning');
// GSAP timeline: animate scan line + fingerprint highlight
scanTimeline = gsap.timeline({ onComplete: () => { scanSuccess(); } });
scanTimeline
.to(scanLine, { opacity: 1, duration: 0.2 })
.set(fingerprintLines, { filter: 'brightness(1.5)' })
.to(fingerprintLines, { clipPath: 'polygon(...)', duration: 2.0 })
.to(scanLine, { y: '80px', duration: 2.0 }, '<');
}
- Plays a scanning sound.
- Animates fingerprint glowing + scan line moving down.
- If animation completes → calls scanSuccess().
7. Cancel Scan
function cancelScan() {
if (scanTimeline && scanTimeline.isActive()) {
scannerSynth.triggerRelease(); // Stop scan sound
failSynth.triggerAttackRelease('A2', '0.1'); // Error beep
scanTimeline.kill(); // Stop animation
gsap.to([fingerprintLines, scanLine], { clearProps: 'all', duration: 0.3 });
scanner.classList.add('fail');
statusText.textContent = 'Scan Failed';
// Reset to idle after 1s
setTimeout(() => {
scanner.classList.remove('scanning');
scanner.classList.remove('fail');
statusText.textContent = 'Awaiting Scan';
}, 1000);
}
}
- If the user releases the finger too early, the scan fails.
- Plays failure beep, resets UI, shows "Scan Failed".
8. Successful Scan
function scanSuccess() {
scanComplete = true;
scanner.style.cursor = 'default';
scannerSynth.triggerRelease(); // Stop scanning sound
const tl = gsap.timeline({ onComplete: activateHandle });
tl.to(fingerprintLines, { filter: 'brightness(1)' })
.to(scanLine, { opacity: 0 })
.set(statusText, { textContent: 'Verified' })
.fromTo(successMark, { scale: 0, opacity: 0 }, { scale: 1, opacity: 1 })
.to(securityPanel, { opacity: 0, scale: 0.9, onComplete: () => { securityPanel.style.display = 'none'; } })
.fromTo(vaultContainer, { display: 'none', opacity: 0 }, { display: 'block', opacity: 1 });
}
- Marks scan as complete.
- Shows "Verified" + green checkmark.
- Hides scanner panel → reveals vault door.
- Calls activateHandle() afterward.
9. Activate Handle
function activateHandle() {
successSynth.triggerAttackRelease(['C4', 'E4', 'G4'], '0.5'); // Success sound
instructions.textContent = 'Click Handle to Open';
handle.classList.add('active'); // Glow effect
}
- Plays success chime.
- Update instructions.
- Makes the handle glow (ready to be clicked).
10. Handle Turn
function handleHandleTurn() {
if (!scanComplete || isUnlocked) return;
isUnlocked = true;
const tl = gsap.timeline();
tl.to(handle, { rotation: 120, duration: 0.5 }) // Rotate handle
.call(() => boltSynth.triggerAttackRelease('C2', '0.2')) // Lock bolt sound
.call(openDoor); // Then open vault
}
- Rotates the handle animation.
- Plays metallic bolt unlocking sound.
- Calls openDoor().
11. Open Vault Door
function openDoor() {
doorSynth.triggerAttack(); // Door opening sound
vaultDoor.classList.add('is-unlocked'); // Rotate open
setTimeout(() => {
mainContent.style.display = 'block';
const revealTl = gsap.timeline({
onComplete: () => { setTimeout(() => preloader.remove(), 500); },
});
revealTl
.to(preloaderContent, { scale: 15, duration: 1.0 }) // Explosion effect
.to(mainContent, { opacity: 1, duration: 1.0 }) // Show content
.to(preloader, { opacity: 0, duration: 0.5 }, '<');
}, 600);
}
- Vault door rotates open.
- Plays whoosh door sound.
- Expands preloader content (cool zoom-out effect).
- Reveals hidden main page content.
- Removes preloader entirely.
document.addEventListener('DOMContentLoaded', () => {
// --- DOM Elements ---
const preloader = document.querySelector('.preloader');
const preloaderContent = document.querySelector('.preloader-content');
const vaultContainer = document.querySelector('.vault-perspective-container');
const vaultDoor = document.querySelector('.vault-door');
const mainContent = document.querySelector('.main-content');
const handle = document.querySelector('.vault-handle');
const scanner = document.querySelector('.fingerprint-scanner');
const securityPanel = document.querySelector('.security-panel');
const statusText = document.querySelector('.status-text');
const instructions = document.querySelector('.vault-instructions p');
const fingerprintLines = document.querySelector('.fingerprint-lines');
const scanLine = document.querySelector('.scan-line');
const successMark = document.querySelector('.success-mark');
// --- State Variables ---
let scanComplete = false;
let isUnlocked = false;
let scanTimeline; // To hold the GSAP timeline
// --- Audio Synthesis ---
const successSynth = new Tone.PolySynth(Tone.Synth, {
volume: -8,
oscillator: { type: 'sine' },
envelope: { attack: 0.005, decay: 0.1, sustain: 0.3, release: 1 },
}).toDestination();
const boltSynth = new Tone.MetalSynth({
frequency: 80,
envelope: { attack: 0.001, decay: 0.1, release: 0.05 },
harmonicity: 3.1,
modulationIndex: 40,
resonance: 3000,
octaves: 1.5,
}).toDestination();
const doorSynth = new Tone.NoiseSynth({
noise: { type: 'brown' },
envelope: { attack: 0.1, decay: 1.4, sustain: 0, release: 0.1 },
}).toDestination();
const scannerSynth = new Tone.AMSynth({
harmonicity: 1.5,
envelope: { attack: 0.01, decay: 0.2, sustain: 0.1, release: 0.1 },
modulationEnvelope: { attack: 0.1, decay: 0.1, sustain: 0.2, release: 0.1 },
}).toDestination();
const failSynth = new Tone.Synth({
oscillator: { type: 'square' },
envelope: { attack: 0.01, decay: 0.2, sustain: 0, release: 0.1 },
}).toDestination();
// --- Event Listeners ---
scanner.addEventListener('mousedown', startScan);
scanner.addEventListener('touchstart', startScan);
scanner.addEventListener('mouseup', cancelScan);
scanner.addEventListener('mouseleave', cancelScan);
scanner.addEventListener('touchend', cancelScan);
handle.addEventListener('click', handleHandleTurn);
function startScan(e) {
e.preventDefault();
if (scanComplete || isUnlocked) return;
scannerSynth.triggerAttack('C2');
statusText.textContent = 'Scanning...';
scanner.classList.add('scanning');
// Create and play the GSAP timeline
scanTimeline = gsap.timeline({
onComplete: () => {
scanSuccess();
},
});
scanTimeline
.to(scanLine, { opacity: 1, duration: 0.2 })
.set(fingerprintLines, { filter: 'brightness(1.5)' })
.to(
fingerprintLines,
{
clipPath: 'polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%)',
duration: 2.0, // Slower scan for hold effect
ease: 'power1.inOut',
},
'-=0.2'
)
.to(scanLine, { y: '80px', duration: 2.0, ease: 'power1.inOut' }, '<');
}
function cancelScan() {
if (scanComplete || isUnlocked) return;
if (scanTimeline && scanTimeline.isActive()) {
scannerSynth.triggerRelease();
failSynth.triggerAttackRelease('A2', '0.1', '+0.05');
scanTimeline.kill();
gsap.to([fingerprintLines, scanLine], {
clearProps: 'all',
duration: 0.3,
});
scanner.classList.add('fail');
statusText.textContent = 'Scan Failed';
setTimeout(() => {
scanner.classList.remove('scanning');
scanner.classList.remove('fail');
statusText.textContent = 'Awaiting Scan';
}, 1000);
}
}
function scanSuccess() {
if (scanComplete) return;
scanComplete = true;
scanner.style.cursor = 'default';
scannerSynth.triggerRelease();
const tl = gsap.timeline({ onComplete: activateHandle });
tl.to(fingerprintLines, { filter: 'brightness(1)', duration: 0.3 })
.to(scanLine, { opacity: 0, duration: 0.3 })
.set(statusText, { textContent: 'Verified' })
.fromTo(
successMark,
{ scale: 0, opacity: 0 },
{ scale: 1, opacity: 1, duration: 0.5, ease: 'back.out(1.7)' }
)
.to(securityPanel, {
opacity: 0,
scale: 0.9,
duration: 0.5,
ease: 'power2.in',
onComplete: () => {
securityPanel.style.display = 'none';
},
})
.fromTo(
vaultContainer,
{ display: 'none', opacity: 0, scale: 0.9 },
{
display: 'block',
opacity: 1,
scale: 1,
duration: 0.6,
ease: 'power2.out',
}
);
}
function activateHandle() {
successSynth.triggerAttackRelease(['C4', 'E4', 'G4'], '0.5', Tone.now());
instructions.textContent = 'Click Handle to Open';
handle.classList.add('active');
}
function handleHandleTurn() {
if (!scanComplete || isUnlocked) return;
isUnlocked = true;
handle.classList.remove('active');
const tl = gsap.timeline();
tl.to(
handle,
{
rotation: 120,
duration: 0.5,
ease: 'power2.inOut',
},
'-=0.2'
)
.call(() => boltSynth.triggerAttackRelease('C2', '0.2', Tone.now()))
.call(openDoor);
securityPanel.style.display = 'none';
}
function openDoor() {
doorSynth.triggerAttack(Tone.now());
vaultDoor.classList.add('is-unlocked');
setTimeout(() => {
mainContent.style.display = 'block';
const revealTl = gsap.timeline({
onComplete: () => {
setTimeout(() => preloader.remove(), 500);
},
});
revealTl
.to(preloaderContent, {
scale: 15,
duration: 1.0,
ease: 'power3.in',
})
.to(mainContent, {
opacity: 1,
duration: 1.0,
ease: 'power1.out',
})
.to(
preloader,
{
opacity: 0,
duration: 0.5,
},
'<'
);
}, 600);
}
});Final Output:
Conclusion:
You’ve successfully created a Futuristic Vault Login with Dummy Fingerprint Scan using HTML, CSS, and JavaScript.
Remember: This is a dummy scanner only for UI and design. It does not provide real biometric security.
This project is great for:
With a little creativity, you can extend it with animations, neon effects, or background music to make it look like a real sci-fi vault access system.
That’s a wrap!
I hope you enjoyed this post. Now, with these examples, you can create your own amazing page.
Did you like it? Let me know in the comments below 🔥 and you can support me by buying me a coffee
And don’t forget to sign up to our email newsletter so you can get useful content like this sent right to your inbox!
Thanks!
Faraz 😊


