Spaces:
Running
Running
Download index.html from mrfakename/chitra-tiny-webgpu: direct link, hf CLI and curl.
- Browser
- Download file 10.1 kB
-
https://huggingface.co/spaces/mrfakename/chitra-tiny-webgpu/resolve/main/index.html
- Command line
-
hf download hf://spaces/mrfakename/chitra-tiny-webgpu/index.html
-
curl -L -o index.html https://huggingface.co/spaces/mrfakename/chitra-tiny-webgpu/resolve/main/index.html
10.1 kB
| <html lang="en"> | |
| <head> | |
| <meta charset="utf-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |
| <title>Chitra Tiny WebGPU</title> | |
| <link rel="icon" href="data:image/svg+xml,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 viewBox=%270 0 16 16%27%3E%3Crect x=%271.5%27 y=%272.5%27 width=%2713%27 height=%2711%27 rx=%271%27 fill=%27none%27 stroke=%27%23b4432b%27 stroke-width=%271.6%27/%3E%3Cpath d=%27M3 12l3.5-4 2.5 2.5L11 8l2 4z%27 fill=%27%23b4432b%27/%3E%3C/svg%3E"> | |
| <style> | |
| :root{--bg:#f4f0e8;--panel:#fbf9f4;--line:#e3ddd0;--text:#1c1b18;--muted:#6b675e;--accent:#b4432b;--accent-text:#fff;--chip:#ece6da;--tile:#e9e3d6} | |
| @media (prefers-color-scheme:dark){:root{--bg:#141311;--panel:#1d1b18;--line:#34302a;--text:#ece8e1;--muted:#a39d92;--accent:#d2593e;--accent-text:#fff;--chip:#27241f;--tile:#24211d}} | |
| *{box-sizing:border-box} | |
| body{margin:0;background:var(--bg);color:var(--text);font:15px/1.5 system-ui,-apple-system,"Segoe UI",sans-serif} | |
| main{max-width:860px;margin:0 auto;padding:28px 16px 40px} | |
| h1{font-size:26px;margin:0 0 6px;letter-spacing:-.01em} | |
| p{margin:0 0 10px} | |
| a{color:var(--accent)} | |
| .muted{color:var(--muted);font-size:14px} | |
| .panel{background:var(--panel);border:1px solid var(--line);border-radius:6px;padding:14px;margin-top:16px} | |
| label{display:block;font-size:13px;color:var(--muted);margin-bottom:6px} | |
| textarea,input[type=text]{width:100%;border:1px solid var(--line);border-radius:4px;background:var(--bg);color:var(--text);font:inherit;padding:8px 10px} | |
| textarea{min-height:64px;resize:vertical} | |
| input[type=text]{height:36px} | |
| textarea:focus,input:focus{outline:2px solid var(--accent);outline-offset:-1px} | |
| details{margin-top:12px;border-top:1px solid var(--line);padding-top:10px} | |
| summary{cursor:pointer;font-size:14px} | |
| .grid{display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-top:10px} | |
| .span2{grid-column:1/-1} | |
| input[type=number]{width:100%;height:36px;border:1px solid var(--line);border-radius:4px;background:var(--bg);color:var(--text);font:inherit;padding:0 10px} | |
| input[type=range]{width:100%;accent-color:var(--accent);height:36px;margin:0} | |
| .seedrow{display:flex;gap:10px;align-items:center} | |
| .seedrow label{margin:0;display:flex;gap:6px;align-items:center;white-space:nowrap;color:var(--text);font-size:14px} | |
| .row{display:flex;gap:10px;align-items:center;margin-top:12px;flex-wrap:wrap} | |
| button{height:40px;border:0;border-radius:4px;background:var(--accent);color:var(--accent-text);font:600 15px system-ui,sans-serif;padding:0 22px;cursor:pointer} | |
| button:disabled{opacity:.5;cursor:default} | |
| .chips{display:flex;flex-wrap:wrap;gap:8px;margin-top:8px} | |
| .chip{background:var(--chip);border:1px solid var(--line);color:var(--text);height:auto;min-height:32px;padding:5px 10px;font-weight:400;font-size:13px;text-align:left;max-width:100%} | |
| .bar{height:4px;background:var(--line);border-radius:2px;overflow:hidden;margin-top:10px;display:none} | |
| .bar>div{height:100%;width:0;background:var(--accent)} | |
| #out{display:grid;grid-template-columns:repeat(auto-fill,minmax(190px,1fr));gap:10px} | |
| #out a{display:block;aspect-ratio:1;background:var(--tile);border-radius:4px;overflow:hidden} | |
| #out img{width:100%;height:100%;display:block;image-rendering:auto} | |
| #out .empty{grid-column:1/-1;color:var(--muted);font-size:14px;padding:30px 0;text-align:center} | |
| footer{margin-top:28px;font-size:14px} | |
| @media (max-width:520px){.grid{grid-template-columns:1fr}} | |
| </style> | |
| </head> | |
| <body> | |
| <main> | |
| <h1>Chitra Tiny</h1> | |
| <p>Type what you want to see and get small 256×256 pictures, made entirely in your browser. Nothing leaves your device. | |
| Best at photos, paintings, landscapes, buildings, vehicles and food; weak at people, text and counting.</p> | |
| <p class="muted">Model: <a href="https://huggingface.co/udgamlabs/Chitra-Tiny" target="_blank" rel="noopener">udgamlabs/Chitra-Tiny</a> · Server demo: <a href="https://huggingface.co/spaces/mrfakename/chitra-tiny" target="_blank" rel="noopener">mrfakename/chitra-tiny</a></p> | |
| <div class="panel"> | |
| <label for="prompt">Prompt</label> | |
| <textarea id="prompt" maxlength="500">a lighthouse on a rocky coast at sunset</textarea> | |
| <details> | |
| <summary>Settings</summary> | |
| <div class="grid"> | |
| <div class="span2"><label for="neg">Avoid (optional)</label><input id="neg" type="text" placeholder="e.g. blurry, dark"></div> | |
| <div><label for="n">Number of pictures: <span id="nv">2</span></label><input id="n" type="range" min="1" max="4" step="1" value="2"></div> | |
| <div><label for="steps">Steps (fewer is faster): <span id="stepsv">30</span></label><input id="steps" type="range" min="10" max="100" step="5" value="30"></div> | |
| <div><label for="guid">Prompt strength: <span id="guidv">7</span></label><input id="guid" type="range" min="1" max="12" step="0.5" value="7"></div> | |
| <div><label for="seed">Seed</label><div class="seedrow"><input id="seed" type="number" min="0" step="1" value="0"><label><input id="rand" type="checkbox" checked> Random</label></div></div> | |
| </div> | |
| </details> | |
| <div class="row"><button id="go" disabled>Loading…</button><span id="status" class="muted"></span></div> | |
| <div class="bar" id="bar"><div></div></div> | |
| </div> | |
| <div class="panel"> | |
| <label>Pictures</label> | |
| <div id="out"><div class="empty">Your pictures will show up here.</div></div> | |
| </div> | |
| <div class="panel"> | |
| <label>Try one</label> | |
| <div class="chips" id="chips"></div> | |
| </div> | |
| <footer class="muted">The first visit downloads about 745 MB, then it is saved in your browser. Needs a browser with WebGPU (recent Chrome, Edge or Safari).<br><a href="https://x.com/realmrfakename" target="_blank" rel="noopener">x.com/realmrfakename</a></footer> | |
| </main> | |
| <script> | |
| const EXAMPLES = [ | |
| "a lighthouse on a rocky coast at sunset", | |
| "an oil painting of a vase of flowers", | |
| "a mountain landscape with snow and pine trees", | |
| "a watercolor painting of a bird on a branch", | |
| "a red car parked in front of a house", | |
| "a bridge over a river in autumn", | |
| "a black and white photo of a city street", | |
| "a bowl of soup next to a spoon", | |
| ]; | |
| const $ = id => document.getElementById(id); | |
| const go = $("go"), status = $("status"), bar = $("bar"), fill = bar.firstElementChild, out = $("out"); | |
| let state = "probe", urls = []; | |
| const worker = new Worker("worker.js", { type: "module" }); | |
| for (const t of EXAMPLES) { | |
| const b = document.createElement("button"); b.className = "chip"; b.textContent = t; | |
| b.onclick = () => { $("prompt").value = t; if (state === "ready") generate(); }; $("chips").appendChild(b); | |
| } | |
| for (const [id, fmt] of [["n", v => v], ["steps", v => v], ["guid", v => Number(v).toString()]]) | |
| $(id).oninput = e => $(id + "v").textContent = fmt(e.target.value); | |
| const progress = f => { bar.style.display = "block"; fill.style.width = (100 * f).toFixed(1) + "%"; }; | |
| const done = () => { bar.style.display = "none"; fill.style.width = "0"; }; | |
| function setButton(text, enabled) { go.textContent = text; go.disabled = !enabled; } | |
| function load() { state = "loading"; setButton("Loading…", false); status.textContent = "Downloading…"; worker.postMessage({ type: "load" }); } | |
| function generate() { | |
| if (state !== "ready") return; | |
| const prompt = $("prompt").value.trim(); | |
| if (!prompt) { status.textContent = "Type a prompt first."; return; } | |
| if ($("rand").checked) $("seed").value = Math.floor(Math.random() * 2147483647); | |
| state = "busy"; setButton("Generating…", false); status.textContent = "Starting…"; progress(0); | |
| worker.postMessage({ type: "generate", prompt, negative: $("neg").value.trim(), n: Number($("n").value), | |
| steps: Number($("steps").value), guidance: Number($("guid").value), seed: Math.max(0, parseInt($("seed").value) || 0) }); | |
| } | |
| go.onclick = () => state === "idle" ? load() : generate(); | |
| $("prompt").addEventListener("keydown", e => { if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); generate(); } }); | |
| const mb = b => (b / 1e6).toFixed(0); | |
| worker.onmessage = ({ data }) => { | |
| if (data.type === "probe") { | |
| if (!data.webgpu) { state = "none"; setButton("Not supported", false); status.textContent = "This browser has no WebGPU. Try recent Chrome, Edge or Safari, or the server demo."; return; } | |
| if (data.cached) load(); else { state = "idle"; setButton("Download model (745 MB)", true); status.textContent = "One-time download, then saved in your browser."; } | |
| } else if (data.type === "download") { | |
| progress(data.got / data.total); status.textContent = `Downloading… ${mb(data.got)} / ${mb(data.total)} MB`; | |
| } else if (data.type === "status") { | |
| status.textContent = data.text; | |
| } else if (data.type === "ready") { | |
| state = "ready"; done(); setButton("Generate", true); status.textContent = "Ready"; | |
| } else if (data.type === "step") { | |
| progress(data.done / data.total); status.textContent = `Step ${data.done} of ${data.total}`; | |
| } else if (data.type === "images") { | |
| state = "ready"; done(); setButton("Generate", true); | |
| urls.forEach(u => URL.revokeObjectURL(u)); urls = data.images.map(b => URL.createObjectURL(b)); | |
| out.replaceChildren(...urls.map((u, i) => { | |
| const a = document.createElement("a"); a.href = u; a.download = `chitra-${data.seed}-${i}.png`; a.title = "Click to save"; | |
| const img = document.createElement("img"); img.src = u; img.alt = $("prompt").value; a.appendChild(img); return a; | |
| })); | |
| status.textContent = `Took ${data.took.toFixed(1)} s`; | |
| document.body.dataset.done = "1"; | |
| } else if (data.type === "error") { | |
| done(); | |
| if (data.message === "NO_WEBGPU") { state = "none"; setButton("Not supported", false); status.textContent = "WebGPU is not available here. Try recent Chrome, Edge or Safari, or the server demo."; return; } | |
| const wasReady = state === "busy"; | |
| state = wasReady ? "ready" : "idle"; setButton(wasReady ? "Generate" : "Try again", true); | |
| status.textContent = "Something went wrong: " + data.message; | |
| document.body.dataset.error = data.message; | |
| } | |
| }; | |
| worker.postMessage({ type: "probe" }); | |
| </script> | |
| </body> | |
| </html> | |