| <!doctype html> |
| <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 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"> |
| |
| <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> |
|
|
| |
| <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> |
|
|