pxg-tiny-site / play.html
Tarul's picture
Upload folder using huggingface_hub
ae0648d verified
Raw
History Blame Contribute Delete
7.09 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>PXG-Tiny Playground — in-browser live demo</title>
<style>
:root{
--bg:#0d0f1a; --panel:#1a1f3a; --panel2:#20264a; --ink:#e8eaf6; --dim:#9aa3c7;
--gold:#ffd24a; --green:#5df08a; --blue:#5db8ff; --line:#2a3060;
--mono:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
}
*{margin:0;padding:0;box-sizing:border-box}
body{background:var(--bg);color:var(--ink);font-family:system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;
min-height:100vh;display:flex;flex-direction:column;align-items:center;padding:34px 18px 60px;
background-image:radial-gradient(circle at 50% -10%,#1b2150 0%,transparent 50%)}
a{color:var(--blue);text-decoration:none}
h1{font-family:var(--mono);font-size:28px;letter-spacing:-.5px}
h1 span{color:var(--gold)}
.tag{color:var(--dim);font-size:14px;margin:8px 0 4px;text-align:center}
.tag b{color:var(--ink)}
.panel{background:var(--panel);border:1px solid var(--line);border-radius:14px;padding:22px;
width:min(720px,100%);margin-top:22px}
.row{display:flex;gap:10px;flex-wrap:wrap}
input[type=text]{flex:1;min-width:220px;background:#0a0c16;border:1px solid var(--line);border-radius:10px;
color:var(--ink);font-family:var(--mono);font-size:15px;padding:12px 14px;outline:none}
input[type=text]:focus{border-color:var(--gold)}
button{background:var(--gold);color:#1a1400;border:none;border-radius:10px;font-family:var(--mono);
font-size:14px;font-weight:700;padding:12px 22px;cursor:pointer}
button:disabled{opacity:.5;cursor:wait}
button.ghost{background:var(--panel2);color:var(--ink);border:1px solid var(--line);font-weight:400}
.subrow{display:flex;gap:16px;align-items:center;margin-top:12px;font-size:13px;color:var(--dim);flex-wrap:wrap}
.subrow label{display:flex;gap:6px;align-items:center;cursor:pointer}
canvas{width:256px;height:256px;background:#0a0c16;border:1px solid var(--line);border-radius:12px;
image-rendering:pixelated;margin:18px auto 4px;display:block}
#status{font-family:var(--mono);font-size:12.5px;color:var(--dim);text-align:center;min-height:20px;margin-top:8px}
#status.ok{color:var(--green)} #status.err{color:#ff7b7b}
.ex{display:flex;gap:8px;flex-wrap:wrap;justify-content:center;margin-top:16px}
.ex button{background:var(--panel2);color:var(--dim);border:1px solid var(--line);font-weight:400;
font-size:12px;padding:7px 12px;border-radius:999px}
.ex button:hover{color:var(--ink);border-color:var(--gold)}
.foot{margin-top:26px;font-size:12.5px;color:var(--dim);text-align:center;line-height:1.8}
.chips{display:flex;gap:8px;flex-wrap:wrap;justify-content:center;margin-top:10px}
.note{font-size:12px;color:var(--dim);text-align:center;margin-top:10px}
</style>
</head>
<body>
<h1>PXG<span>-Tiny</span> playground</h1>
<p class="tag">the <b>entire model runs in your browser</b> — real NumPy inference via Pyodide,
weights (~1.8&nbsp;MB) served from this page · <b>no server, no GPU, no API</b></p>
<div class="panel">
<div class="row">
<input type="text" id="prompt" placeholder="a golden sword" spellcheck="false">
<button id="go" disabled>Generate</button>
</div>
<div class="subrow">
<label>seed <input type="number" id="seed" value="0" style="width:80px;background:#0a0c16;border:1px solid var(--line);border-radius:6px;color:var(--ink);padding:4px 8px;font-family:var(--mono)"></label>
<label><input type="checkbox" id="verify" checked> verifier-guided retries (8 attempts)</label>
</div>
<canvas id="cv" width="16" height="16"></canvas>
<div id="status">booting Pyodide + loading weights… first load takes ~20-40 s, then ~1-3 s per sprite</div>
<div class="ex" id="ex"></div>
</div>
<p class="note">38 trained classes — characters (wizard, knight, archer, skeleton, zombie),
animals (cat, dog, bird, fish, deer, mouse, bat), weapons, potions, treasure, furniture,
trees, rocks, bushes, tiles, fireball, house. Anything else → a clarifying question.</p>
<div class="foot">
model: <a href="https://huggingface.co/Tarul/pxg-tiny">huggingface.co/Tarul/pxg-tiny</a> ·
code: <a href="https://github.com/tarulahsan/PXG-Tiny-AI">github.com/tarulahsan/PXG-Tiny-AI</a><br>
created by <b>Chowdhury Tarul Ahsan</b> · <a href="mailto:tarulahsan@gmail.com">tarulahsan@gmail.com</a> ·
<a href="index.html">← back to the landing page</a>
</div>
<script>
const EXAMPLES = ["a golden sword","a purple bat with wings","a brown deer with antlers",
"a slim vial of green potion","a wizard with a purple hat","a cottage with a red roof",
"a wooden chair","a knight in iron armor","a mossy boulder","old oak with autumn leaves",
"a knight with a golden sword","a large fireball","a water texture tile","a gold coin"];
const cv = document.getElementById("cv"), ctx = cv.getContext("2d");
const statusEl = document.getElementById("status");
const goBtn = document.getElementById("go"), promptEl = document.getElementById("prompt");
const seedEl = document.getElementById("seed"), verifyEl = document.getElementById("verify");
let busy = false;
const worker = new Worker("worker.js");
worker.onmessage = (e) => {
const d = e.data;
if (d.type === "status") { statusEl.textContent = d.msg; }
else if (d.type === "ready") {
statusEl.textContent = "model ready — the whole 483k-param network is running in this tab";
statusEl.className = "ok"; goBtn.disabled = false;
} else if (d.type === "result") { draw(d.data); busy = false; goBtn.disabled = false; }
else if (d.type === "error") {
statusEl.textContent = "error: " + d.msg; statusEl.className = "err";
busy = false; goBtn.disabled = false;
}
};
worker.postMessage({ type: "init" });
function draw(r) {
if (r.rgba === null || r.rgba === undefined) {
statusEl.textContent = r.message || ("[" + r.gate + "] try one of the trained classes");
statusEl.className = "err"; return;
}
const img = ctx.createImageData(16, 16);
img.data.set(r.rgba);
ctx.putImageData(img, 0, 0);
if (r.gate === "accept")
statusEl.textContent = `accepted — attempt ${r.attempt + 1}, seed ${r.seed}`;
else
statusEl.textContent = `last-resort sprite (hard prompt) — seed ${r.seed}, try another seed`;
statusEl.className = "ok";
}
function go() {
if (busy) return;
const p = promptEl.value.trim();
if (p.length < 4) { statusEl.textContent = "type a prompt, e.g. 'a gold sword'"; return; }
busy = true; goBtn.disabled = true;
statusEl.textContent = "generating…";
worker.postMessage({ type: "generate", prompt: p,
seed: Number(seedEl.value) || 0, verify: verifyEl.checked });
}
goBtn.onclick = go;
promptEl.addEventListener("keydown", (e) => { if (e.key === "Enter") go(); });
document.getElementById("ex").innerHTML = "";
EXAMPLES.forEach((p) => {
const b = document.createElement("button");
b.textContent = p;
b.onclick = () => { promptEl.value = p; go(); };
document.getElementById("ex").appendChild(b);
});
</script>
</body>
</html>