File size: 8,370 Bytes
c5b48f1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
<!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&nbsp;+&nbsp;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 &amp; 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&nbsp;&nbsp;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>