| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>MCMC Animation Generator (Stable Diffusion)</title> |
| <style> |
| body { background: #1e1e2e; color: white; font-family: sans-serif; } |
| canvas { background: black; display: block; margin: 10px auto; border: 2px solid #4ecdc4; } |
| .log { max-height: 200px; overflow-y: auto; font-family: monospace; font-size: 12px; background: #111; padding: 10px; border-radius: 10px; margin: 10px; } |
| </style> |
| </head> |
| <body> |
| <h1 style="text-align:center">MCMC Animation Generator (Stable Diffusion)</h1> |
| <div style="text-align:center"> |
| <input type="text" id="prompt" placeholder="Prompt" value="A surreal dreamscape" /> |
| <input type="number" id="seed" value="42" /> |
| <button onclick="generateInitialImage()">Generate Image</button> |
| <button onclick="startAnimation()">Start Animation</button> |
| </div> |
| <canvas id="canvas" width="512" height="512"></canvas> |
| <div class="log" id="log"></div> |
| <script> |
| const canvas = document.getElementById('canvas'); |
| const ctx = canvas.getContext('2d'); |
| const logEl = document.getElementById('log'); |
| |
| let state = { |
| currentPosition: Array.from({ length: 100 }, () => Math.random() * 2 - 1), |
| currentVelocity: Array(100).fill(0), |
| currentEnergy: 0, |
| baseImage: null, // This will now store a canvas element |
| seed: 42, |
| }; |
| |
| function log(msg) { |
| logEl.innerHTML += `[${new Date().toLocaleTimeString()}] ${msg}<br>`; |
| logEl.scrollTop = logEl.scrollHeight; |
| } |
| |
| // This function is no longer needed as we're fetching from Stable Diffusion |
| // function generateMockImage(seed) { ... } |
| |
| async function generateInitialImage() { |
| const prompt = document.getElementById('prompt').value; |
| const seed = parseInt(document.getElementById('seed').value); |
| state.seed = seed; |
| |
| log('Generating image with Stable Diffusion...'); |
| |
| try { |
| const response = await fetch(' |
| https://devinendorphin-mcmc-generator.hf.space/run/predict', { // REPLACE THIS URL |
| method: 'POST', |
| headers: { 'Content-Type': 'application/json' }, |
| body: JSON.stringify({ data: [prompt, seed] }), |
| }); |
| |
| if (!response.ok) { |
| const errorData = await response.json(); |
| throw new Error(errorData.detail || `HTTP error! status: ${response.status}`); |
| } |
| |
| const result = await response.json(); |
| // Assuming the image is returned as a base64 data URL (e.g., "data:image/png;base64,...") |
| const imageDataUrl = result.data[0]; |
| |
| const img = new Image(); |
| img.src = imageDataUrl; |
| |
| // Wait for the image to load before drawing |
| await new Promise((resolve) => { img.onload = resolve; }); |
| |
| // Clear the main canvas and draw the new image |
| ctx.clearRect(0, 0, canvas.width, canvas.height); |
| ctx.drawImage(img, 0, 0, 512, 512); |
| |
| // Create a new canvas to store the base image for MCMC operations |
| state.baseImage = document.createElement('canvas'); |
| state.baseImage.width = 512; |
| state.baseImage.height = 512; |
| const baseCtx = state.baseImage.getContext('2d'); |
| baseCtx.drawImage(img, 0, 0, 512, 512); |
| |
| |
| const imageData = ctx.getImageData(0, 0, 512, 512); |
| state.currentEnergy = calculateEnergy(imageData); |
| |
| log('Stable Diffusion image loaded and energy calculated.'); |
| } catch (e) { |
| log('Error using Hugging Face Space: ' + e.message); |
| } |
| } |
| |
| function calculateEnergy(imageData) { |
| let energy = 0; |
| const data = imageData.data; |
| for (let i = 0; i < data.length; i += 4) { |
| const intensity = (data[i] + data[i + 1] + data[i + 2]) / 3; |
| energy += Math.pow(intensity - 128, 2) * 0.00001; |
| } |
| return energy; |
| } |
| |
| function generateImageVariation(base, params) { |
| const canvas = document.createElement('canvas'); |
| canvas.width = 512; |
| canvas.height = 512; |
| const ctx = canvas.getContext('2d'); |
| ctx.drawImage(base, 0, 0); // 'base' is now a canvas element |
| const imgData = ctx.getImageData(0, 0, 512, 512); |
| const data = imgData.data; |
| for (let i = 0; i < data.length; i += 4) { |
| const p = params[Math.floor((i / 4) % 100)]; |
| data[i] += p * 10; |
| data[i + 1] += p * 5; |
| data[i + 2] += p * 15; |
| } |
| ctx.putImageData(imgData, 0, 0); |
| return canvas; |
| } |
| |
| function calculateGradient(position, baseImage) { |
| const epsilon = 0.01; |
| const grad = new Array(position.length).fill(0); |
| for (let i = 0; i < position.length; i++) { |
| const posPlus = [...position]; |
| const posMinus = [...position]; |
| posPlus[i] += epsilon; |
| posMinus[i] -= epsilon; |
| |
| const imgPlus = generateImageVariation(baseImage, posPlus); |
| const imgMinus = generateImageVariation(baseImage, posMinus); |
| |
| const energyPlus = calculateEnergy(imgPlus.getContext('2d').getImageData(0, 0, 512, 512)); |
| const energyMinus = calculateEnergy(imgMinus.getContext('2d').getImageData(0, 0, 512, 512)); |
| |
| grad[i] = (energyPlus - energyMinus) / (2 * epsilon); |
| } |
| return grad; |
| } |
| |
| async function startAnimation() { |
| if (!state.baseImage) return log('No base image loaded. Please generate an image first.'); |
| log('Starting animation...'); |
| const T = 1.0, stepSize = 0.01, damping = 0.5; |
| |
| for (let frame = 0; frame < 30; frame++) { |
| const grad = calculateGradient(state.currentPosition, state.baseImage); |
| const newVelocity = state.currentVelocity.map((v, i) => v - stepSize * (grad[i] + damping * v) + (Math.random() * 2 - 1) * Math.sqrt(2 * damping * T * stepSize)); |
| const newPosition = state.currentPosition.map((x, i) => x + stepSize * newVelocity[i]); |
| const proposedImage = generateImageVariation(state.baseImage, newPosition); |
| const proposedEnergy = calculateEnergy(proposedImage.getContext('2d').getImageData(0, 0, 512, 512)); |
| |
| let accept = false; |
| if (proposedEnergy <= state.currentEnergy) accept = true; |
| else if (Math.random() < Math.exp(-(proposedEnergy - state.currentEnergy) / T)) accept = true; |
| |
| if (accept) { |
| state.currentPosition = newPosition; |
| state.currentVelocity = newVelocity; |
| state.currentEnergy = proposedEnergy; |
| ctx.drawImage(proposedImage, 0, 0); |
| log(`Frame ${frame + 1}: Accepted, Energy: ${proposedEnergy.toFixed(4)}`); |
| } else { |
| log(`Frame ${frame + 1}: Rejected`); |
| } |
| await new Promise(res => setTimeout(res, 50)); |
| } |
| log('Animation complete.'); |
| } |
| </script> |
| </body> |
| </html> |