Spaces:
Running
Running
Download index.html from Ezequield/anycoder-12eb250f: direct link, hf CLI and curl.
- Browser
- Download file 24.1 kB
-
https://huggingface.co/spaces/Ezequield/anycoder-12eb250f/resolve/main/index.html
- Command line
-
hf download hf://spaces/Ezequield/anycoder-12eb250f/index.html
-
curl -L -o index.html https://huggingface.co/spaces/Ezequield/anycoder-12eb250f/resolve/main/index.html
24.1 kB
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>VHS Studio | Retro Video Processor</title> | |
| <!-- Fonts: Inter for UI, VT323 for Retro Timestamp --> | |
| <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&family=VT323&display=swap" rel="stylesheet"> | |
| <!-- Icons (RemixIcon) --> | |
| <link href="https://cdn.jsdelivr.net/npm/remixicon@3.5.0/fonts/remixicon.css" rel="stylesheet"> | |
| <style> | |
| :root { | |
| --bg-body: #050505; | |
| --bg-panel: #0f0f11; | |
| --bg-input: #1a1a1d; | |
| --border-color: #333333; | |
| --primary: #ff3333; /* VHS Red */ | |
| --primary-hover: #cc0000; | |
| --text-main: #e0e0e0; | |
| --text-muted: #888888; | |
| --accent-gradient: linear-gradient(135deg, #ff3333 0%, #ff8800 100%); | |
| --glass: rgba(255, 255, 255, 0.03); | |
| --radius-sm: 4px; /* Sharper corners for retro feel */ | |
| --radius-md: 8px; | |
| --radius-lg: 12px; | |
| --transition: all 0.2s ease; | |
| /* VHS Specific Colors */ | |
| --crt-green: #33ff00; | |
| --sony-silver: #e0e0e0; | |
| } | |
| * { | |
| margin: 0; | |
| padding: 0; | |
| box-sizing: border-box; | |
| } | |
| body { | |
| font-family: 'Inter', sans-serif; | |
| background-color: var(--bg-body); | |
| color: var(--text-main); | |
| min-height: 100vh; | |
| display: flex; | |
| flex-direction: column; | |
| overflow-x: hidden; | |
| } | |
| /* --- Scrollbar --- */ | |
| ::-webkit-scrollbar { | |
| width: 8px; | |
| } | |
| ::-webkit-scrollbar-track { | |
| background: var(--bg-body); | |
| } | |
| ::-webkit-scrollbar-thumb { | |
| background: var(--border-color); | |
| border-radius: 2px; | |
| } | |
| ::-webkit-scrollbar-thumb:hover { | |
| background: #555; | |
| } | |
| /* --- Header --- */ | |
| header { | |
| height: 70px; | |
| border-bottom: 1px solid var(--border-color); | |
| background: rgba(5, 5, 5, 0.9); | |
| backdrop-filter: blur(8px); | |
| display: flex; | |
| align-items: center; | |
| justify-content: space-between; | |
| padding: 0 24px; | |
| position: fixed; | |
| top: 0; | |
| width: 100%; | |
| z-index: 100; | |
| box-shadow: 0 4px 20px rgba(0,0,0,0.5); | |
| } | |
| .logo { | |
| display: flex; | |
| align-items: center; | |
| gap: 8px; | |
| font-weight: 700; | |
| font-size: 1.1rem; | |
| letter-spacing: 0.1em; | |
| text-transform: uppercase; | |
| font-style: italic; | |
| } | |
| .logo i { | |
| font-size: 1.4rem; | |
| color: var(--primary); | |
| } | |
| .logo span { | |
| color: var(--sony-silver); | |
| text-shadow: 0 0 5px rgba(255, 255, 255, 0.3); | |
| } | |
| .header-actions { | |
| display: flex; | |
| align-items: center; | |
| gap: 20px; | |
| } | |
| .nav-link { | |
| color: var(--text-muted); | |
| text-decoration: none; | |
| font-size: 0.85rem; | |
| font-weight: 500; | |
| transition: var(--transition); | |
| text-transform: uppercase; | |
| } | |
| .nav-link:hover { | |
| color: var(--text-main); | |
| } | |
| .anycoder-link { | |
| font-size: 0.75rem; | |
| color: var(--text-main); | |
| background: #222; | |
| padding: 6px 14px; | |
| border-radius: 2px; | |
| text-decoration: none; | |
| border: 1px solid #444; | |
| transition: var(--transition); | |
| font-family: 'VT323', monospace; | |
| letter-spacing: 1px; | |
| } | |
| .anycoder-link:hover { | |
| background: #333; | |
| border-color: var(--primary); | |
| color: var(--primary); | |
| } | |
| /* --- Main Layout --- */ | |
| main { | |
| margin-top: 70px; | |
| flex: 1; | |
| display: grid; | |
| grid-template-columns: 320px 1fr; | |
| height: calc(100vh - 70px); | |
| overflow: hidden; | |
| } | |
| /* --- Sidebar (Controls) --- */ | |
| aside { | |
| background: var(--bg-panel); | |
| border-right: 1px solid var(--border-color); | |
| padding: 20px; | |
| overflow-y: auto; | |
| display: flex; | |
| flex-direction: column; | |
| gap: 20px; | |
| } | |
| .control-group { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 10px; | |
| } | |
| .label-row { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| } | |
| label { | |
| font-size: 0.75rem; | |
| font-weight: 600; | |
| color: var(--text-muted); | |
| text-transform: uppercase; | |
| letter-spacing: 0.1em; | |
| } | |
| .value-display { | |
| font-size: 0.8rem; | |
| color: var(--primary); | |
| font-family: 'VT323', monospace; | |
| } | |
| /* Inputs */ | |
| textarea { | |
| background: var(--bg-input); | |
| border: 1px solid var(--border-color); | |
| border-radius: var(--radius-sm); | |
| padding: 10px; | |
| color: var(--text-main); | |
| font-family: inherit; | |
| resize: none; | |
| height: 80px; | |
| font-size: 0.85rem; | |
| transition: var(--transition); | |
| width: 100%; | |
| } | |
| textarea:focus { | |
| outline: none; | |
| border-color: var(--primary); | |
| box-shadow: 0 0 0 1px var(--primary); | |
| } | |
| /* Range Slider */ | |
| input[type="range"] { | |
| -webkit-appearance: none; | |
| width: 100%; | |
| height: 4px; | |
| background: var(--bg-input); | |
| border-radius: 2px; | |
| outline: none; | |
| } | |
| input[type="range"]::-webkit-slider-thumb { | |
| -webkit-appearance: none; | |
| width: 16px; | |
| height: 16px; | |
| border-radius: 0; /* Square thumb for retro */ | |
| background: var(--text-main); | |
| cursor: pointer; | |
| border: 1px solid var(--border-color); | |
| transition: transform 0.1s; | |
| } | |
| input[type="range"]::-webkit-slider-thumb:hover { | |
| transform: scale(1.2); | |
| background: var(--primary); | |
| } | |
| /* Upload Zone */ | |
| .upload-zone { | |
| border: 2px dashed var(--border-color); | |
| border-radius: var(--radius-sm); | |
| padding: 40px 20px; | |
| text-align: center; | |
| cursor: pointer; | |
| transition: var(--transition); | |
| background: repeating-linear-gradient( | |
| 45deg, | |
| var(--bg-input), | |
| var(--bg-input) 10px, | |
| #151515 10px, | |
| #151515 20px | |
| ); | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .upload-zone:hover, | |
| .upload-zone.dragover { | |
| border-color: var(--primary); | |
| background-color: rgba(255, 51, 51, 0.05); | |
| } | |
| .upload-icon { | |
| font-size: 2.5rem; | |
| color: var(--text-muted); | |
| margin-bottom: 12px; | |
| } | |
| .upload-text { | |
| font-size: 0.9rem; | |
| color: var(--text-muted); | |
| font-family: 'VT323', monospace; | |
| text-transform: uppercase; | |
| letter-spacing: 1px; | |
| } | |
| .upload-preview { | |
| display: none; /* Video preview handled in workspace */ | |
| } | |
| /* Generate Button */ | |
| .generate-btn { | |
| background: var(--accent-gradient); | |
| color: white; | |
| border: none; | |
| padding: 14px; | |
| border-radius: var(--radius-sm); | |
| font-weight: 700; | |
| font-size: 0.9rem; | |
| cursor: pointer; | |
| transition: var(--transition); | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| gap: 8px; | |
| margin-top: auto; | |
| text-transform: uppercase; | |
| letter-spacing: 1px; | |
| box-shadow: 0 4px 15px rgba(255, 51, 51, 0.2); | |
| } | |
| .generate-btn:hover { | |
| opacity: 1; | |
| transform: translateY(-1px); | |
| box-shadow: 0 6px 20px rgba(255, 51, 51, 0.4); | |
| } | |
| .generate-btn:disabled { | |
| opacity: 0.5; | |
| cursor: not-allowed; | |
| transform: none; | |
| filter: grayscale(1); | |
| } | |
| /* --- Workspace (Right - CRT Monitor) --- */ | |
| .workspace { | |
| padding: 40px; | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| justify-content: center; | |
| background-color: #080808; | |
| background-image: | |
| radial-gradient(circle at 50% 50%, #111 0%, #000 100%); | |
| position: relative; | |
| } | |
| /* The TV Frame */ | |
| .tv-frame { | |
| width: 100%; | |
| max-width: 900px; | |
| aspect-ratio: 4/3; /* Classic TV Aspect Ratio */ | |
| background: #111; | |
| border-radius: 20px; | |
| padding: 20px; | |
| box-shadow: | |
| 0 0 0 2px #333, | |
| 0 0 0 6px #000, | |
| 0 20px 50px rgba(0, 0, 0, 0.8); | |
| position: relative; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| } | |
| .screen-container { | |
| width: 100%; | |
| height: 100%; | |
| background: #000; | |
| border-radius: 60px / 20px; /* Slight curve */ | |
| overflow: hidden; | |
| position: relative; | |
| box-shadow: inset 0 0 20px rgba(0,0,0,0.8); | |
| } | |
| /* Video Element */ | |
| video { | |
| width: 100%; | |
| height: 100%; | |
| object-fit: cover; | |
| display: none; | |
| filter: contrast(1.1) saturate(1.2) brightness(0.9); | |
| } | |
| /* VHS Overlay Effects */ | |
| .vhs-overlay { | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| pointer-events: none; | |
| z-index: 10; | |
| display: none; /* Hidden until video plays */ | |
| } | |
| /* Scanlines */ | |
| .scanlines { | |
| background: linear-gradient( | |
| to bottom, | |
| rgba(255,255,255,0), | |
| rgba(255,255,255,0) 50%, | |
| rgba(0,0,0,0.2) 50%, | |
| rgba(0,0,0,0.2) | |
| ); | |
| background-size: 100% 4px; | |
| width: 100%; | |
| height: 100%; | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| z-index: 11; | |
| } | |
| /* Noise / Static Animation */ | |
| .noise { | |
| position: absolute; | |
| top: -50%; | |
| left: -50%; | |
| width: 200%; | |
| height: 200%; | |
| background: transparent url('data:image/svg+xml;utf8,%3Csvg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg"%3E%3Cfilter id="noiseFilter"%3E%3CfeTurbulence type="fractalNoise" baseFrequency="0.85" numOctaves="3" stitchTiles="stitch"/%3E%3C/filter%3E%3Crect width="100%25" height="100%25" filter="url(%23noiseFilter)" opacity="0.15"/%3E%3C/svg%3E'); | |
| opacity: 0.4; | |
| animation: noise 0.2s steps(4) infinite; | |
| z-index: 12; | |
| } | |
| @keyframes noise { | |
| 0%, 100% { transform: translate(0, 0); } | |
| 10% { transform: translate(-5%, -5%); } | |
| 20% { transform: translate(-10%, 5%); } | |
| 30% { transform: translate(5%, -10%); } | |
| 40% { transform: translate(-5%, 15%); } | |
| 50% { transform: translate(-10%, 5%); } | |
| 60% { transform: translate(15%, 0); } | |
| 70% { transform: translate(0, 10%); } | |
| 80% { transform: translate(-15%, 0); } | |
| 90% { transform: translate(10%, 5%); } | |
| } | |
| /* Chromatic Aberration / Color Bleed */ | |
| .vhs-effect-active video { | |
| filter: contrast(1.2) saturate(1.3) blur(0.5px); | |
| text-shadow: 2px 0 rgba(255,0,0,0.5), -2px 0 rgba(0,0,255,0.5); | |
| } | |
| /* REC Indicator */ | |
| .rec-indicator { | |
| position: absolute; | |
| top: 30px; | |
| right: 40px; | |
| display: flex; | |
| align-items: center; | |
| gap: 8px; | |
| color: var(--primary); | |
| font-weight: bold; | |
| font-family: 'VT323', monospace; | |
| font-size: 1.5rem; | |
| z-index: 20; | |
| display: none; | |
| } | |
| .rec-dot { | |
| width: 12px; | |
| height: 12px; | |
| background: var(--primary); | |
| border-radius: 50%; | |
| animation: blink 1s infinite; | |
| } | |
| @keyframes blink { | |
| 0%, 100% { opacity: 1; } | |
| 50% { opacity: 0; } | |
| } | |
| /* Timestamp */ | |
| .timestamp { | |
| position: absolute; | |
| bottom: 30px; | |
| right: 40px; | |
| color: white; | |
| font-family: 'VT323', monospace; | |
| font-size: 1.2rem; | |
| z-index: 20; | |
| text-shadow: 1px 1px 0 #000; | |
| display: none; | |
| } | |
| /* Empty State */ | |
| .empty-state { | |
| color: #444; | |
| text-align: center; | |
| } | |
| .empty-state i { | |
| font-size: 4rem; | |
| margin-bottom: 1rem; | |
| color: #222; | |
| } | |
| /* Loading State */ | |
| .loading-overlay { | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| background: #000; | |
| display: none; | |
| flex-direction: column; | |
| align-items: center; | |
| justify-content: center; | |
| z-index: 30; | |
| color: var(--primary); | |
| font-family: 'VT323', monospace; | |
| font-size: 1.5rem; | |
| letter-spacing: 2px; | |
| } | |
| .loading-bar { | |
| width: 60%; | |
| height: 4px; | |
| background: #333; | |
| margin-top: 20px; | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .loading-progress { | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| height: 100%; | |
| width: 50%; | |
| background: var(--primary); | |
| animation: loading 1s linear infinite; | |
| } | |
| @keyframes loading { | |
| 0% { left: -50%; } | |
| 100% { left: 100%; } | |
| } | |
| /* Toast */ | |
| .toast { | |
| position: fixed; | |
| bottom: 30px; | |
| right: 30px; | |
| background: var(--bg-panel); | |
| border: 1px solid var(--primary); | |
| color: white; | |
| padding: 12px 20px; | |
| font-family: 'VT323', monospace; | |
| font-size: 1.1rem; | |
| display: flex; | |
| align-items: center; | |
| gap: 10px; | |
| transform: translateY(100px); | |
| opacity: 0; | |
| transition: all 0.4s ease; | |
| z-index: 200; | |
| box-shadow: 0 0 15px rgba(255, 51, 51, 0.2); | |
| } | |
| .toast.show { | |
| transform: translateY(0); | |
| opacity: 1; | |
| } | |
| /* Responsive */ | |
| @media (max-width: 900px) { | |
| main { | |
| grid-template-columns: 1fr; | |
| height: auto; | |
| overflow-y: auto; | |
| } | |
| aside { | |
| border-right: none; | |
| border-bottom: 1px solid var(--border-color); | |
| } | |
| .tv-frame { | |
| max-width: 100%; | |
| aspect-ratio: 16/9; | |
| padding: 10px; | |
| border-radius: 10px; | |
| } | |
| .screen-container { | |
| border-radius: 4px; | |
| } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <!-- Header --> | |
| <header> | |
| <div class="logo"> | |
| <i class="ri-movie-2-line"></i> | |
| <span>VHS Studio</span> | |
| </div> | |
| <div class="header-actions"> | |
| <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="anycoder-link"> | |
| Built with anycoder | |
| </a> | |
| <div class="user-avatar" style="width: 32px; height: 32px; background: #333; border-radius: 50%; display: grid; place-items: center; color: #888;"> | |
| <i class="ri-user-smile-line"></i> | |
| </div> | |
| </div> | |
| </header> | |
| <!-- Main Content --> | |
| <main> | |
| <!-- Sidebar Controls --> | |
| <aside> | |
| <div class="control-group"> | |
| <div class="label-row"> | |
| <label>Source Tape</label> | |
| <span class="value-display" id="file-name">NO TAPE</span> | |
| </div> | |
| <!-- Upload Input --> | |
| <input type="file" id="video-upload" accept="video/*" style="display: none;"> | |
| <div class="upload-zone" id="drop-zone"> | |
| <i class="ri-vip-crown-2-line upload-icon"></i> | |
| <div class="upload-text">Insert Video Tape<br>(Drag & Drop)</div> | |
| </div> | |
| </div> | |
| <div class="control-group"> | |
| <div class="label-row"> | |
| <label>Tape Label</label> | |
| </div> | |
| <textarea id="prompt-input" placeholder="Enter tape description or date..."></textarea> | |
| </div> | |
| <div class="control-group"> | |
| <div class="label-row"> | |
| <label>Tracking</label> | |
| <span class="value-display" id="tracking-val">0%</span> | |
| </div> | |
| <input type="range" id="tracking" min="0" max="100" value="0"> | |
| </div> | |
| <div class="control-group"> | |
| <div class="label-row"> | |
| <label>Color Bleed</label> | |
| <span class="value-display" id="bleed-val">50%</span> | |
| </div> | |
| <input type="range" id="bleed" min="0" max="100" value="50"> | |
| </div> | |
| <div class="control-group"> | |
| <div class="label-row"> | |
| <label>Static Noise</label> | |
| <span class="value-display" id="noise-val">30%</span> | |
| </div> | |
| <input type="range" id="noise" min="0" max="100" value="30"> | |
| </div> | |
| <button class="generate-btn" id="process-btn" disabled> | |
| <i class="ri-play-circle-line"></i> Play Tape | |
| </button> | |
| </aside> | |
| <!-- Workspace / CRT Screen --> | |
| <div class="workspace"> | |
| <div class="tv-frame"> | |
| <div class="screen-container" id="screen"> | |
| <!-- Empty State --> | |
| <div class="empty-state" id="empty-state"> | |
| <i class="ri-tv-line"></i> | |
| <p>NO SIGNAL</p> | |
| <p style="font-size: 0.8rem; margin-top: 5px; color: #333;">INSERT TAPE TO BEGIN</p> | |
| </div> | |
| <!-- Video Player --> | |
| <video id="main-video" loop playsinline></video> | |
| <!-- VHS Overlays --> | |
| <div class="vhs-overlay" id="vhs-layer"> | |
| <div class="scanlines"></div> | |
| <div class="noise" id="noise-layer"></div> | |
| </div> | |
| <!-- UI Overlays --> | |
| <div class="rec-indicator" id="rec-indicator"> | |
| <div class="rec-dot"></div> REC | |
| </div> | |
| <div class="timestamp" id="timestamp"> | |
| 1994 12 24 | |
| </div> | |
| <!-- Loading --> | |
| <div class="loading-overlay" id="loading"> | |
| <div>REWINDING TAPE...</div> | |
| <div class="loading-bar"><div class="loading-progress"></div></div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- TV Branding Badge --> | |
| <div style="margin-top: 15px; font-family: 'Inter', sans-serif; font-weight: 700; color: #444; letter-spacing: 2px; font-size: 0.8rem;"> | |
| TRINITRON <span style="font-weight: 300; color: #333;">COLOR VIDEO MONITOR</span> | |
| </div> | |
| </div> | |
| </main> | |
| <!-- Toast Notification --> | |
| <div class="toast" id="toast"> | |
| <i class="ri-checkbox-circle-line"></i> | |
| <span id="toast-msg">Tape Loaded Successfully</span> | |
| </div> | |
| <script> | |
| // DOM Elements | |
| const dropZone = document.getElementById('drop-zone'); | |
| const fileInput = document.getElementById('video-upload'); | |
| const fileNameDisplay = document.getElementById('file-name'); | |
| const processBtn = document.getElementById('process-btn'); | |
| const mainVideo = document.getElementById('main-video'); | |
| const emptyState = document.getElementById('empty-state'); | |
| const vhsLayer = document.getElementById('vhs-layer'); | |
| const noiseLayer = document.getElementById('noise-layer'); | |
| const loadingOverlay = document.getElementById('loading'); | |
| const recIndicator = document.getElementById('rec-indicator'); | |
| const timestampDisplay = document.getElementById('timestamp'); | |
| const screen = document.getElementById('screen'); | |
| const toast = document.getElementById('toast'); | |
| const toastMsg = document.getElementById('toast-msg'); | |
| // Sliders | |
| const trackingSlider = document.getElementById('tracking'); | |
| const bleedSlider = document.getElementById('bleed'); | |
| const noiseSlider = document.getElementById('noise'); | |
| const trackingVal = document.getElementById('tracking-val'); | |
| const bleedVal = document.getElementById('bleed-val'); | |
| const noiseVal = document.getElementById('noise-val'); | |
| let isPlaying = false; | |
| let videoLoaded = false; | |
| // --- File Handling --- | |
| // Click to upload | |
| dropZone.addEventListener('click', () => fileInput.click()); | |
| // Drag & Drop | |
| dropZone.addEventListener('dragover', (e) => { | |
| e.preventDefault(); | |
| dropZone.classList.add('dragover'); | |
| }); | |
| dropZone.addEventListener('dragleave', () => { | |
| dropZone.classList.remove('dragover'); | |
| }); | |
| dropZone.addEventListener('drop', (e) => { | |
| e.preventDefault(); | |
| dropZone.classList.remove('dragover'); | |
| if (e.dataTransfer.files.length) { | |
| handleFile(e.dataTransfer.files[0]); | |
| } | |
| }); | |
| fileInput.addEventListener('change', (e) => { | |
| if (e.target.files.length) { | |
| handleFile(e.target.files[0]); | |
| } | |
| }); | |
| function handleFile(file) { | |
| if (!file.type.startsWith('video/')) { | |
| showToast('Error: Please insert a video tape.', true); | |
| return; | |
| } | |
| const url = URL.createObjectURL(file); | |
| mainVideo.src = url; | |
| fileNameDisplay.textContent = file.name.substring(0, 15) + '...'; | |
| // Reset UI state | |
| mainVideo.style.display = 'none'; | |
| vhsLayer.style.display = 'none'; | |
| recIndicator.style.display = 'none'; | |
| timestampDisplay.style.display = 'none'; | |
| emptyState.style.display = 'block'; | |
| processBtn.innerHTML = '<i class="ri-play-circle-line"></i> Play Tape'; | |
| processBtn.disabled = false; | |
| videoLoaded = true; | |
| showToast('Tape Loaded'); | |
| } | |
| // --- Playback Logic --- | |
| processBtn.addEventListener('click', () => { | |
| if (!videoLoaded) return; | |
| if (isPlaying) { | |
| // Stop | |
| mainVideo.pause(); | |
| isPlaying = false; | |
| processBtn.innerHTML = '<i class="ri-play-circle-line"></i> Play Tape'; | |
| vhsLayer.style.display = 'none'; | |
| recIndicator.style.display = 'none'; | |
| timestampDisplay.style.display = 'none'; | |
| } else { | |
| // Play | |
| isPlaying = true; | |
| processBtn.innerHTML = '<i class="ri-stop-circle-line"></i> Stop'; | |
| // Show loading simulation | |
| loadingOverlay.style.display = 'flex'; | |
| setTimeout(() => { | |
| loadingOverlay.style.display = 'none'; | |
| emptyState.style.display = 'none'; | |
| mainVideo.style.display = 'block'; | |
| vhsLayer.style.display = 'block'; | |
| recIndicator.style.display = 'flex'; | |
| timestampDisplay.style.display = 'block'; | |
| mainVideo.play(); | |
| updateTimestamp(); | |
| }, 1500); // 1.5s fake rewind | |
| } | |
| }); | |
| mainVideo.addEventListener('ended', () => { | |
| isPlaying = false; | |
| processBtn.innerHTML = '<i class="ri-play-circle-line"></i> Play Tape'; | |
| vhsLayer.style.display = 'none'; | |
| recIndicator.style.display = 'none'; | |
| }); | |
| // --- Effects Logic --- | |
| function updateEffects() { | |
| const tracking = trackingSlider.value; | |
| const bleed = bleedSlider.value; | |
| const noise = noiseSlider.value; | |
| // Update text displays | |
| trackingVal.textContent = tracking + '%'; | |
| bleedVal.textContent = bleed + '%'; | |
| noiseVal.textContent = noise + '%'; | |
| // Apply CSS Filters for VHS look | |
| // Tracking: Adds vertical shift jitter simulation (via translation) | |
| // Bleed: Adds blur and color separation | |
| // Noise: Adjusts opacity of noise layer | |
| const blurAmount = (bleed / 100) * 2; // Max 2px blur | |
| const contrastAmount = 1.1 + (tracking / 500); // Slight contrast bump | |
| mainVideo.style.filter = `contrast(${contrastAmount}) saturate(1.2) blur(${blurAmount}px)`; | |
| noiseLayer.style.opacity = (noise / 100) * 0.4; // Max 0.4 opacity | |
| // Simulate tracking lines (moving background position) | |
| if (tracking > 50) { | |
| screen.style.transform = `translateY(${Math.random() * 2 - 1}px)`; | |
| } else { | |
| screen.style.transform = `translateY(0)`; | |
| } | |
| } | |
| // Attach listeners | |
| trackingSlider.addEventListener('input', updateEffects); | |
| bleedSlider.addEventListener('input', updateEffects); | |
| noiseSlider.addEventListener('input', updateEffects); | |
| // Initial call | |
| updateEffects(); | |
| // --- Utilities --- | |
| function showToast(message, isError = false) { | |
| toastMsg.textContent = message; | |
| toast.style.borderColor = isError ? 'red' : 'var(--primary)'; | |
| toast.querySelector('i').className = isError ? 'ri-error-warning-line' : 'ri-checkbox-circle-line'; | |
| toast.classList.add('show'); | |
| setTimeout(() => { | |
| toast.classList.remove('show'); | |
| }, 3000); | |
| } | |
| function updateTimestamp() { | |
| if (!isPlaying) return; | |
| const now = new Date(); | |
| const dateStr = now.getFullYear() + ' ' + | |
| String(now.getMonth() + 1).padStart(2, '0') + ' ' + | |
| String(now.getDate()).padStart(2, '0'); | |
| const timeStr = String(now.getHours()).padStart(2, '0') + ':' + | |
| String(now.getMinutes()).padStart(2, '0') + ':' + | |
| String(now.getSeconds()).padStart(2, '0'); | |
| timestampDisplay.textContent = `${dateStr} ${timeStr}`; | |
| requestAnimationFrame(updateTimestamp); | |
| } | |
| </script> | |
| </body> | |
| </html> |