Spaces:
Running
Running
| <html> | |
| <head> | |
| <base href="https://websim.ai/clock-with-ai/"> | |
| </base> | |
| <title>AI-Powered Clock with Dynamic Backgrounds</title> | |
| <style> | |
| body { | |
| background-color: #1a1a1a; | |
| display: flex; | |
| flex-direction: column; | |
| justify-content: center; | |
| align-items: center; | |
| height: 100vh; | |
| margin: 0; | |
| font-family: Arial, sans-serif; | |
| color: #00ff00; | |
| transition: background-image 1s ease-in-out; | |
| } | |
| #clock { | |
| font-weight: bold; | |
| text-shadow: 0 0 10px currentColor; | |
| z-index: 10; | |
| } | |
| #settings-btn { | |
| position: fixed; | |
| top: 10px; | |
| right: 10px; | |
| background: rgba(0, 255, 0, 0.2); | |
| border: none; | |
| color: #00ff00; | |
| font-size: 16px; | |
| cursor: pointer; | |
| z-index: 20; | |
| padding: 5px 10px; | |
| border-radius: 5px; | |
| } | |
| #settings-panel { | |
| position: fixed; | |
| top: 50px; | |
| right: 10px; | |
| background: rgba(255, 255, 255, 0.9); | |
| color: #000; | |
| padding: 20px; | |
| border-radius: 10px; | |
| display: none; | |
| z-index: 20; | |
| max-width: 300px; | |
| max-height: 80vh; | |
| overflow-y: auto; | |
| } | |
| #token-input, | |
| #prompt-input, | |
| #clock-color-input, | |
| #clock-size-input, | |
| #model-select, | |
| #generation-interval-input { | |
| width: 100%; | |
| margin-bottom: 10px; | |
| } | |
| .btn { | |
| border: none; | |
| color: white; | |
| padding: 10px 20px; | |
| text-align: center; | |
| text-decoration: none; | |
| display: inline-block; | |
| font-size: 16px; | |
| margin: 4px 2px; | |
| cursor: pointer; | |
| border-radius: 5px; | |
| transition: background-color 0.3s; | |
| } | |
| #fullscreen-btn { | |
| background-color: #4CAF50; | |
| } | |
| #fullscreen-btn:hover { | |
| background-color: #45a049; | |
| } | |
| #save-settings { | |
| background-color: #008CBA; | |
| } | |
| #save-settings:hover { | |
| background-color: #007B9A; | |
| } | |
| #test-image { | |
| background-color: #f44336; | |
| } | |
| #test-image:hover { | |
| background-color: #da190b; | |
| } | |
| #reset-prompt { | |
| background-color: #ff9800; | |
| } | |
| #reset-prompt:hover { | |
| background-color: #e68a00; | |
| } | |
| #background-image { | |
| position: fixed; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| object-fit: cover; | |
| z-index: 1; | |
| } | |
| label { | |
| display: block; | |
| margin-top: 10px; | |
| margin-bottom: 5px; | |
| } | |
| #countdown-bar { | |
| position: fixed; | |
| bottom: 0; | |
| left: 0; | |
| height: 5px; | |
| width: 100%; | |
| background-color: rgba(0, 255, 0, 0.3); | |
| z-index: 15; | |
| } | |
| #countdown-progress { | |
| height: 100%; | |
| width: 0%; | |
| background-color: rgba(0, 255, 0, 0.7); | |
| transition: width 1s linear; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <img id="background-image" alt="Please specify your HF token in the settings to generate images" src=""> | |
| <div id="clock"></div> | |
| <button id="settings-btn">Settings</button> | |
| <div id="settings-panel"> | |
| <label for="token-input">Hugging Face Token:</label> | |
| <input type="password" id="token-input" placeholder="Enter Hugging Face token"> | |
| <label for="model-select">Select Model:</label> | |
| <select id="model-select"> | |
| <option value="black-forest-labs/FLUX.1-schnell">FLUX.1-schnell</option> | |
| <option value="black-forest-labs/FLUX.1-dev">FLUX.1-dev</option> | |
| <option value="XLabs-AI/flux-RealismLora">flux-RealismLora</option> | |
| <option value="stabilityai/stable-diffusion-xl-base-1.0">Stable Diffusion XL</option> | |
| <option value="artificialguybr/PixelArtRedmond">PixelArtRedmond</option> | |
| <option value="linoyts/yarn_art_Flux_LoRA">yarn art style Flux LoRA (to activate LoRA use "yarn art style"</option> | |
| <option value="stabilityai/stable-diffusion-3.5-large">stable-diffusion-3.5-large</option> | |
| </select> | |
| <label for="prompt-input">Custom Prompt:</label> | |
| <textarea id="prompt-input" rows="4" placeholder="Enter custom prompt (use {time} for current time)"></textarea> | |
| <label for="clock-color-input">Clock Color:</label> | |
| <input type="color" id="clock-color-input"> | |
| <label for="clock-size-input">Clock Size (vw):</label> | |
| <input type="range" id="clock-size-input" min="1" max="20" step="0.1"> | |
| <span id="clock-size-value"></span> | |
| <label for="generation-interval-input">Image Generation Interval (seconds):</label> | |
| <input type="number" id="generation-interval-input" min="10" step="1"> | |
| <button id="fullscreen-btn" class="btn">Toggle Fullscreen</button> | |
| <button id="save-settings" class="btn">Save</button> | |
| <button id="test-image" class="btn">Test Image Generation</button> | |
| <button id="reset-prompt" class="btn">Reset Prompt</button> | |
| </div> | |
| <div id="countdown-bar"> | |
| <div id="countdown-progress"></div> | |
| </div> | |
| <script> | |
| let hfToken = localStorage.getItem('hfToken') || ''; | |
| let customPrompt = localStorage.getItem('customPrompt') || 'beautiful abstract landscape that suits the time {time}'; | |
| let generationFrequency = parseInt(localStorage.getItem('generationFrequency')) || 150; | |
| let clockColor = localStorage.getItem('clockColor') || '#00ff00'; | |
| let clockSize = parseFloat(localStorage.getItem('clockSize')) || 8; | |
| let selectedModel = localStorage.getItem('selectedModel') || 'black-forest-labs/FLUX.1-schnell'; | |
| let imageGenerationInterval; | |
| let countdownInterval; | |
| const DEFAULT_PROMPT = 'beautiful abstract landscape that suits the time {time}'; | |
| function updateClock() { | |
| const now = new Date(); | |
| let hours = now.getHours(); | |
| const minutes = now.getMinutes() | |
| .toString() | |
| .padStart(2, '0'); | |
| const seconds = now.getSeconds() | |
| .toString() | |
| .padStart(2, '0'); | |
| const ampm = hours >= 12 ? 'PM' : 'AM'; | |
| hours = hours % 12; | |
| hours = hours ? hours : 12; | |
| hours = hours.toString() | |
| .padStart(2, '0'); | |
| const timeString = `${hours}:${minutes}:${seconds} ${ampm}`; | |
| const clockElement = document.getElementById('clock'); | |
| clockElement.textContent = timeString; | |
| clockElement.style.color = clockColor; | |
| clockElement.style.fontSize = `${clockSize}vw`; | |
| return `${hours}:${minutes} ${ampm}`; | |
| } | |
| async function generateImage(prompt) { | |
| if (!hfToken) return; | |
| try { | |
| const response = await fetch(`https://api-inference.huggingface.co/models/${selectedModel}`, { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| 'Authorization': `Bearer ${hfToken}` | |
| }, | |
| body: JSON.stringify({ | |
| inputs: prompt | |
| }) | |
| }); | |
| if (!response.ok) { | |
| const errorData = await response.json(); | |
| throw new Error(`${response.status}: ${errorData.error || 'Unknown error'}`); | |
| } | |
| const blob = await response.blob(); | |
| const imageUrl = URL.createObjectURL(blob); | |
| const backgroundImage = document.getElementById('background-image'); | |
| const newImage = new Image(); | |
| newImage.onload = function() { | |
| URL.revokeObjectURL(backgroundImage.src); | |
| backgroundImage.src = this.src; | |
| }; | |
| newImage.src = imageUrl; | |
| } catch (error) { | |
| console.error('Error generating image:', error); | |
| speakError(`Error generating image. ${error.message}`); | |
| } | |
| } | |
| function speakError(message) { | |
| if ('speechSynthesis' in window) { | |
| const utterance = new SpeechSynthesisUtterance(message); | |
| speechSynthesis.speak(utterance); | |
| } | |
| } | |
| function updateBackgroundImage() { | |
| const currentTime = updateClock() | |
| .slice(0, -3); | |
| const prompt = customPrompt.replace('{time}', currentTime); | |
| generateImage(prompt); | |
| startCountdown(); | |
| } | |
| function startCountdown() { | |
| const progressBar = document.getElementById('countdown-progress'); | |
| let timeLeft = generationFrequency; | |
| clearInterval(countdownInterval); | |
| countdownInterval = setInterval(() => { | |
| timeLeft--; | |
| const progress = ((generationFrequency - timeLeft) / generationFrequency) * 100; | |
| progressBar.style.width = `${progress}%`; | |
| if (timeLeft <= 0) { | |
| clearInterval(countdownInterval); | |
| } | |
| }, 1000); | |
| } | |
| function initializeSettings() { | |
| document.getElementById('token-input') | |
| .value = hfToken; | |
| document.getElementById('prompt-input') | |
| .value = customPrompt; | |
| document.getElementById('clock-color-input') | |
| .value = clockColor; | |
| document.getElementById('clock-size-input') | |
| .value = clockSize; | |
| document.getElementById('clock-size-value') | |
| .textContent = clockSize + ' vw'; | |
| document.getElementById('model-select') | |
| .value = selectedModel; | |
| document.getElementById('generation-interval-input') | |
| .value = generationFrequency; | |
| } | |
| function toggleFullscreen() { | |
| if (!document.fullscreenElement) { | |
| document.documentElement.requestFullscreen() | |
| .catch(err => console.error(err)); | |
| } else { | |
| if (document.exitFullscreen) { | |
| document.exitFullscreen(); | |
| } | |
| } | |
| } | |
| function setupImageGenerationInterval() { | |
| clearInterval(imageGenerationInterval); | |
| imageGenerationInterval = setInterval(updateBackgroundImage, generationFrequency * 1000); | |
| } | |
| setInterval(updateClock, 1000); | |
| setupImageGenerationInterval(); | |
| updateClock(); | |
| updateBackgroundImage(); | |
| initializeSettings(); | |
| document.getElementById('settings-btn') | |
| .addEventListener('click', () => { | |
| const panel = document.getElementById('settings-panel'); | |
| panel.style.display = panel.style.display === 'none' ? 'block' : 'none'; | |
| }); | |
| document.getElementById('fullscreen-btn') | |
| .addEventListener('click', toggleFullscreen); | |
| document.getElementById('save-settings') | |
| .addEventListener('click', () => { | |
| hfToken = document.getElementById('token-input') | |
| .value; | |
| customPrompt = document.getElementById('prompt-input') | |
| .value; | |
| clockColor = document.getElementById('clock-color-input') | |
| .value; | |
| clockSize = parseFloat(document.getElementById('clock-size-input') | |
| .value); | |
| selectedModel = document.getElementById('model-select') | |
| .value; | |
| generationFrequency = parseInt(document.getElementById('generation-interval-input') | |
| .value); | |
| localStorage.setItem('hfToken', hfToken); | |
| localStorage.setItem('customPrompt', customPrompt); | |
| localStorage.setItem('clockColor', clockColor); | |
| localStorage.setItem('clockSize', clockSize); | |
| localStorage.setItem('selectedModel', selectedModel); | |
| localStorage.setItem('generationFrequency', generationFrequency); | |
| document.getElementById('settings-panel') | |
| .style.display = 'none'; | |
| updateBackgroundImage(); | |
| setupImageGenerationInterval(); | |
| updateClock(); | |
| }); | |
| document.getElementById('test-image') | |
| .addEventListener('click', () => { | |
| updateBackgroundImage(); | |
| }); | |
| document.getElementById('reset-prompt') | |
| .addEventListener('click', () => { | |
| customPrompt = DEFAULT_PROMPT; | |
| document.getElementById('prompt-input') | |
| .value = customPrompt; | |
| }); | |
| document.getElementById('clock-size-input') | |
| .addEventListener('input', (e) => { | |
| const value = e.target.value; | |
| document.getElementById('clock-size-value') | |
| .textContent = value + ' vw'; | |
| clockSize = parseFloat(value); | |
| updateClock(); | |
| }); | |
| document.getElementById('clock-color-input') | |
| .addEventListener('input', (e) => { | |
| clockColor = e.target.value; | |
| updateClock(); | |
| }); | |
| document.addEventListener('DOMContentLoaded', () => { | |
| const settingsBtn = document.getElementById('settings-btn'); | |
| const newSettingsBtn = settingsBtn.cloneNode(true); | |
| settingsBtn.parentNode.replaceChild(newSettingsBtn, settingsBtn); | |
| newSettingsBtn.addEventListener('click', () => { | |
| const panel = document.getElementById('settings-panel'); | |
| panel.style.display = panel.style.display === 'none' ? 'block' : 'none'; | |
| }); | |
| }); | |
| </script> | |
| </body> | |
| </html> |