gup/FLUX.1-dev
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>FLUX.1 [dev] — text-to-image</title>7<style>8 :root {9 --bg: #0b0f19; --panel: #121a2b; --line: #22304a; --text: #e8edf6;10 --muted: #93a0b6; --accent: #ffb347; --accent2: #5aa7ff;11 }12 * { box-sizing: border-box; margin: 0; padding: 0; }13 body {14 background: radial-gradient(900px 500px at 80% -10%, rgba(90,167,255,.08), transparent 60%), var(--bg);15 color: var(--text); font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Inter, sans-serif;16 min-height: 100vh; padding: 32px 16px;17 }18 .wrap { max-width: 720px; margin: 0 auto; }19 h1 { font-size: 30px; letter-spacing: -.01em; }20 h1 small { font-size: 15px; color: var(--muted); font-weight: 400; display: block; margin-top: 4px; }21 .topbar { display: flex; justify-content: space-between; align-items: flex-start; gap: 12px; margin-bottom: 20px; }22 button {23 font: inherit; border: none; border-radius: 8px; cursor: pointer;24 padding: 10px 18px; font-weight: 600;25 }26 #login-btn { background: var(--accent); color: #1a1205; white-space: nowrap; }27 #logout-btn { background: transparent; color: var(--muted); border: 1px solid var(--line); padding: 6px 12px; font-size: 12.5px; }28 .userchip { display: flex; align-items: center; gap: 8px; font-size: 13px; color: var(--muted); }29 .row { display: flex; gap: 10px; margin-bottom: 12px; }30 textarea {31 flex: 1; background: var(--panel); border: 1px solid var(--line); border-radius: 10px;32 color: var(--text); font: inherit; padding: 12px 14px; resize: vertical; min-height: 52px;33 }34 textarea:focus { outline: none; border-color: var(--accent2); }35 #generate {36 background: var(--accent2); color: #06101f; min-width: 110px;37 }38 #generate:disabled { opacity: .45; cursor: not-allowed; }39 .examples { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 16px; }40 .chip {41 background: var(--panel); border: 1px solid var(--line); color: var(--muted);42 border-radius: 20px; padding: 5px 12px; font-size: 12.5px; cursor: pointer;43 }44 .chip:hover { border-color: var(--accent2); color: var(--text); }45 details {46 background: var(--panel); border: 1px solid var(--line); border-radius: 10px;47 padding: 12px 16px; margin-bottom: 16px; font-size: 13.5px;48 }49 summary { cursor: pointer; color: var(--muted); font-weight: 600; }50 .controls { display: grid; grid-template-columns: 1fr 1fr; gap: 14px 24px; margin-top: 14px; }51 .ctl label { display: flex; justify-content: space-between; font-size: 12.5px; color: var(--muted); margin-bottom: 5px; }52 .ctl label output { color: var(--text); font-variant-numeric: tabular-nums; }53 input[type=range] { width: 100%; accent-color: var(--accent2); }54 input[type=number] {55 width: 100%; background: #0d1524; border: 1px solid var(--line); border-radius: 6px;56 color: var(--text); font: inherit; padding: 6px 8px;57 }58 .check { display: flex; align-items: center; gap: 8px; font-size: 13px; color: var(--muted); margin-top: 18px; }59 #status { min-height: 22px; font-size: 13.5px; color: var(--muted); margin-bottom: 10px; }60 #status.err { color: #ef8a84; }61 #result-box {62 background: var(--panel); border: 1px solid var(--line); border-radius: 12px;63 min-height: 200px; display: flex; align-items: center; justify-content: center;64 overflow: hidden; position: relative;65 }66 #result-box img { max-width: 100%; height: auto; display: block; }67 #placeholder { color: #3d4962; font-size: 14px; padding: 60px 20px; text-align: center; }68 #dl { display: none; position: absolute; right: 10px; bottom: 10px; background: rgba(10,15,25,.8);69 color: var(--text); font-size: 12.5px; padding: 6px 12px; border-radius: 6px; text-decoration: none; }70 .footnote { font-size: 11.5px; color: #5b6780; margin-top: 16px; line-height: 1.5; }71 .footnote a { color: var(--accent2); text-decoration: none; }72 .spinner { width: 26px; height: 26px; border: 3px solid var(--line); border-top-color: var(--accent2);73 border-radius: 50%; animation: spin .8s linear infinite; }74 @keyframes spin { to { transform: rotate(360deg); } }75</style>76</head>77<body>78<div class="wrap">79 <div class="topbar">80 <h1>FLUX.1 [dev]<small>12B rectified-flow transformer by Black Forest Labs — via HF Inference Providers</small></h1>81 <div id="auth-area"><button id="login-btn">Sign in with 🤗</button></div>82 </div>83 84 <div class="row">85 <textarea id="prompt" placeholder="Describe the image you want…" spellcheck="false"></textarea>86 <button id="generate" disabled>Generate</button>87 </div>88 89 <div class="examples" id="examples"></div>90 91 <details>92 <summary>Advanced settings</summary>93 <div class="controls">94 <div class="ctl"><label>Width <output id="width-out">1024</output></label>95 <input type="range" id="width" min="256" max="1440" step="32" value="1024"></div>96 <div class="ctl"><label>Height <output id="height-out">1024</output></label>97 <input type="range" id="height" min="256" max="1440" step="32" value="1024"></div>98 <div class="ctl"><label>Inference steps <output id="steps-out">28</output></label>99 <input type="range" id="steps" min="1" max="50" step="1" value="28"></div>100 <div class="ctl"><label>Guidance scale <output id="guidance-out">3.5</output></label>101 <input type="range" id="guidance" min="1" max="10" step="0.1" value="3.5"></div>102 <div class="ctl"><label>Seed</label><input type="number" id="seed" min="0" step="1" value="42"></div>103 <div class="ctl"><div class="check"><input type="checkbox" id="rand-seed" checked><label for="rand-seed">Randomize seed</label></div></div>104 </div>105 </details>106 107 <div id="status"></div>108 109 <div id="result-box">110 <div id="placeholder">Sign in, enter a prompt, and hit Generate.<br>Inference is billed to your Hugging Face account's provider quota.</div>111 <a id="dl" download="flux1-dev.png">Download</a>112 </div>113 114 <p class="footnote">115 Model: <a href="https://huggingface.co/black-forest-labs/FLUX.1-dev" target="_blank">black-forest-labs/FLUX.1-dev</a>116 (<a href="https://huggingface.co/black-forest-labs/FLUX.1-dev/blob/main/LICENSE.md" target="_blank">non-commercial license</a>).117 Serving: <a href="https://huggingface.co/docs/inference-providers" target="_blank">HF Inference Providers</a> (fal / Replicate / Wavespeed, routed automatically).118 This static Space hosts no model and stores nothing; your token stays in your browser.119 </p>120</div>121 122<script type="module">123import { oauthLoginUrl, oauthHandleRedirectIfPresent } from "https://esm.sh/@huggingface/hub@2";124import { InferenceClient } from "https://esm.sh/@huggingface/inference@4";125 126const $ = (id) => document.getElementById(id);127const statusEl = $("status");128const genBtn = $("generate");129const OAUTH_KEY = "flux_oauth";130 131const EXAMPLES = [132 "a tiny astronaut hatching from an egg on the moon",133 "a cat holding a sign that says hello world",134 "an anime illustration of a wiener schnitzel",135];136 137let oauth = null;138 139// wire sliders to their value labels140for (const id of ["width", "height", "steps", "guidance"]) {141 $(id).addEventListener("input", () => { $(id + "-out").textContent = $(id).value; });142}143 144// example chips145for (const ex of EXAMPLES) {146 const chip = document.createElement("button");147 chip.className = "chip";148 chip.textContent = ex;149 chip.addEventListener("click", () => { $("prompt").value = ex; });150 $("examples").appendChild(chip);151}152 153function setStatus(msg, isErr = false) {154 statusEl.textContent = msg;155 statusEl.className = isErr ? "err" : "";156}157 158function setSignedIn() {159 const name = oauth?.userInfo?.preferred_username || oauth?.userInfo?.name || "signed in";160 $("auth-area").innerHTML =161 `<div class="userchip"><span>🤗 ${name}</span><button id="logout-btn">Sign out</button></div>`;162 $("logout-btn").addEventListener("click", () => {163 localStorage.removeItem(OAUTH_KEY);164 location.href = location.pathname;165 });166 genBtn.disabled = false;167 setStatus("Ready.");168}169 170async function initAuth() {171 const fresh = await oauthHandleRedirectIfPresent();172 if (fresh) {173 localStorage.setItem(OAUTH_KEY, JSON.stringify(fresh));174 history.replaceState(null, "", location.pathname); // clean token fragments from URL175 oauth = fresh;176 return setSignedIn();177 }178 const stored = localStorage.getItem(OAUTH_KEY);179 if (stored) {180 const parsed = JSON.parse(stored);181 if (new Date(parsed.accessTokenExpiresAt) > new Date(Date.now() + 60_000)) {182 oauth = parsed;183 return setSignedIn();184 }185 localStorage.removeItem(OAUTH_KEY);186 }187 $("login-btn").addEventListener("click", async () => {188 location.href = await oauthLoginUrl(); // clientId/scopes auto-detected from the Space189 });190}191 192async function generate() {193 const prompt = $("prompt").value.trim();194 if (!prompt) return setStatus("Enter a prompt first.", true);195 if (!oauth) return setStatus("Sign in first.", true);196 197 if ($("rand-seed").checked) $("seed").value = Math.floor(Math.random() * 2 ** 31);198 const seed = parseInt($("seed").value, 10);199 200 genBtn.disabled = true;201 $("placeholder").innerHTML = "";202 $("placeholder").appendChild(Object.assign(document.createElement("div"), { className: "spinner" }));203 $("dl").style.display = "none";204 const existing = document.querySelector("#result-box img");205 if (existing) existing.remove();206 setStatus(`Generating… (${$("steps").value} steps, seed ${seed})`);207 const t0 = performance.now();208 209 try {210 const client = new InferenceClient(oauth.accessToken);211 const blob = await client.textToImage(212 {213 model: "black-forest-labs/FLUX.1-dev",214 provider: "auto",215 inputs: prompt,216 parameters: {217 width: parseInt($("width").value, 10),218 height: parseInt($("height").value, 10),219 num_inference_steps: parseInt($("steps").value, 10),220 guidance_scale: parseFloat($("guidance").value),221 seed,222 },223 },224 { outputType: "blob" },225 );226 const url = URL.createObjectURL(blob);227 const img = document.createElement("img");228 img.src = url;229 img.alt = prompt;230 $("placeholder").innerHTML = "";231 $("result-box").prepend(img);232 $("dl").href = url;233 $("dl").style.display = "block";234 setStatus(`Done in ${((performance.now() - t0) / 1000).toFixed(1)}s · seed ${seed}`);235 } catch (e) {236 console.error(e);237 $("placeholder").textContent = "Generation failed.";238 let msg = e?.message || String(e);239 if (/quota|payment|402|credit/i.test(msg)) {240 msg += " — your HF inference-provider quota may be exhausted (free tier is limited; PRO includes monthly credits).";241 }242 setStatus(msg, true);243 } finally {244 genBtn.disabled = false;245 }246}247 248genBtn.addEventListener("click", generate);249$("prompt").addEventListener("keydown", (e) => {250 if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); generate(); }251});252 253initAuth();254</script>255</body>256</html>257 