| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>Deepfake Detector</title> |
| <link href="https://fonts.googleapis.com/css2?family=Quicksand:wght@300;400;500;600;700&display=swap" rel="stylesheet"> |
| <style> |
| * { |
| margin: 0; |
| padding: 0; |
| box-sizing: border-box; |
| font-family: 'Quicksand', sans-serif; |
| } |
| |
| body { |
| min-height: 100vh; |
| background: linear-gradient(135deg, #fce4ec 0%, #f8bbd0 25%, #e1bee7 50%, #f8bbd0 75%, #fce4ec 100%); |
| background-size: 400% 400%; |
| animation: bgFloat 15s ease infinite; |
| display: flex; |
| justify-content: center; |
| align-items: center; |
| padding: 20px; |
| } |
| |
| @keyframes bgFloat { |
| 0% { background-position: 0% 50%; } |
| 50% { background-position: 100% 50%; } |
| 100% { background-position: 0% 50%; } |
| } |
| |
| .container { |
| background: rgba(255, 255, 255, 0.85); |
| backdrop-filter: blur(15px); |
| border: 3px solid #ffb6c1; |
| border-radius: 30px; |
| box-shadow: 0 10px 40px rgba(255, 182, 193, 0.4), 0 0 60px rgba(255, 192, 203, 0.2); |
| padding: 40px; |
| max-width: 850px; |
| width: 100%; |
| animation: fadeInUp 0.8s ease; |
| } |
| |
| @keyframes fadeInUp { |
| from { |
| opacity: 0; |
| transform: translateY(30px); |
| } |
| to { |
| opacity: 1; |
| transform: translateY(0); |
| } |
| } |
| |
| h1 { |
| font-size: 2.5em; |
| font-weight: 700; |
| text-align: center; |
| background: linear-gradient(45deg, #ff69b4, #e91e63, #ff1493); |
| -webkit-background-clip: text; |
| -webkit-text-fill-color: transparent; |
| background-clip: text; |
| margin-bottom: 15px; |
| letter-spacing: 1px; |
| } |
| |
| .description { |
| color: #ad1457; |
| font-size: 1.1em; |
| font-weight: 400; |
| text-align: center; |
| background: rgba(255, 228, 235, 0.5); |
| border-radius: 20px; |
| padding: 18px; |
| border-left: 4px solid #ff69b4; |
| line-height: 1.8; |
| margin-bottom: 30px; |
| } |
| |
| .upload-area { |
| background: rgba(255, 228, 235, 0.6); |
| border: 3px dashed #ffb6c1; |
| border-radius: 20px; |
| padding: 40px; |
| text-align: center; |
| cursor: pointer; |
| transition: all 0.4s ease; |
| margin-bottom: 25px; |
| position: relative; |
| } |
| |
| .upload-area:hover { |
| background: rgba(255, 192, 203, 0.5); |
| border-color: #ff69b4; |
| box-shadow: 0 5px 25px rgba(255, 105, 180, 0.3); |
| transform: translateY(-3px); |
| } |
| |
| .upload-area.dragover { |
| background: rgba(255, 192, 203, 0.7); |
| border-color: #ff1493; |
| box-shadow: 0 8px 35px rgba(255, 20, 147, 0.4); |
| } |
| |
| .upload-icon { |
| font-size: 4em; |
| margin-bottom: 10px; |
| } |
| |
| .upload-text { |
| color: #d81b60; |
| font-size: 1.2em; |
| font-weight: 600; |
| } |
| |
| .upload-subtext { |
| color: #c2185b; |
| font-size: 0.9em; |
| margin-top: 5px; |
| font-weight: 400; |
| } |
| |
| #fileInput { |
| display: none; |
| } |
| |
| .preview-container { |
| display: none; |
| text-align: center; |
| margin-bottom: 25px; |
| animation: fadeInUp 0.5s ease; |
| } |
| |
| .preview-container.show { |
| display: block; |
| } |
| |
| #previewImage { |
| max-width: 100%; |
| max-height: 400px; |
| border: 4px solid #ffb6c1; |
| border-radius: 20px; |
| box-shadow: 0 8px 25px rgba(255, 105, 180, 0.3); |
| transition: all 0.4s ease; |
| } |
| |
| #previewImage:hover { |
| border-color: #ff69b4; |
| box-shadow: 0 12px 35px rgba(255, 20, 147, 0.5); |
| transform: scale(1.02); |
| } |
| |
| .btn { |
| display: block; |
| width: 100%; |
| background: linear-gradient(45deg, #ffb6c1, #ff69b4, #ff1493); |
| border: none; |
| color: white; |
| font-weight: 700; |
| font-size: 1.2em; |
| border-radius: 50px; |
| padding: 16px 40px; |
| letter-spacing: 1px; |
| box-shadow: 0 8px 25px rgba(255, 105, 180, 0.5); |
| transition: all 0.3s ease; |
| cursor: pointer; |
| text-transform: uppercase; |
| margin-bottom: 20px; |
| } |
| |
| .btn:hover { |
| background: linear-gradient(45deg, #ff69b4, #ff1493, #c2185b); |
| box-shadow: 0 12px 35px rgba(255, 20, 147, 0.6); |
| transform: translateY(-4px) scale(1.02); |
| } |
| |
| .btn:active { |
| transform: translateY(-1px) scale(0.98); |
| } |
| |
| .btn:disabled { |
| opacity: 0.5; |
| cursor: not-allowed; |
| transform: none; |
| } |
| |
| .result-container { |
| display: none; |
| background: linear-gradient(135deg, rgba(255, 182, 193, 0.15), rgba(255, 192, 203, 0.1)); |
| border: 2px solid #ffb6c1; |
| border-radius: 25px; |
| padding: 25px; |
| text-align: center; |
| box-shadow: 0 5px 20px rgba(255, 182, 193, 0.3); |
| animation: fadeInUp 0.6s ease; |
| } |
| |
| .result-container.show { |
| display: block; |
| } |
| |
| .result-label { |
| color: #c2185b; |
| font-size: 1em; |
| font-weight: 500; |
| margin-bottom: 10px; |
| letter-spacing: 1px; |
| } |
| |
| .result-value { |
| color: #c2185b; |
| font-size: 1.8em; |
| font-weight: 700; |
| letter-spacing: 1px; |
| text-transform: uppercase; |
| } |
| |
| .examples { |
| display: grid; |
| grid-template-columns: repeat(auto-fill, minmax(80px, 1fr)); |
| gap: 10px; |
| margin-top: 25px; |
| padding: 15px; |
| background: rgba(255, 228, 235, 0.3); |
| border-radius: 20px; |
| } |
| |
| .example-img { |
| width: 100%; |
| aspect-ratio: 1; |
| object-fit: cover; |
| border: 3px solid #ffb6c1; |
| border-radius: 15px; |
| cursor: pointer; |
| transition: all 0.3s ease; |
| } |
| |
| .example-img:hover { |
| border-color: #ff69b4; |
| box-shadow: 0 5px 20px rgba(255, 105, 180, 0.4); |
| transform: scale(1.1); |
| } |
| |
| .example-label { |
| color: #c2185b; |
| font-size: 0.8em; |
| text-align: center; |
| font-weight: 500; |
| margin-top: 5px; |
| } |
| |
| .spinner { |
| display: inline-block; |
| width: 20px; |
| height: 20px; |
| border: 3px solid rgba(255, 255, 255, 0.3); |
| border-radius: 50%; |
| border-top-color: #fff; |
| animation: spin 0.8s linear infinite; |
| margin-right: 10px; |
| vertical-align: middle; |
| } |
| |
| @keyframes spin { |
| to { transform: rotate(360deg); } |
| } |
| |
| .error-message { |
| color: #d32f2f; |
| background: rgba(211, 47, 47, 0.1); |
| border: 1px solid #d32f2f; |
| border-radius: 15px; |
| padding: 15px; |
| text-align: center; |
| margin-top: 15px; |
| display: none; |
| animation: fadeInUp 0.4s ease; |
| } |
| |
| .error-message.show { |
| display: block; |
| } |
| |
| footer { |
| color: #e91e63; |
| text-align: center; |
| font-size: 0.9em; |
| opacity: 0.8; |
| font-weight: 300; |
| margin-top: 25px; |
| } |
| </style> |
| </head> |
| <body> |
| <div class="container"> |
| <h1>Deepfake Detector</h1> |
| <p class="description">Upload a face image to check if it's real or AI-generated. Your privacy is safe with us.</p> |
| |
| <div class="upload-area" id="uploadArea"> |
| <div class="upload-icon">📸</div> |
| <div class="upload-text">Click or drag your image here</div> |
| <div class="upload-subtext">Supports JPG, PNG, WEBP</div> |
| </div> |
| |
| <input type="file" id="fileInput" accept="image/*"> |
| |
| <div class="preview-container" id="previewContainer"> |
| <img id="previewImage" src="" alt="Preview"> |
| </div> |
| |
| <button class="btn" id="detectBtn" disabled> |
| <span id="btnText">Scan Image</span> |
| </button> |
| |
| <div class="result-container" id="resultContainer"> |
| <div class="result-label">Scan Result</div> |
| <div class="result-value" id="resultValue"></div> |
| </div> |
| |
| <div class="error-message" id="errorMessage"></div> |
| |
| <div class="examples"> |
| <div> |
| <img class="example-img" src="https://via.placeholder.com/80/ffb6c1/ffffff?text=Fake1" alt="Example fake 1" onclick="loadExample('example_fake1.jpg')"> |
| <div class="example-label">Fake</div> |
| </div> |
| <div> |
| <img class="example-img" src="https://via.placeholder.com/80/ffb6c1/ffffff?text=Fake2" alt="Example fake 2" onclick="loadExample('example_fake2.jpg')"> |
| <div class="example-label">Fake</div> |
| </div> |
| <div> |
| <img class="example-img" src="https://via.placeholder.com/80/ffb6c1/ffffff?text=Fake3" alt="Example fake 3" onclick="loadExample('example_fake3.jpg')"> |
| <div class="example-label">Fake</div> |
| </div> |
| <div> |
| <img class="example-img" src="https://via.placeholder.com/80/ffb6c1/ffffff?text=Fake4" alt="Example fake 4" onclick="loadExample('example_fake4.jpg')"> |
| <div class="example-label">Fake</div> |
| </div> |
| <div> |
| <img class="example-img" src="https://via.placeholder.com/80/e1bee7/ffffff?text=Real1" alt="Example real 1" onclick="loadExample('example_real1.jpg')"> |
| <div class="example-label">Real</div> |
| </div> |
| <div> |
| <img class="example-img" src="https://via.placeholder.com/80/e1bee7/ffffff?text=Real2" alt="Example real 2" onclick="loadExample('example_real2.jpg')"> |
| <div class="example-label">Real</div> |
| </div> |
| <div> |
| <img class="example-img" src="https://via.placeholder.com/80/e1bee7/ffffff?text=Real3" alt="Example real 3" onclick="loadExample('example_real3.jpg')"> |
| <div class="example-label">Real</div> |
| </div> |
| <div> |
| <img class="example-img" src="https://via.placeholder.com/80/e1bee7/ffffff?text=Real4" alt="Example real 4" onclick="loadExample('example_real4.jpg')"> |
| <div class="example-label">Real</div> |
| </div> |
| <div> |
| <img class="example-img" src="https://via.placeholder.com/80/e1bee7/ffffff?text=Real5" alt="Example real 5" onclick="loadExample('example_real5.jpg')"> |
| <div class="example-label">Real</div> |
| </div> |
| </div> |
| |
| <footer>Made with love</footer> |
| </div> |
|
|
| <script> |
| const uploadArea = document.getElementById('uploadArea'); |
| const fileInput = document.getElementById('fileInput'); |
| const previewContainer = document.getElementById('previewContainer'); |
| const previewImage = document.getElementById('previewImage'); |
| const detectBtn = document.getElementById('detectBtn'); |
| const btnText = document.getElementById('btnText'); |
| const resultContainer = document.getElementById('resultContainer'); |
| const resultValue = document.getElementById('resultValue'); |
| const errorMessage = document.getElementById('errorMessage'); |
| |
| let selectedFile = null; |
| |
| uploadArea.addEventListener('click', () => fileInput.click()); |
| |
| uploadArea.addEventListener('dragover', (e) => { |
| e.preventDefault(); |
| uploadArea.classList.add('dragover'); |
| }); |
| |
| uploadArea.addEventListener('dragleave', () => { |
| uploadArea.classList.remove('dragover'); |
| }); |
| |
| uploadArea.addEventListener('drop', (e) => { |
| e.preventDefault(); |
| uploadArea.classList.remove('dragover'); |
| const file = e.dataTransfer.files[0]; |
| if (file && file.type.startsWith('image/')) { |
| handleFile(file); |
| } |
| }); |
| |
| fileInput.addEventListener('change', (e) => { |
| const file = e.target.files[0]; |
| if (file) { |
| handleFile(file); |
| } |
| }); |
| |
| function handleFile(file) { |
| selectedFile = file; |
| const reader = new FileReader(); |
| reader.onload = (e) => { |
| previewImage.src = e.target.result; |
| previewContainer.classList.add('show'); |
| detectBtn.disabled = false; |
| resultContainer.classList.remove('show'); |
| errorMessage.classList.remove('show'); |
| }; |
| reader.readAsDataURL(file); |
| } |
| |
| detectBtn.addEventListener('click', async () => { |
| if (!selectedFile) return; |
| |
| detectBtn.disabled = true; |
| btnText.innerHTML = '<span class="spinner"></span> Scanning...'; |
| resultContainer.classList.remove('show'); |
| errorMessage.classList.remove('show'); |
| |
| const formData = new FormData(); |
| formData.append('image', selectedFile); |
| |
| try { |
| const response = await fetch('/predict', { |
| method: 'POST', |
| body: formData |
| }); |
| |
| const data = await response.json(); |
| |
| if (response.ok) { |
| resultValue.textContent = data.prediction; |
| resultContainer.classList.add('show'); |
| } else { |
| errorMessage.textContent = data.error || 'Something went wrong. Please try again.'; |
| errorMessage.classList.add('show'); |
| } |
| } catch (error) { |
| errorMessage.textContent = 'Connection error. Please check your internet and try again.'; |
| errorMessage.classList.add('show'); |
| } finally { |
| detectBtn.disabled = false; |
| btnText.textContent = 'Scan Image'; |
| } |
| }); |
| |
| function loadExample(filename) { |
| fetch(`/examples/${filename}`) |
| .then(response => response.blob()) |
| .then(blob => { |
| const file = new File([blob], filename, { type: 'image/jpeg' }); |
| handleFile(file); |
| }) |
| .catch(() => { |
| errorMessage.textContent = 'Example image not available.'; |
| errorMessage.classList.add('show'); |
| }); |
| } |
| </script> |
| </body> |
| </html> |