laya-snake / src /web /index.html
deepondas's picture
Claude Opus 5.5
feat(web): add view-only mode and a Hugging Face Space deployment
634fb2d
Raw History Blame Contribute Delete
16.8 kB
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Laya Snake Live</title>
<link rel="stylesheet" href="/css/dashboard.css">
<script type="importmap">
{ "imports": { "three": "/vendor/three/build/three.module.min.js", "three/addons/": "/vendor/three/addons/" } }
</script>
</head>
<body>
<div class="app">
<nav class="nav" aria-label="Sections">
<a href="#game" class="on"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="6" width="20" height="12" rx="4"/><path d="M7 10v4M5 12h4"/><circle cx="16" cy="11" r="1"/><circle cx="18" cy="13" r="1"/></svg>Game</a>
<a href="#stats"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M5 20V10M12 20V4M19 20v-7"/></svg>Statistics</a>
<a href="#model"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="6" y="6" width="12" height="12" rx="2"/><path d="M9 2v4M15 2v4M9 18v4M15 18v4M2 9h4M2 15h4M18 9h4M18 15h4"/></svg>Model details</a>
<a href="#charts"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 17l5-6 4 4 8-9"/></svg>Analytics</a>
<a href="#history"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 12a9 9 0 1 0 3-6.7L3 8"/><path d="M3 3v5h5M12 7v5l3 2"/></svg>History</a>
<a href="#settings"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.7 1.7 0 0 0 .3 1.8l.1.1a2 2 0 1 1-2.8 2.8l-.1-.1a1.7 1.7 0 0 0-1.8-.3 1.7 1.7 0 0 0-1 1.5V21a2 2 0 1 1-4 0v-.1a1.7 1.7 0 0 0-1.1-1.5 1.7 1.7 0 0 0-1.8.3l-.1.1a2 2 0 1 1-2.8-2.8l.1-.1a1.7 1.7 0 0 0 .3-1.8 1.7 1.7 0 0 0-1.5-1H3a2 2 0 1 1 0-4h.1a1.7 1.7 0 0 0 1.5-1.1 1.7 1.7 0 0 0-.3-1.8l-.1-.1a2 2 0 1 1 2.8-2.8l.1.1a1.7 1.7 0 0 0 1.8.3H9a1.7 1.7 0 0 0 1-1.5V3a2 2 0 1 1 4 0v.1a1.7 1.7 0 0 0 1 1.5 1.7 1.7 0 0 0 1.8-.3l.1-.1a2 2 0 1 1 2.8 2.8l-.1.1a1.7 1.7 0 0 0-.3 1.8V9a1.7 1.7 0 0 0 1.5 1H21a2 2 0 1 1 0 4h-.1a1.7 1.7 0 0 0-1.5 1z"/></svg>Settings</a>
<div class="grow"></div>
<div class="foot">Every move is one forward pass of the local model. Nothing leaves this machine.</div>
</nav>
<div class="main">
<header>
<div class="brand">
<div class="logo" aria-hidden="true">
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><path d="M17 4c-3 0-6 1-6 4s5 3 5 6-3 4-6 4H6"/><circle cx="17.5" cy="4" r="1.3" fill="currentColor"/></svg>
</div>
<div>
<h1>Laya Snake Live</h1>
<p>Played live by a local non-autoregressive decision model</p>
</div>
</div>
<div class="hdr-right">
<span class="pill" id="status"><span class="dot"></span><span id="status-text">CONNECTING</span></span>
<span class="pill num" id="hdr-speed">L-/5</span>
<span class="pill" id="hdr-view">3D view</span>
<span class="pill num" id="hdr-size">--x--</span>
<span class="pill live" title="No network calls are made during play"><span class="dot"></span>OFFLINE · LOCAL</span>
</div>
</header>
<div class="banner" id="mock-banner">Mock mode. Decisions come from a hand-written heuristic, not the Laya model. Start without <span class="mono">--mock</span> to run the real checkpoint.</div>
<div class="play">
<section id="game">
<div class="stage-box" id="stage-box" data-mode="3d">
<canvas id="stage" aria-label="Snake board, top-down view"></canvas>
<canvas id="stage3d" aria-label="Snake board, 3D view"></canvas>
<div class="glass hud-load" id="load3d">Loading 3D scene…</div>
<div class="glass debug" id="debug3d">
<label><input type="checkbox" data-dbg="path"> Snake path</label>
<label><input type="checkbox" data-dbg="bones"> Skeleton</label>
<label><input type="checkbox" data-dbg="grid"> Grid</label>
<label><input type="checkbox" data-dbg="axes"> World axes</label>
<label><input type="checkbox" data-dbg="orbit"> Orbit camera</label>
<span>FPS <b class="num" id="dbg-fps">-</b></span>
<span>Camera <b class="num" id="dbg-cam">-</b></span>
<span>Snake <b class="num" id="dbg-model">-</b></span>
</div>
<div class="glass hud-score">
<div><div class="k">Score</div><div class="v accent num" id="score">000</div></div>
<div><div class="k">Length</div><div class="v num" id="length">000</div></div>
<div><div class="k">Best</div><div class="v gold num" id="best">000</div></div>
</div>
<div class="glass hud-round">
<div><div class="k num" id="round">ROUND 01</div><div class="v num" id="round-clock">00:00</div></div>
<div><div class="k">SESSION</div><div class="v gold num" id="clock">00:00</div></div>
</div>
<div class="glass hud-think" id="thinking"><span class="spinner"></span><span id="thinking-text">idle</span></div>
<div class="glass hud-trail" id="trail" aria-label="Last moves"></div>
<div class="overlay" id="overlay"><div class="glass msg"><b>GAME OVER</b><span id="overlay-text"></span></div></div>
</div>
<div class="fillrow"><span>Board fill</span><div class="meter"><i id="fill"></i></div><span class="num" id="fill-v">0.0%</span></div>
</section>
<aside class="side">
<section class="card" id="move">
<h2>Current move <span class="aside">model probabilities</span></h2>
<div class="move">
<div class="dpad" aria-hidden="true">
<span class="up" data-d="up"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round"><path d="M12 19V5M5 12l7-7 7 7"/></svg></span>
<span class="left" data-d="left"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round"><path d="M19 12H5M12 19l-7-7 7-7"/></svg></span>
<span class="right" data-d="right"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14M12 5l7 7-7 7"/></svg></span>
<span class="down" data-d="down"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round"><path d="M12 5v14M19 12l-7 7-7-7"/></svg></span>
</div>
<div class="probs" id="probs"></div>
</div>
<div class="exec-line">
<span>Executing <b id="exec">-</b></span>
<span id="exec-tag" class="tag">waiting</span>
</div>
</section>
<section class="card" id="settings">
<h2>Game settings <span class="aside" id="settings-aside"></span></h2>
<div class="set">
<label for="size-sel">Board size</label>
<select id="size-sel"></select>
<span class="k">Game speed</span>
<span class="range"><input type="range" id="speed" min="1" max="5" step="1" aria-label="Game speed"><span class="num" id="speed-v">L3/5</span></span>
<span class="k">View mode</span>
<span class="seg" id="view-seg"><button data-v="2d">2D</button><button data-v="3d">3D</button><button data-v="cinematic">Realistic</button></span>
<label for="tod-sel">Time of day</label>
<select id="tod-sel"><option value="day">Daylight</option><option value="golden">Evening</option><option value="night">Night</option></select>
<span class="k">Food on board</span>
<span class="stepper"><button id="food-dec" aria-label="Fewer foods">−</button><span class="num" id="food-v">-</span><button id="food-inc" aria-label="More foods">+</button></span>
<span class="div"></span>
<span class="k">Obstacles <kbd>O</kbd></span><button class="switch" id="sw-obst" aria-label="Obstacles"></button>
<span class="k">Show grid <kbd>G</kbd></span><button class="switch" id="sw-grid" aria-label="Show grid"></button>
<span class="k">Camera follow <kbd>F</kbd></span><button class="switch" id="sw-follow" aria-label="Camera follow"></button>
<span class="k">Safety shield <kbd>S</kbd></span><button class="switch" id="sw-shield" aria-label="Safety shield"></button>
<span class="k">Auto-restart <kbd>A</kbd></span><button class="switch" id="sw-auto" aria-label="Auto-restart"></button>
</div>
<div class="actions">
<button id="btn-pause"><span id="pause-label">Pause</span><kbd>Space</kbd></button>
<button id="btn-reset" class="danger">New round<kbd>R</kbd></button>
</div>
</section>
</aside>
</div>
<div class="tiles" id="stats">
<div class="tile"><div class="ic"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="6" y="6" width="12" height="12" rx="2"/><path d="M9 2v4M15 2v4M9 18v4M15 18v4M2 9h4M2 15h4M18 9h4M18 15h4"/></svg></div><div class="body"><div class="k">Decisions</div><div class="v num" id="t-dec">0</div><div class="s" id="t-dec-s">this session</div></div></div>
<div class="tile"><div class="ic"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="13" r="8"/><path d="M12 9v4l2 2M9 2h6"/></svg></div><div class="body"><div class="k">Avg latency</div><div class="v num" id="t-avg">-</div><div class="s" id="t-range">range -</div></div></div>
<div class="tile"><div class="ic"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 3v18h18"/><path d="M7 15l4-4 3 3 5-6"/></svg></div><div class="body"><div class="k">p50 latency</div><div class="v num" id="t-p50">-</div><div class="s" id="t-win">last 0 moves</div></div></div>
<div class="tile"><div class="ic"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 3v18h18"/><path d="M7 16l3-7 3 4 5-9"/></svg></div><div class="body"><div class="k">p95 latency</div><div class="v num" id="t-p95">-</div><div class="s">tail of the window</div></div></div>
<div class="tile"><div class="ic"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><ellipse cx="9" cy="6" rx="1.8" ry="4.5"/><ellipse cx="15" cy="6" rx="1.8" ry="4.5"/><circle cx="12" cy="15" r="6"/></svg></div><div class="body"><div class="k">Food eaten</div><div class="v num" id="t-food">0</div><div class="s" id="t-food-s">-</div></div></div>
<div class="tile"><div class="ic"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg></div><div class="body"><div class="k">Shield rate</div><div class="v num" id="t-shield">-</div><div class="s">overrides / decision</div></div></div>
<div class="tile"><div class="ic"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 22V4M4 4h13l-2 4 2 4H4"/></svg></div><div class="body"><div class="k">Rounds</div><div class="v num" id="t-rounds">0</div><div class="s" id="t-deaths">-</div></div></div>
<div class="tile"><div class="ic"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M8 21h8M12 17v4M7 4h10v5a5 5 0 0 1-10 0V4z"/><path d="M17 5h3v2a3 3 0 0 1-3 3M7 5H4v2a3 3 0 0 0 3 3"/></svg></div><div class="body"><div class="k">Mean score</div><div class="v num" id="t-mean">-</div><div class="s" id="t-mean-s">finished rounds</div></div></div>
</div>
<div class="details" id="model">
<section class="card">
<h2>Safety <span class="aside">game-code heuristics, not the model</span></h2>
<div class="gauge">
<div class="row"><span class="k">Dead-end risk</span><span class="num" id="risk-v">0.00</span></div>
<div class="meter"><i id="risk"></i></div>
</div>
<div class="gauge">
<div class="row"><span class="k">Any food reachable</span><span class="num" id="reach-v">-</span></div>
<div class="meter"><i id="reach"></i></div>
</div>
<div class="kv">
<span class="k">Shield</span><span class="v" id="shield-v">-</span>
<span class="k">Overrides this round</span><span class="v num" id="ovr-round">0</span>
<span class="k">Overrides total</span><span class="v num" id="ovr-total">0</span>
</div>
</section>
<section class="card">
<h2>Engine</h2>
<div class="kv">
<span class="k">Model</span><span class="v mono" id="e-model">-</span>
<span class="k">Runtime</span><span class="v mono" id="e-engine">-</span>
<span class="k">Device</span><span class="v mono" id="e-device">-</span>
<span class="k">Last inference</span><span class="v num" id="e-last">-</span>
<span class="k">Decisions / s</span><span class="v num" id="e-dps">-</span>
<span class="k">Output tokens</span><span class="v num">0</span>
<span class="k">Model errors</span><span class="v num" id="e-err">0</span>
</div>
<p class="hint" id="e-error"></p>
</section>
</div>
<div class="charts" id="charts">
<section class="card">
<h2>Inference latency <span class="aside num" id="c-lat-aside">ms per decision</span></h2>
<div class="chart"><canvas id="c-lat"></canvas><div class="tip"></div></div>
</section>
<section class="card">
<h2>Model confidence <span class="aside">probability of the model's top pick</span></h2>
<div class="chart"><canvas id="c-conf"></canvas><div class="tip"></div></div>
</section>
<section class="card">
<h2>Score by round <span class="aside" id="c-score-aside">last rounds</span></h2>
<div class="chart"><canvas id="c-score"></canvas><div class="tip"></div></div>
</section>
</div>
<div class="tables" id="history">
<section class="card">
<h2>Round history <span class="aside" id="rounds-aside"></span></h2>
<div class="tbl-wrap">
<table>
<thead><tr><th class="l">Round</th><th>Board</th><th>Score</th><th>Length</th><th>Moves</th><th>Overrides</th><th>Avg ms</th><th>Time</th><th class="l">End</th></tr></thead>
<tbody id="rounds"></tbody>
</table>
</div>
</section>
<section class="card">
<h2>Decision log <span class="aside">newest first · U D L R</span></h2>
<div class="tbl-wrap">
<table>
<thead><tr><th class="l">#</th><th class="l">Model</th><th class="l">Executed</th><th>Top p</th><th>Dist</th><th>Risk</th><th>ms</th><th>Score</th></tr></thead>
<tbody id="log"></tbody>
</table>
</div>
</section>
</div>
<footer>
<div class="keys">
<span class="srv"><kbd>Space</kbd> pause</span><span class="srv"><kbd>↑</kbd><kbd>↓</kbd> speed</span><span class="srv"><kbd>+</kbd><kbd>−</kbd> board</span>
<span><kbd>V</kbd> 2D / 3D / Realistic</span><span class="srv"><kbd>O</kbd> obstacles</span><span class="srv"><kbd>S</kbd> shield</span><span class="srv"><kbd>R</kbd> new round</span>
</div>
<span>Probabilities and timings are the engine's real output. The scene only draws the server's state.</span>
</footer>
</div>
</div>
<script type="module" src="/js/app.js"></script>
</body>
</html>