GPT-6 Luna
by OpenAIReasoningClean
Running live in a sandboxed frame. Click inside it before using the keyboard.
How it went
- Total time
- 38.1 s
- Cost
- $0.0028
- Output tokens
- 5,560
- Spent thinking
- 0 tokens
- Speed
- 149.7 tok/s
- Console errors
- 0
- First token after
- 932 ms
- First visible text after
- 932 ms
- Prompt tokens
- 493
- Code size
- 16.5 KB
- Finish reason
- stop
- Served by
- OpenAI
- Model id
openai/gpt-6-luna- Reasoning sent
{"effort":"none"}- Max output
- 64,000 tokens
- Generated
- Nov 10, 2026
What it was asked
The prompt
Build a polished Snake game in a single HTML file using vanilla JavaScript and a <canvas>. Requirements: - A grid of at least 20×20 cells, centered on the page. - Arrow keys and WASD control the snake. Quick successive key presses must both be honored, but the snake can never reverse onto itself. - Eating food adds 10 points and grows the snake by one cell; new food never spawns on the snake. - Game over when the snake hits a wall or itself, with an overlay showing the score; Space or Enter restarts, P pauses. - Visual polish: smooth movement, a snake with a distinct head and eyes facing its direction, and a small particle burst when eating. - Display the current score and the best score of the session.
System prompt, sent with every prompt
You are generating a self-contained web demo. It will be rendered automatically in a sandboxed iframe and compared side by side with the output of other AI models given the exact same task. Output rules: - Reply with exactly ONE fenced code block containing the complete file: ```html (or ```svg when the task asks for an SVG). No text before or after the code block. - Everything must be inline: CSS in <style>, JavaScript in <script>. No external files, images, fonts or network requests, except the CDN URLs explicitly allowed by the task. - Do not use localStorage, sessionStorage, cookies, IndexedDB, alert(), confirm(), prompt() or window.open(): they are blocked in the sandbox and will throw. - The page must look right in a 1280×800 viewport and adapt gracefully down to 375px wide. No horizontal scrollbar. - Something meaningful must be visible on first paint: a screenshot is taken automatically about 3 seconds after load, without any interaction. - Everything that makes the demo interesting must be visible within the first seconds: nothing hidden behind a high score, a later level or a long play session. - Games must start in a self-playing demo mode (a simple AI plays) behind a "Click or press a key to start" message, so the game can be judged in action without playing. - The iframe only receives keyboard events after the user clicks it: if the demo uses the keyboard, display a clear "Click or press a key to start" message. - The code must run with zero errors in the console of the latest Chrome.
Code run-1.html · 438 lines
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Snake — Arcade</title>
<style>
:root {
color-scheme: dark;
--bg: #0b1115;
--panel: #111b20;
--line: rgba(185, 220, 211, .1);
--muted: #869b9a;
--mint: #76f2b2;
--mint2: #37ce91;
--coral: #ff706b;
}
* { box-sizing: border-box; }
html, body { margin: 0; min-height: 100%; }
body {
min-height: 100vh;
display: grid;
place-items: center;
padding: 25px;
color: #f1f7f4;
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
background:
radial-gradient(ellipse at 50% 40%, rgba(30, 78, 63, .2), transparent 54%),
var(--bg);
}
.app { width: min(100%, 760px); }
.topline {
display: flex;
justify-content: space-between;
align-items: center;
margin: 0 2px 17px;
}
.brand { display: flex; align-items: center; gap: 11px; }
.mark {
width: 35px; height: 35px; display: grid; place-items: center;
color: var(--mint); border: 1px solid rgba(118,242,178,.26);
border-radius: 11px; background: rgba(118,242,178,.07);
}
.mark svg { width: 20px; height: 20px; }
.title { font-size: 16px; font-weight: 750; letter-spacing: .02em; }
.subtitle { color: #829390; font-size: 11px; margin-top: 2px; letter-spacing: .06em; }
.live { color: #91a5a0; display: flex; align-items: center; gap: 8px; font-size: 11px; letter-spacing: .12em; }
.live-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--mint); box-shadow: 0 0 12px #76f2b288; }
.scorebar {
display: flex; gap: 10px; margin-bottom: 12px;
}
.stat {
background: linear-gradient(145deg, rgba(21,34,38,.96), rgba(14,24,28,.96));
border: 1px solid var(--line); border-radius: 13px;
min-width: 132px; padding: 10px 15px 9px;
box-shadow: 0 8px 24px rgba(0,0,0,.12);
}
.stat-label { font-size: 9px; color: #8a9b98; letter-spacing: .17em; font-weight: 700; }
.stat-value { font-size: 22px; line-height: 1.2; font-weight: 750; letter-spacing: .04em; font-variant-numeric: tabular-nums; }
.stat-value small { font-size: 11px; color: #829390; font-weight: 600; }
.board-wrap {
position: relative; overflow: hidden; border-radius: 17px;
background: #101a1d;
border: 1px solid rgba(162, 205, 191, .15);
box-shadow: 0 24px 70px rgba(0,0,0,.36), 0 0 50px rgba(39,126,89,.07);
line-height: 0;
}
canvas { display: block; width: 100%; aspect-ratio: 1; }
.overlay {
position: absolute; inset: 0; display: grid; place-items: center;
background: rgba(6, 13, 15, .55);
backdrop-filter: blur(4px);
transition: opacity .22s ease, visibility .22s ease;
line-height: normal;
}
.overlay.hidden { opacity: 0; visibility: hidden; pointer-events: none; }
.card {
width: min(320px, calc(100% - 36px)); padding: 24px 24px 20px;
border: 1px solid rgba(172,218,199,.19); border-radius: 18px;
background: rgba(16,27,29,.92); text-align: center;
box-shadow: 0 16px 55px rgba(0,0,0,.36);
}
.card-icon {
width: 44px; height: 44px; margin: 0 auto 13px; display: grid; place-items: center;
color: var(--mint); border-radius: 14px; background: rgba(118,242,178,.1);
border: 1px solid rgba(118,242,178,.2);
}
.card-icon svg { width: 23px; height: 23px; }
.card h1 { font-size: 22px; line-height: 1.2; margin: 0 0 7px; letter-spacing: -.03em; }
.card p { color: #a2b2ad; font-size: 12px; line-height: 1.55; margin: 0 0 16px; }
.keycap {
color: #e9f5ef; border: 1px solid rgba(207,235,221,.2);
background: rgba(215,245,227,.07); border-radius: 5px;
padding: 2px 6px; font-size: 10px; font-weight: 700;
box-shadow: 0 2px 0 rgba(0,0,0,.3);
}
.hint { color: #b0c1ba; font-size: 11px; }
.footer {
display: flex; justify-content: space-between; align-items: center;
margin: 13px 3px 0; color: #859895; font-size: 11px;
}
.controls { display: flex; gap: 13px; align-items: center; }
.controls span { display: inline-flex; gap: 5px; align-items: center; }
.pause-note { color: #6f8580; }
@media (max-width: 520px) {
body { padding: 16px; }
.topline { margin-bottom: 12px; }
.subtitle { font-size: 10px; }
.stat { flex: 1; min-width: 0; padding: 9px 12px; }
.stat-value { font-size: 20px; }
.footer { align-items: flex-start; gap: 10px; flex-direction: column; }
.controls { gap: 10px; flex-wrap: wrap; }
}
</style>
</head>
<body>
<main class="app">
<header class="topline">
<div class="brand">
<div class="mark" aria-hidden="true">
<svg viewBox="0 0 24 24" fill="none"><path d="M5 8h7a4 4 0 0 1 0 8H8a3 3 0 0 1 0-6h8a3 3 0 0 1 0 6h-1" stroke="currentColor" stroke-width="2.2" stroke-linecap="round"/><circle cx="16.2" cy="10" r="1" fill="currentColor"/></svg>
</div>
<div><div class="title">SNAKE</div><div class="subtitle">ARCADE CLASSIC <span style="color:#51635f">/</span> No. 01</div></div>
</div>
<div class="live"><span class="live-dot"></span> <span id="status">READY</span></div>
</header>
<section class="scorebar" aria-label="Score">
<div class="stat"><div class="stat-label">SCORE</div><div class="stat-value"><span id="score">000</span> <small>pts</small></div></div>
<div class="stat"><div class="stat-label">BEST SESSION</div><div class="stat-value" id="best">000</div></div>
</section>
<div class="board-wrap">
<canvas id="game" aria-label="Snake game board"></canvas>
<div class="overlay" id="overlay">
<div class="card">
<div class="card-icon" id="cardIcon">
<svg viewBox="0 0 24 24" fill="none"><path d="M5 8h7a4 4 0 0 1 0 8H8a3 3 0 0 1 0-6h8a3 3 0 0 1 0 6h-1" stroke="currentColor" stroke-width="2.1" stroke-linecap="round"/><circle cx="16.2" cy="10" r="1" fill="currentColor"/></svg>
</div>
<h1 id="overlayTitle">Ready to play?</h1>
<p id="overlayText">Guide the snake, collect the glowing fruit, and see how long you can last.</p>
<div class="hint" id="overlayHint"><span class="keycap">CLICK</span> or press any key to start</div>
</div>
</div>
</div>
<footer class="footer">
<div class="controls">
<span><span class="keycap">↑</span><span class="keycap">←</span><span class="keycap">↓</span><span class="keycap">→</span> <span>or WASD</span></span>
<span><span class="keycap">P</span> pause</span>
<span><span class="keycap">SPACE</span> restart</span>
</div>
<div class="pause-note">Take a breath. Then chase.</div>
</footer>
</main>
<script>
(() => {
const canvas = document.getElementById('game');
const ctx = canvas.getContext('2d');
const overlay = document.getElementById('overlay');
const titleEl = document.getElementById('overlayTitle');
const textEl = document.getElementById('overlayText');
const hintEl = document.getElementById('overlayHint');
const statusEl = document.getElementById('status');
const scoreEl = document.getElementById('score');
const bestEl = document.getElementById('best');
const N = 28;
let size = 560, cell = size / N, dpr = 1;
let snake, food, dir, turnQueue, score, best = 0, state = 'ready';
let lastStep = 0, stepMs = 126, accumulator = 0;
let particles = [];
let animTime = 0;
let prevSnake = [];
function resize() {
const width = Math.min(canvas.parentElement.clientWidth, 680);
dpr = Math.min(window.devicePixelRatio || 1, 2);
size = width;
canvas.width = Math.round(size * dpr);
canvas.height = Math.round(size * dpr);
canvas.style.width = size + 'px';
canvas.style.height = size + 'px';
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
cell = size / N;
draw();
}
window.addEventListener('resize', resize);
function reset() {
snake = [{x:13,y:14},{x:12,y:14},{x:11,y:14},{x:10,y:14},{x:9,y:14}];
prevSnake = snake.map(p => ({...p}));
dir = {x:1,y:0};
turnQueue = [];
score = 0;
particles = [];
placeFood();
scoreEl.textContent = '000';
state = 'playing';
statusEl.textContent = 'IN PLAY';
overlay.classList.add('hidden');
lastStep = performance.now();
accumulator = 0;
draw();
}
function placeFood() {
const free = [];
for (let y=0;y<N;y++) for (let x=0;x<N;x++)
if (!snake.some(s => s.x===x && s.y===y)) free.push({x,y});
food = free[Math.floor(Math.random()*free.length)];
}
function setOverlay(title, text, hint) {
titleEl.textContent = title;
textEl.textContent = text;
hintEl.innerHTML = hint;
overlay.classList.remove('hidden');
}
function showReady() {
state = 'ready';
statusEl.textContent = 'READY';
setOverlay('Ready to play?', 'Guide the snake, collect the glowing fruit, and see how long you can last.', '<span class="keycap">CLICK</span> or press any key to start');
}
function requestTurn(x,y) {
if (state === 'over') return;
if (state === 'ready') { reset(); if (x || y) enqueue(x,y); return; }
if (state === 'paused') {
if (x || y) { state='playing'; statusEl.textContent='IN PLAY'; overlay.classList.add('hidden'); enqueue(x,y); lastStep=performance.now(); }
return;
}
enqueue(x,y);
}
function enqueue(x,y) {
const base = turnQueue.length ? turnQueue[turnQueue.length-1] : dir;
if (base.x === -x && base.y === -y) return;
if (base.x === x && base.y === y) return;
if (turnQueue.length < 2) turnQueue.push({x,y});
}
document.addEventListener('keydown', e => {
const k = e.key.toLowerCase();
const map = {
arrowup:[0,-1], w:[0,-1], arrowdown:[0,1], s:[0,1],
arrowleft:[-1,0], a:[-1,0], arrowright:[1,0], d:[1,0]
};
if (map[k]) { e.preventDefault(); requestTurn(...map[k]); return; }
if (k === 'p') {
e.preventDefault();
if (state === 'playing') {
state='paused'; statusEl.textContent='PAUSED';
setOverlay('Paused', 'The board will be right here when you are ready.', '<span class="keycap">P</span> or any direction to continue');
} else if (state === 'paused') {
state='playing'; statusEl.textContent='IN PLAY'; overlay.classList.add('hidden'); lastStep=performance.now();
}
return;
}
if (k === ' ' || k === 'enter') {
e.preventDefault();
if (state !== 'playing') reset();
}
});
overlay.addEventListener('click', () => { if (state !== 'playing') reset(); });
function gameOver() {
state = 'over';
statusEl.textContent = 'GAME OVER';
setOverlay('Game over', `You scored ${score} points. ${score > 0 ? 'Nice run.' : 'Ready for another go?'}`, '<span class="keycap">SPACE</span> or <span class="keycap">ENTER</span> to play again');
}
function tick() {
if (turnQueue.length) dir = turnQueue.shift();
const head = snake[0];
const next = {x:head.x+dir.x, y:head.y+dir.y};
const eating = food && next.x===food.x && next.y===food.y;
const bodyToCheck = eating ? snake : snake.slice(0,-1);
if (next.x<0 || next.y<0 || next.x>=N || next.y>=N ||
bodyToCheck.some(s => s.x===next.x && s.y===next.y)) {
gameOver();
return;
}
prevSnake = snake.map(p=>({...p}));
snake.unshift(next);
if (eating) {
score += 10;
scoreEl.textContent = String(score).padStart(3,'0');
best = Math.max(best, score);
bestEl.textContent = String(best).padStart(3,'0');
burst(food.x,food.y);
placeFood();
stepMs = Math.max(78, 126 - score * .22);
} else snake.pop();
}
function burst(x,y) {
const cx=(x+.5)*cell, cy=(y+.5)*cell;
for(let i=0;i<17;i++){
const a=Math.random()*Math.PI*2, speed=35+Math.random()*100;
particles.push({x:cx,y:cy,vx:Math.cos(a)*speed,vy:Math.sin(a)*speed,life:.42+Math.random()*.3,max:.72,r:2+Math.random()*2});
}
}
function roundedRect(x,y,w,h,r) {
ctx.beginPath();
ctx.roundRect(x,y,w,h,r);
}
function draw() {
if (!ctx || !snake) return;
const t = performance.now();
ctx.clearRect(0,0,size,size);
ctx.fillStyle = '#101a1d';
ctx.fillRect(0,0,size,size);
// Subtle board glow and crisp, low-contrast grid.
const glow=ctx.createRadialGradient(size*.48,size*.45,0,size*.48,size*.45,size*.78);
glow.addColorStop(0,'rgba(32,75,59,.12)');
glow.addColorStop(1,'rgba(4,12,14,0)');
ctx.fillStyle=glow; ctx.fillRect(0,0,size,size);
ctx.strokeStyle='rgba(162,204,188,.045)';
ctx.lineWidth=1;
for(let i=1;i<N;i++){
const p=Math.round(i*cell)+.5;
ctx.beginPath();ctx.moveTo(p,0);ctx.lineTo(p,size);ctx.stroke();
ctx.beginPath();ctx.moveTo(0,p);ctx.lineTo(size,p);ctx.stroke();
}
// soft inset edge
ctx.strokeStyle='rgba(202,238,219,.09)';ctx.lineWidth=1;
ctx.strokeRect(.5,.5,size-1,size-1);
if(food){
const fx=(food.x+.5)*cell, fy=(food.y+.5)*cell;
const pulse=1+Math.sin(t*.006)*.09;
ctx.save();
ctx.shadowColor='#ff706b';ctx.shadowBlur=19;
ctx.fillStyle='rgba(255,112,107,.18)';
ctx.beginPath();ctx.arc(fx,fy,cell*.39*pulse,0,Math.PI*2);ctx.fill();
ctx.shadowBlur=10;
ctx.fillStyle='#ff766f';
ctx.beginPath();ctx.arc(fx,fy,cell*.22*pulse,0,Math.PI*2);ctx.fill();
ctx.shadowBlur=0;
ctx.fillStyle='#ffd0a4';
ctx.beginPath();ctx.arc(fx-cell*.055,fy-cell*.06,cell*.065,0,Math.PI*2);ctx.fill();
ctx.strokeStyle='#91d89c';ctx.lineWidth=cell*.075;ctx.lineCap='round';
ctx.beginPath();ctx.moveTo(fx,fy-cell*.17);ctx.quadraticCurveTo(fx+cell*.08,fy-cell*.34,fx+cell*.2,fy-cell*.29);ctx.stroke();
ctx.restore();
}
// Smooth interpolation from previous positions to current positions.
let alpha = state==='playing' ? Math.min(1, accumulator/stepMs) : 1;
const positions = snake.map((p,i)=>{
const old=prevSnake[i] || p;
let x=p.x, y=p.y;
if (old && Math.abs(p.x-old.x)<=1 && Math.abs(p.y-old.y)<=1) {
x=old.x+(p.x-old.x)*alpha;
y=old.y+(p.y-old.y)*alpha;
}
return {x,y};
});
for(let i=positions.length-1;i>=1;i--){
const p=positions[i], fade=i/(positions.length+1);
const pad=cell*.115;
const x=p.x*cell+pad,y=p.y*cell+pad,w=cell-pad*2,h=cell-pad*2;
const light=1-fade*.37;
ctx.fillStyle=`rgb(${Math.round(54*light)},${Math.round(205*light)},${Math.round(136*light)})`;
ctx.shadowColor='rgba(55,220,145,.18)';ctx.shadowBlur=7;
roundedRect(x,y,w,h,cell*.25);ctx.fill();
ctx.shadowBlur=0;
ctx.fillStyle='rgba(211,255,226,.16)';
roundedRect(x+cell*.08,y+cell*.07,w*.53,cell*.07,cell*.035);ctx.fill();
}
const h=positions[0];
const hx=(h.x+.5)*cell, hy=(h.y+.5)*cell;
ctx.save();
ctx.shadowColor='rgba(93,255,176,.43)';ctx.shadowBlur=14;
const headGrad=ctx.createLinearGradient(hx-cell*.35,hy-cell*.35,hx+cell*.35,hy+cell*.35);
headGrad.addColorStop(0,'#a2ffd0');headGrad.addColorStop(1,'#55df9c');
ctx.fillStyle=headGrad;
roundedRect(h.x*cell+cell*.055,h.y*cell+cell*.055,cell*.89,cell*.89,cell*.31);ctx.fill();
ctx.restore();
// Eyes face the direction of travel.
const fx=dir.x, fy=dir.y, sideX=-fy, sideY=fx;
const eyeForward=cell*.17, eyeSide=cell*.19;
const eyes=[
{x:hx+fx*eyeForward+sideX*eyeSide,y:hy+fy*eyeForward+sideY*eyeSide},
{x:hx+fx*eyeForward-sideX*eyeSide,y:hy+fy*eyeForward-sideY*eyeSide}
];
for(const e of eyes){
ctx.fillStyle='#10241c';
ctx.beginPath();ctx.arc(e.x,e.y,cell*.085,0,Math.PI*2);ctx.fill();
ctx.fillStyle='rgba(255,255,255,.9)';
ctx.beginPath();ctx.arc(e.x+fx*cell*.02-cell*.018,e.y+fy*cell*.02-cell*.018,cell*.025,0,Math.PI*2);ctx.fill();
}
// Food particles.
const dt=1/60;
particles=particles.filter(p=>p.life>0);
for(const p of particles){
p.life-=dt;p.x+=p.vx*dt;p.y+=p.vy*dt;p.vx*=.97;p.vy*=.97;
ctx.globalAlpha=Math.max(0,p.life/p.max);
ctx.fillStyle='#ffaaa0';
ctx.beginPath();ctx.arc(p.x,p.y,p.r,0,Math.PI*2);ctx.fill();
}
ctx.globalAlpha=1;
}
function frame(now) {
if (state==='playing') {
let elapsed=Math.min(now-lastStep,100);
lastStep=now;
accumulator+=elapsed;
while(accumulator>=stepMs && state==='playing'){
accumulator-=stepMs;
tick();
}
}
draw();
requestAnimationFrame(frame);
}
// Self-playing attract demo, visible immediately behind the start prompt.
function demoPaint() {
snake = [
{x:14,y:15},{x:13,y:15},{x:12,y:15},{x:11,y:15},{x:10,y:15},
{x:9,y:15},{x:8,y:15},{x:7,y:15}
];
prevSnake=snake.map(p=>({...p}));
dir={x:1,y:0};
food={x:20,y:15};
score=0;scoreEl.textContent='000';bestEl.textContent='000';
draw();
}
demoPaint();
resize();
requestAnimationFrame(frame);
})();
</script>
</body>
</html>