ALSv/self-forcing
0
1<!DOCTYPE html>2<html lang="en">3<head>4 <meta charset="UTF-8">5 <meta name="viewport" content="width=device-width, initial-scale=1.0">6 <title>Self Forcing</title>7 <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.0/socket.io.js"></script>8 <style>9 body {10 font-family: Arial, sans-serif;11 max-width: 1400px;12 margin: 0 auto;13 padding: 20px;14 background-color: #f5f5f5;15 }16 .container {17 background: white;18 padding: 20px;19 border-radius: 10px;20 box-shadow: 0 2px 10px rgba(0,0,0,0.1);21 }22 .main-layout {23 display: grid;24 grid-template-columns: 1fr 1fr;25 gap: 30px;26 margin-top: 20px;27 }28 .left-column {29 padding-right: 15px;30 }31 .right-column {32 padding-left: 15px;33 }34 @media (max-width: 768px) {35 .main-layout {36 grid-template-columns: 1fr;37 gap: 20px;38 }39 .left-column, .right-column {40 padding: 0;41 }42 }43 .controls {44 margin-bottom: 20px;45 }46 .control-group {47 margin-bottom: 15px;48 }49 label {50 display: block;51 margin-bottom: 5px;52 font-weight: bold;53 }54 input, textarea, button, select {55 padding: 8px;56 border: 1px solid #ddd;57 border-radius: 4px;58 }59 textarea {60 width: 100%;61 height: 90px;62 resize: vertical;63 }64 input[type="range"] {65 width: 200px;66 }67 button {68 background-color: #007bff;69 color: white;70 border: none;71 padding: 10px 20px;72 cursor: pointer;73 margin-right: 10px;74 }75 button:hover {76 background-color: #0056b3;77 }78 button:disabled {79 background-color: #6c757d;80 cursor: not-allowed;81 }82 .stop-btn {83 background-color: #dc3545;84 }85 .stop-btn:hover {86 background-color: #c82333;87 }88 .video-container {89 text-align: center;90 background: #000;91 border-radius: 8px;92 padding: 20px;93 margin: 20px auto;94 display: flex;95 flex-direction: column;96 align-items: center;97 justify-content: center;98 }99 #videoFrame {100 max-width: 100%;101 height: auto;102 border-radius: 4px;103 }104 .progress-container {105 margin: 20px 0;106 }107 .progress-bar {108 width: 100%;109 height: 20px;110 background-color: #e9ecef;111 border-radius: 10px;112 overflow: hidden;113 }114 .progress-fill {115 height: 100%;116 background-color: #007bff;117 transition: width 0.3s ease;118 }119 .status {120 margin: 10px 0;121 padding: 10px;122 border-radius: 4px;123 }124 .status.info {125 background-color: #d1ecf1;126 color: #0c5460;127 }128 .status.error {129 background-color: #f8d7da;130 color: #721c24;131 }132 .status.success {133 background-color: #d4edda;134 color: #155724;135 }136 .frame-info {137 color: #666;138 font-size: 0.9em;139 margin-top: 10px;140 }141 .buffer-info {142 background-color: #e3f2fd;143 padding: 15px;144 border-radius: 4px;145 margin: 15px 0;146 color: #1976d2;147 }148 .playback-controls {149 margin: 15px 0;150 display: flex;151 align-items: center;152 justify-content: center;153 gap: 10px;154 }155 .playback-controls button {156 margin: 0 5px;157 padding: 8px 15px;158 }159 #playbackSpeed {160 width: 80px;161 }162 .torch-compile-toggle {163 background-color: #f8f9fa;164 border: 1px solid #dee2e6;165 border-radius: 6px;166 padding: 10px;167 margin: 0;168 flex: 1;169 min-width: 120px;170 }171 .torch-compile-toggle label {172 display: flex;173 align-items: center;174 font-weight: bold;175 color: #495057;176 margin-bottom: 0;177 font-size: 0.9em;178 }179 .torch-compile-toggle input[type="checkbox"] {180 transform: scale(1.1);181 margin-right: 8px;182 }183 </style>184</head>185<body>186 <div class="container">187 <h1>🚀 Self Forcing</h1>188 189 <div class="main-layout">190 <div class="left-column">191 <div class="controls">192 <div class="control-group">193 <label for="prompt">Prompt (long, detailed prompts work better):</label>194 <textarea id="prompt" placeholder="Describe the video you want to generate..."></textarea>195 196 <div style="margin-top: 10px;">197 <label>Quick Prompts:</label>198 <div style="display: flex; flex-direction: column; gap: 8px; margin-top: 5px;">199 <button type="button" onclick="setQuickPrompt('quick-demo-1')" style="background-color: #28a745; font-size: 11px; padding: 8px; width: 100%; text-align: left; white-space: pre-wrap; line-height: 1.3; min-height: 60px; border-radius: 4px; color: white; border: none; cursor: pointer;">A stylish woman walks down a Tokyo street filled with warm glowing neon and animated city signage. She wears a black leather jacket, a long red dress, and black boots, and carries a black purse. She wears sunglasses and red lipstick. She walks confidently and casually. The street is damp and reflective, creating a mirror effect of the colorful lights. Many pedestrians walk about.</button>200 <button type="button" onclick="setQuickPrompt('quick-demo-2')" style="background-color: #17a2b8; font-size: 11px; padding: 8px; width: 100%; text-align: left; white-space: pre-wrap; line-height: 1.3; min-height: 60px; border-radius: 4px; color: white; border: none; cursor: pointer;">A white and orange tabby cat is seen happily darting through a dense garden, as if chasing something. Its eyes are wide and happy as it jogs forward, scanning the branches, flowers, and leaves as it walks. The path is narrow as it makes its way between all the plants. the scene is captured from a ground-level angle, following the cat closely, giving a low and intimate perspective. The image is cinematic with warm tones and a grainy texture. The scattered daylight between the leaves and plants above creates a warm contrast, accentuating the cat’s orange fur. The shot is clear and sharp, with a shallow depth of field.</button>201 </div>202 </div>203 </div>204 205 <div style="display: flex; gap: 20px;">206 <div class="control-group">207 <label for="seed">Seed:</label>208 <input type="number" id="seed" value="-1" min="0" max="999999">209 </div>210 211 <div class="control-group">212 <label for="fps">Target FPS: <span id="fpsValue">6</span></label>213 <input type="range" id="fps" min="2" max="16" value="6" step="0.5">214 </div>215 216 <!-- <div class="control-group">217 <label for="blocks">Total Blocks: <span id="blocksValue">7</span></label>218 <input type="range" id="blocks" min="3" max="10" value="7" step="1">219 </div> -->220 </div>221 222 <div class="control-group">223 <div style="display: flex; gap: 15px; align-items: flex-start; flex-wrap: wrap;">224 <div class="torch-compile-toggle">225 <label>226 <input type="checkbox" id="torchCompile">227 🔥 torch.compile228 </label>229 </div>230 <div class="torch-compile-toggle">231 <label>232 <input type="checkbox" id="fp8Toggle">233 ⚡ FP8 Quantization234 </label>235 </div>236 <div class="torch-compile-toggle">237 <label>238 <input type="checkbox" id="taehvToggle">239 ⚡ TAEHV VAE240 </label>241 </div>242 </div>243 <!-- <div style="font-size: 0.85em; color: #666; margin-top: 5px;">244 <strong>Note:</strong> torch.compile and FP8 are one-time toggles (cannot be changed once applied)245 </div> -->246 </div>247 248 <div class="control-group">249 <button id="startBtn" onclick="startGeneration()">🚀 Start Generation</button>250 <button id="stopBtn" onclick="stopGeneration()" disabled class="stop-btn">⏹️ Stop</button>251 </div>252 </div>253 254 <div class="progress-container">255 <div class="progress-bar">256 <div id="progressFill" class="progress-fill" style="width: 0%"></div>257 </div>258 <div id="progressText">Ready to generate</div>259 </div>260 </div>261 262 <div class="right-column">263 <div class="buffer-info">264 <strong>📦 Frame Buffer:</strong> <span id="bufferCount">0</span> frames ready |265 <strong>📺 Displayed:</strong> <span id="displayedCount">0</span> frames266 <!-- <strong>⚡ Receive Rate:</strong> <span id="receiveRate">0</span> fps -->267 </div>268 269 <div class="playback-controls">270 <button id="playBtn" onclick="togglePlayback()" disabled>▶️ Play</button>271 <button id="resetBtn" onclick="resetPlayback()" disabled>⏮️ Reset</button>272 <label for="playbackSpeed">Speed:</label>273 <select id="playbackSpeed" onchange="updatePlaybackSpeed()">274 <option value="0.25">0.25x</option>275 <option value="0.5">0.5x</option>276 <option value="0.75">0.75x</option>277 <option value="1" selected>1x</option>278 <option value="1.25">1.25x</option>279 <option value="1.5">1.5x</option>280 <option value="2">2x</option>281 </select>282 </div>283 284 <div id="statusContainer"></div>285 286 <div class="video-container">287 <img id="videoFrame" src="" alt="Video frames will appear here" style="display: none;">288 <div id="placeholderText">Click "Start Generation" to begin</div>289 <div id="frameInfo" class="frame-info"></div>290 </div>291 </div>292 </div>293 </div>294 295 <script>296 const socket = io();297 let frameBuffer = []; // Store all received frames298 let currentFrameIndex = 0;299 let isPlaying = false;300 let playbackInterval = null;301 let targetFps = 6;302 let playbackSpeed = 1.0;303 let startTime = null;304 let lastReceiveTime = null;305 let receiveCount = 0;306 let receiveRate = 0;307 308 // State tracking for one-time toggles309 let torchCompileApplied = false;310 let fp8Applied = false;311 312 // Update slider values313 document.getElementById('fps').oninput = function() {314 targetFps = parseFloat(this.value);315 document.getElementById('fpsValue').textContent = this.value;316 updatePlaybackTiming();317 };318 319 // document.getElementById('blocks').oninput = function() {320 // document.getElementById('blocksValue').textContent = this.value;321 // };322 323 // Handle toggle behavior and fetch current status324 function updateToggleStates() {325 fetch('/api/status')326 .then(response => response.json())327 .then(data => {328 torchCompileApplied = data.torch_compile_applied;329 fp8Applied = data.fp8_applied;330 331 // Update UI based on current state332 const torchToggle = document.getElementById('torchCompile');333 const fp8Toggle = document.getElementById('fp8Toggle');334 const taehvToggle = document.getElementById('taehvToggle');335 336 // Disable one-time toggles if already applied337 if (torchCompileApplied) {338 torchToggle.checked = true;339 torchToggle.disabled = true;340 torchToggle.parentElement.style.opacity = '0.6';341 }342 343 if (fp8Applied) {344 fp8Toggle.checked = true;345 fp8Toggle.disabled = true;346 fp8Toggle.parentElement.style.opacity = '0.6';347 }348 349 // Set TAEHV toggle based on current state350 taehvToggle.checked = data.current_use_taehv;351 })352 .catch(err => console.log('Status check failed:', err));353 }354 355 // Handle torch.compile toggle356 document.getElementById('torchCompile').onchange = function() {357 if (torchCompileApplied && !this.checked) {358 this.checked = true; // Prevent unchecking359 alert('torch.compile cannot be disabled once applied');360 }361 };362 363 // Handle FP8 toggle364 document.getElementById('fp8Toggle').onchange = function() {365 if (fp8Applied && !this.checked) {366 this.checked = true; // Prevent unchecking367 alert('FP8 quantization cannot be disabled once applied');368 }369 };370 371 // Update toggle states on page load372 updateToggleStates();373 374 // Socket event handlers375 socket.on('connect', function() {376 // showStatus('Connected to frontend-buffered server', 'info');377 });378 379 socket.on('status', function(data) {380 // showStatus(data.message, 'info');381 });382 383 socket.on('progress', function(data) {384 updateProgress(data.progress, data.message);385 });386 387 socket.on('frame_ready', function(data) {388 // Add frame to buffer immediately389 frameBuffer.push(data);390 receiveCount++;391 392 // Calculate receive rate393 const now = Date.now();394 if (lastReceiveTime) {395 const interval = (now - lastReceiveTime) / 1000;396 receiveRate = (1 / interval).toFixed(1);397 }398 lastReceiveTime = now;399 400 updateBufferInfo();401 402 // Auto-start playback when we have some frames403 if (frameBuffer.length === 5 && !isPlaying) {404 // showStatus('Auto-starting playback with buffer of 5 frames', 'info');405 startPlayback();406 }407 });408 409 socket.on('generation_complete', function(data) {410 // showStatus(data.message + ` (Generated in ${data.generation_time})`, 'success');411 enableControls(true);412 const duration = startTime ? ((Date.now() - startTime) / 1000).toFixed(1) : 'unknown';413 updateFrameInfo(`Generation complete! ${data.total_frames} frames in ${duration}s`);414 415 // Update toggle states after generation416 updateToggleStates();417 });418 419 socket.on('error', function(data) {420 // showStatus(`Error: ${data.message}`, 'error');421 enableControls(true);422 });423 424 function startGeneration() {425 const prompt = document.getElementById('prompt').value.trim();426 if (!prompt) {427 alert('Please enter a prompt');428 return;429 }430 431 const seed = parseInt(document.getElementById('seed').value) || 31337;432 // const totalBlocks = parseInt(document.getElementById('blocks').value) || 7;433 const enableTorchCompile = document.getElementById('torchCompile').checked;434 const enableFp8 = document.getElementById('fp8Toggle').checked;435 const useTaehv = document.getElementById('taehvToggle').checked;436 437 // Reset state438 frameBuffer = [];439 currentFrameIndex = 0;440 receiveCount = 0;441 receiveRate = 0;442 stopPlayback();443 444 enableControls(false);445 startTime = Date.now();446 447 socket.emit('start_generation', {448 prompt: prompt,449 seed: seed,450 enable_torch_compile: enableTorchCompile,451 enable_fp8: enableFp8,452 use_taehv: useTaehv453 });454 }455 456 function stopGeneration() {457 socket.emit('stop_generation');458 enableControls(true);459 }460 461 function togglePlayback() {462 if (isPlaying) {463 stopPlayback();464 } else {465 startPlayback();466 }467 }468 469 function startPlayback() {470 if (frameBuffer.length === 0) return;471 472 isPlaying = true;473 document.getElementById('playBtn').textContent = '⏸️ Pause';474 document.getElementById('playBtn').disabled = false;475 document.getElementById('resetBtn').disabled = false;476 477 updatePlaybackTiming();478 // showStatus('Playback started', 'info');479 }480 481 function stopPlayback() {482 isPlaying = false;483 if (playbackInterval) {484 clearInterval(playbackInterval);485 playbackInterval = null;486 }487 document.getElementById('playBtn').textContent = '▶️ Play';488 }489 490 function resetPlayback() {491 stopPlayback();492 493 // Clear the entire frame buffer494 frameBuffer = [];495 currentFrameIndex = 0;496 receiveCount = 0;497 receiveRate = 0;498 499 // Reset video display to initial state500 const img = document.getElementById('videoFrame');501 const placeholder = document.getElementById('placeholderText');502 503 img.src = '';504 img.style.display = 'none';505 placeholder.style.display = 'block';506 507 // Update UI508 updateBufferInfo();509 updateFrameInfo('Reset - buffer cleared');510 511 // Disable playback controls since there's no content512 document.getElementById('playBtn').disabled = true;513 document.getElementById('resetBtn').disabled = true;514 }515 516 function updatePlaybackSpeed() {517 playbackSpeed = parseFloat(document.getElementById('playbackSpeed').value);518 if (isPlaying) {519 updatePlaybackTiming();520 }521 }522 523 function updatePlaybackTiming() {524 if (playbackInterval) {525 clearInterval(playbackInterval);526 }527 528 if (isPlaying) {529 const interval = (1000 / targetFps) / playbackSpeed;530 playbackInterval = setInterval(displayNextFrame, interval);531 }532 }533 534 function displayNextFrame() {535 if (currentFrameIndex >= frameBuffer.length) {536 // Reached end of buffer537 if (document.querySelector('#progressFill').style.width === '100%') {538 // Generation complete, stop playback539 stopPlayback();540 // showStatus('Playback complete', 'success');541 }542 return;543 }544 545 const frameData = frameBuffer[currentFrameIndex];546 displayFrame(frameData);547 currentFrameIndex++;548 549 updateBufferInfo();550 }551 552 function displayFrame(frameData) {553 const img = document.getElementById('videoFrame');554 const placeholder = document.getElementById('placeholderText');555 556 img.src = frameData.data;557 img.style.display = 'block';558 placeholder.style.display = 'none';559 560 const elapsed = startTime ? ((Date.now() - startTime) / 1000).toFixed(1) : '0';561 updateFrameInfo(`Frame ${frameData.frame_index + 1} | Block ${frameData.block_index + 1} | ${elapsed}s elapsed | ${targetFps} FPS @ ${playbackSpeed}x speed`);562 }563 564 function updateBufferInfo() {565 document.getElementById('bufferCount').textContent = frameBuffer.length;566 document.getElementById('displayedCount').textContent = currentFrameIndex;567 // document.getElementById('receiveRate').textContent = receiveRate;568 }569 570 function setQuickPrompt(type) {571 const promptBox = document.getElementById('prompt');572 if (type === 'quick-demo-1') {573 promptBox.value = 'A stylish woman walks down a Tokyo street filled with warm glowing neon and animated city signage. She wears a black leather jacket, a long red dress, and black boots, and carries a black purse. She wears sunglasses and red lipstick. She walks confidently and casually. The street is damp and reflective, creating a mirror effect of the colorful lights. Many pedestrians walk about.';574 } else if (type === 'quick-demo-2') {575 promptBox.value = 'A white and orange tabby cat is seen happily darting through a dense garden, as if chasing something. Its eyes are wide and happy as it jogs forward, scanning the branches, flowers, and leaves as it walks. The path is narrow as it makes its way between all the plants. the scene is captured from a ground-level angle, following the cat closely, giving a low and intimate perspective. The image is cinematic with warm tones and a grainy texture. The scattered daylight between the leaves and plants above creates a warm contrast, accentuating the cat’s orange fur. The shot is clear and sharp, with a shallow depth of field.';576 }577 }578 579 function enableControls(enabled) {580 document.getElementById('startBtn').disabled = !enabled;581 document.getElementById('stopBtn').disabled = enabled;582 }583 584 function updateProgress(progress, message) {585 document.getElementById('progressFill').style.width = progress + '%';586 document.getElementById('progressText').textContent = message;587 }588 589 function updateFrameInfo(text) {590 document.getElementById('frameInfo').textContent = text;591 }592 593 function showStatus(message, type) {594 const container = document.getElementById('statusContainer');595 const statusDiv = document.createElement('div');596 statusDiv.className = `status ${type}`;597 statusDiv.textContent = message;598 599 container.insertBefore(statusDiv, container.firstChild);600 601 // Remove old status messages (keep only last 3)602 while (container.children.length > 3) {603 container.removeChild(container.lastChild);604 }605 606 // Auto-remove after 5 seconds607 setTimeout(() => {608 if (statusDiv.parentNode) {609 statusDiv.parentNode.removeChild(statusDiv);610 }611 }, 5000);612 }613 </script>614</body>615</html>616 