Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="utf-8" /> | |
| <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" /> | |
| <title>PixelLock — AI Pixel-Art Retexturing</title> | |
| <meta name="description" content="Footprint-locked AI pixel-art retexturing — keep the exact silhouette, change the style." /> | |
| <link rel="preconnect" href="https://fonts.googleapis.com"> | |
| <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> | |
| <link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;700&family=JetBrains+Mono:wght@400;500;700&display=swap" rel="stylesheet"> | |
| <link rel="stylesheet" href="/static/styles.css" /> | |
| <link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Crect width='16' height='16' rx='3' fill='%230b0b14'/%3E%3Crect x='3' y='3' width='4' height='4' fill='%23a06bff'/%3E%3Crect x='9' y='3' width='4' height='4' fill='%2300e5d4'/%3E%3Crect x='3' y='9' width='4' height='4' fill='%2300e5d4'/%3E%3Crect x='9' y='9' width='4' height='4' fill='%23a06bff'/%3E%3C/svg%3E" /> | |
| </head> | |
| <body> | |
| <div style="background:linear-gradient(90deg,#a06bff,#00e5d4);padding:10px 16px;text-align:center;font-family:'Space Grotesk',system-ui,sans-serif;line-height:1.5;"><span style="color:#0b0b14;font-weight:700;font-size:14px;">⚡ CPU instance — fastest way to see it work:</span> <a href="https://youtu.be/Ju-Mpddfn3c" target="_blank" rel="noopener" style="color:#0b0b14;font-weight:800;text-decoration:underline;margin:0 5px;font-size:14px;">▶ watch the 45-sec demo</a><span style="color:#0b0b14;opacity:.6;">·</span><a href="https://huggingface.co/spaces/solarkyle/pixellock" target="_blank" rel="noopener" style="color:#0b0b14;font-weight:800;text-decoration:underline;margin:0 5px;font-size:14px;">run it live on GPU → (wakes ~1–2 min)</a></div> | |
| <div class="bg-glow" aria-hidden="true"></div> | |
| <div class="scanlines" aria-hidden="true"></div> | |
| <header class="appbar"> | |
| <div class="brand"> | |
| <span class="logo" aria-hidden="true"><i></i><i></i><i></i><i></i></span> | |
| <h1 class="wordmark">Pixel<b>Lock</b></h1> | |
| <span class="brand-div">|</span> | |
| <span class="brand-sub">AI Pixel-Art Retexturing Tool</span> | |
| </div> | |
| <div class="fp-pill" title="Every edit keeps your sprite's exact shape, by construction"> | |
| <svg viewBox="0 0 24 24" width="14" height="14"><path fill="currentColor" d="M12 1a5 5 0 0 0-5 5v3H6a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-9a2 2 0 0 0-2-2h-1V6a5 5 0 0 0-5-5m0 2a3 3 0 0 1 3 3v3H9V6a3 3 0 0 1 3-3"/></svg> | |
| Footprint-locked retexturing | |
| </div> | |
| <div class="appbar-right"> | |
| <span class="server-pill"><span id="server-dot" class="dot down"></span><span id="server-label">connecting…</span></span> | |
| <a class="ghost-btn" href="https://github.com/ggml-org/llama.cpp" target="_blank" rel="noopener">⌁ GBNF · llama.cpp</a> | |
| </div> | |
| </header> | |
| <div class="howbar"> | |
| <span class="how-tag">⚡ NOT an image generator</span> | |
| <span class="how-steps">your PNG → a <b>text grid</b> (PALETTE + GRID) → a <b>fine-tuned text LLM</b> rewrites it under a <b>GBNF grammar</b> that locks the exact shape → pixel-perfect PNG. <em>It's language modeling, not diffusion.</em></span> | |
| </div> | |
| <section class="proofbar" aria-label="PixelLock guarantees"> | |
| <div class="proof-card"> | |
| <span class="proof-k">100%</span> | |
| <span class="proof-v">footprint constrained</span> | |
| </div> | |
| <div class="proof-card"> | |
| <span class="proof-k">PNG → TEXT → PNG</span> | |
| <span class="proof-v">palette grid, not diffusion</span> | |
| </div> | |
| <div class="proof-card"> | |
| <span class="proof-k">LLAMA.CPP + GBNF</span> | |
| <span class="proof-v">shape locked in the decoder</span> | |
| </div> | |
| </section> | |
| <main class="layout"> | |
| <!-- LEFT: controls --> | |
| <aside class="controls"> | |
| <section class="step"> | |
| <h2><span class="num">01</span> Upload sprite</h2> | |
| <div id="dropzone" class="dropzone" tabindex="0" role="button" aria-label="Upload a sprite PNG"> | |
| <input id="file" type="file" accept="image/png,image/*" hidden /> | |
| <div id="dz-empty" class="dz-empty"> | |
| <div class="dz-ico" aria-hidden="true">⬆</div> | |
| <p class="dz-main">Drop sprite PNG or <span class="link">click to upload</span></p> | |
| <p class="dz-hint">best for 16×16 – 64×64</p> | |
| <span class="dz-tag">PNG</span> | |
| </div> | |
| <div id="dz-loaded" class="dz-loaded" hidden> | |
| <img id="dz-preview" class="pixelated" alt="loaded sprite" /> | |
| <div class="dz-meta"><span id="dz-name" class="dz-name">sprite.png</span> | |
| <button id="dz-clear" class="link-btn" type="button">change</button></div> | |
| </div> | |
| </div> | |
| </section> | |
| <section class="step starter-step"> | |
| <h2><span class="num">01B</span> Starter sprites <span class="opt">click to load</span></h2> | |
| <div id="asset-strip" class="asset-strip" aria-label="Starter sprites"></div> | |
| </section> | |
| <section class="step"> | |
| <h2><span class="num">02</span> Theme / vibe <span class="opt">optional</span></h2> | |
| <div id="chips" class="chips" role="listbox" aria-label="Theme presets"></div> | |
| </section> | |
| <section class="step"> | |
| <h2><span class="num">03</span> Prompt <span class="opt">optional</span></h2> | |
| <textarea id="prompt" class="prompt" rows="3" spellcheck="false" | |
| placeholder="Restyle into… (the AI invents the colors; the shape stays locked)"></textarea> | |
| </section> | |
| <section class="step"> | |
| <h2><span class="num">04</span> Mode</h2> | |
| <div id="mode-toggle" class="toggle" role="tablist" aria-label="Edit mode"> | |
| <span id="toggle-glider" class="toggle-glider" aria-hidden="true"></span> | |
| <button class="toggle-opt active" data-mode="exact" role="tab" aria-selected="true">🎨 Retheme</button> | |
| <button class="toggle-opt" data-mode="upscale2x" role="tab" aria-selected="false">⤢ Upscale 2×</button> | |
| </div> | |
| </section> | |
| <button id="run" class="run-btn" disabled> | |
| <span class="run-label">▶ RUN RETEXTURE</span> | |
| <span class="run-spinner" aria-hidden="true"></span> | |
| </button> | |
| <p id="run-hint" class="hint">load a sprite to begin</p> | |
| </aside> | |
| <!-- CENTER: stage --> | |
| <section class="stage lab"> | |
| <div class="compare"> | |
| <figure class="frame"> | |
| <figcaption>INPUT <span>your sprite</span></figcaption> | |
| <div class="checker canvas"><img id="img-in" class="pixelated" alt="input sprite" /></div> | |
| </figure> | |
| <div class="arrow" aria-hidden="true">▶</div> | |
| <figure class="frame"> | |
| <figcaption>OUTPUT <span>AI restyled</span></figcaption> | |
| <div class="checker canvas"> | |
| <img id="img-out" class="pixelated" alt="restyled sprite" /> | |
| <div id="frame-empty" class="frame-empty"><p>your restyled sprite<br/>appears here</p></div> | |
| <div id="frame-loader" class="frame-loader" hidden> | |
| <div class="loader-grid"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></div> | |
| <p id="loader-text">working…</p> | |
| </div> | |
| </div> | |
| </figure> | |
| <aside class="sil-panel"> | |
| <div class="sil-head"> | |
| <svg viewBox="0 0 24 24" width="12" height="12"><path fill="currentColor" d="M12 1a5 5 0 0 0-5 5v3H6a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-9a2 2 0 0 0-2-2h-1V6a5 5 0 0 0-5-5m0 2a3 3 0 0 1 3 3v3H9V6a3 3 0 0 1 3-3"/></svg> | |
| SILHOUETTE LOCKED | |
| </div> | |
| <div class="sil-art checker"><canvas id="sil-canvas" width="64" height="64"></canvas></div> | |
| <p class="sil-note">Exact footprint & transparency preserved — guaranteed by the decoding grammar, not hoped for.</p> | |
| </aside> | |
| </div> | |
| <div class="statusbar"> | |
| <span id="badge" class="badge ok" hidden>✓ silhouette preserved</span> | |
| <span id="status" class="status">—</span> | |
| <a id="download" class="dl-btn" download hidden>⭳ Download PNG</a> | |
| </div> | |
| <details class="wire-reveal"> | |
| <summary>⌖ see what the model actually <b>wrote</b> — it's text, not pixels</summary> | |
| <pre id="wire-out" class="wire">Run an edit, then peek here: the LLM literally writes a PALETTE block and a GRID of cells (K R R K …). The GBNF grammar forces every transparent cell to stay transparent — that's why the shape can't break.</pre> | |
| </details> | |
| <section class="gallery-wrap" aria-label="Before and after examples"> | |
| <div class="gallery-head"><h3>Before → After examples</h3><span class="hint-sm">click any card to load it</span></div> | |
| <div id="gallery" class="gallery"></div> | |
| </section> | |
| </section> | |
| </main> | |
| <footer class="site-foot"> | |
| <span class="foot-strong">✦ Pixel-precise · AI-powered · your shape, any vibe</span> | |
| <span class="sep">·</span> | |
| <span class="foot-dim">fine-tuned Gemma-4-12B · GBNF footprint grammar · runs on llama.cpp · Build Small Hackathon</span> | |
| </footer> | |
| <script src="/static/app.js" defer></script> | |
| </body> | |
| </html> | |