GPT-5 Nano
by OpenAIReasoning1 console error
Running live in a sandboxed frame. Click inside it before using the keyboard.
How it went
- Total time
- 29.9 s
- Cost
- $0.0018
- Output tokens
- 4,317
- Spent thinking
- 0 tokens
- Speed
- 149.6 tok/s
- Console errors
- 1
- First token after
- 1.1 s
- First visible text after
- 1.1 s
- Prompt tokens
- 493
- Code size
- 13.6 KB
- Finish reason
- stop
- Served by
- OpenAI
- Model id
openai/gpt-5-nano- Reasoning sent
{"effort":"minimal"}- Max output
- 128,000 tokens
- Generated
- Nov 12, 2026
Console errors
Caught in the first 3 seconds after load, in a sandboxed iframe like the one above.
Cannot read properties of undefined (reading 'x')
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 · 538 lines
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Snake - Vanilla JS</title>
<style>
:root{
--bg: #0e1020;
--panel: rgba(20,24,40,.9);
--grid: rgba(255,255,255,.08);
--snake: #7bd389;
--snakeHead: #2bd07f;
--food: #ff6b6b;
--overlay: rgba(0,0,0,.66);
--text: #e6f0ff;
--shadow: rgba(0,0,0,.25);
}
html, body {
height: 100%;
margin: 0;
background: radial-gradient(circle at 50% -10%, #1a1f3a 0%, #0e1020 40%, #0e1020 100%), #0e1020;
font-family: system-ui, -apple-system, "Segoe UI", Roboto, Inter, Arial;
color: var(--text);
overflow: hidden;
}
.wrap {
height: 100%;
display: grid;
grid-template-rows: auto 1fr auto;
place-items: center;
padding: 20px;
}
.card {
background: linear-gradient(180deg, rgba(255,255,255,.04), rgba(255,255,255,.02) 60%), var(--panel);
border-radius: 14px;
padding: 14px;
box-shadow: 0 10px 30px rgba(0,0,0,.4);
display: grid;
gap: 12px;
align-items: center;
justify-items: center;
max-width: 92vw;
width: 860px;
}
canvas {
display: block;
background: #0a0f1a;
border-radius: 8px;
box-shadow: inset 0 0 0 1px rgba(255,255,255,.04), 0 8px 20px rgba(0,0,0,.35);
}
.hud {
display: flex;
gap: 14px;
align-items: center;
justify-content: space-between;
width: 100%;
flex-wrap: wrap;
}
.badge {
padding: 6px 12px;
border-radius: 999px;
background: rgba(0,0,0,.25);
border: 1px solid rgba(255,255,255,.08);
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas;
font-size: 14px;
letter-spacing: .4px;
}
.title {
font-size: 18px;
font-weight: 600;
letter-spacing: .4px;
}
.hint {
font-size: 13px;
opacity: .9;
}
.overlay {
position: fixed;
inset: 0;
display: grid;
place-items: center;
background: var(--overlay);
z-index: 5;
}
.panel {
background: rgba(15,18,40,.95);
border-radius: 12px;
padding: 28px 28px;
text-align: center;
max-width: 420px;
box-shadow: 0 6px 20px rgba(0,0,0,.5);
}
.panel h2 {
margin: 0 0 8px 0;
font-size: 28px;
}
.panel p {
margin: 6px 0 14px 0;
color: #d9e6ff;
font-size: 14px;
}
.panel .score {
font-family: ui-mloose, monospace;
font-weight: 700;
font-size: 32px;
margin: 6px 0 12px 0;
}
.btns {
display: flex;
gap: 10px;
justify-content: center;
flex-wrap: wrap;
}
button.btn {
appearance: none;
border: 0;
border-radius: 999px;
padding: 12px 18px;
background: #2b9c8e;
color: white;
font-weight: 600;
cursor: pointer;
box-shadow: 0 4px 10px rgba(0,0,0,.25);
}
button.btn.secondary {
background: #3a3f6b;
}
@media (max-width: 720px){
.card { width: 100%; padding: 12px; }
canvas { width: 100%; height: auto; }
}
</style>
</head>
<body>
<div class="wrap">
<div class="card" aria-label="Snake game board">
<div class="hud" id="hud">
<div class="badge" id="scoreLabel">Score: 0</div>
<div class="badge" id="bestLabel">Best: 0</div>
<div class="badge" id="stateLabel" style="margin-left:auto; opacity:.9">Click or press a key to start</div>
</div>
<canvas id="board" width="640" height="640" aria-label="Snake game canvas"></canvas>
<div class="hint" id="hint" style="font-size:12px; opacity:.8; text-align:center;">
Use Arrow Keys or WASD to move. Space or Enter to restart after game over. P to pause.
</div>
</div>
</div>
<div class="overlay" id="overlay" style="display:none" aria-hidden="true">
<div class="panel" role="dialog" aria-label="Game over dialog">
<h2>Game Over</h2>
<p>Your score</p>
<div class="score" id="overlayScore">0</div>
<div class="btns" style="margin-top:6px;">
<button class="btn" id="restartBtn">Restart (Space / Enter)</button>
<button class="btn secondary" id="closeBtn">Close</button>
</div>
</div>
</div>
<script>
(() => {
// Configuration
const GRID = 20; // grid cells per row/col (minimum 20)
const CELL = 0; // will compute from canvas size
const INITIAL_SPEED = 6; // steps per second
const ACCEL_PER_EAT = 0; // could speed up later
const SCORE_PER_EAT = 10;
// Canvas setup
const canvas = document.getElementById('board');
const ctx = canvas.getContext('2d');
// Responsive sizing: keep square grid centered
function resizeCanvas() {
const wrapW = Math.min(window.innerWidth * 0.92, 860);
const size = Math.floor(Math.min(wrapW, window.innerHeight * 0.8));
// ensure grid cells are integers
const cellSize = Math.floor(size / GRID);
const boardSize = cellSize * GRID;
canvas.style.width = boardSize + 'px';
canvas.style.height = boardSize + 'px';
canvas.width = boardSize;
canvas.height = boardSize;
}
window.addEventListener('resize', resizeCanvas, {passive:true});
resizeCanvas();
// Game state
let gridSize = GRID;
let cellSize = canvas.width / gridSize;
let snake = [];
let dir = {x:1, y:0}; // current direction
let nextDir = {x:1, y:0};
let food = null;
let growing = 0;
let score = 0;
let best = 0;
let speed = INITIAL_SPEED;
let frameAccumulator = 0;
let paused = false;
let running = false;
let gameOver = false;
let lastTime = 0;
const keys = {};
// Initialize
function resetGame() {
// center snake
const mid = Math.floor(gridSize/2);
snake = [{x:mid-1, y:mid}, {x:mid, y:mid}, {x:mid+1, y:mid}];
dir = {x:1, y:0};
nextDir = {x:1, y:0};
placeFood();
score = 0;
speed = INITIAL_SPEED;
frameAccumulator = 0;
paused = false;
gameOver = false;
running = true;
updateHUD();
}
function updateHUD() {
document.getElementById('scoreLabel').textContent = 'Score: ' + score;
document.getElementById('bestLabel').textContent = 'Best: ' + best;
document.getElementById('stateLabel').textContent = gameOver ? 'Game Over' : (paused ? 'Paused' : 'Running');
}
function placeFood() {
// choose a cell not in snake
let empty = [];
for (let y = 0; y < gridSize; y++) {
for (let x = 0; x < gridSize; x++) {
if (!snake.some(s => s.x === x && s.y === y)) {
empty.push({x, y});
}
}
}
if (empty.length === 0) {
// win condition (unlikely)
food = null;
return;
}
food = empty[Math.floor(Math.random() * empty.length)];
}
// Input handling
window.addEventListener('keydown', (e) => {
if (e.repeat) return;
const k = e.key.toLowerCase();
if (k === 'arrowup') queueDir(0,-1);
else if (k === 'arrowdown') queueDir(0,1);
else if (k === 'arrowleft') queueDir(-1,0);
else if (k === 'arrowright') queueDir(1,0);
else if (k === 'w') queueDir(0,-1);
else if (k === 's') queueDir(0,1);
else if (k === 'a') queueDir(-1,0);
else if (k === 'd') queueDir(1,0);
else if (k === ' ') {
e.preventDefault();
if (!running) {
startGame();
} else {
togglePause();
}
} else if (k === 'enter') {
if (gameOver) restartFromOverlay();
else if (!running) startGame();
else togglePause();
} else if (k === 'p') {
togglePause();
}
}, {passive:true});
function queueDir(dx, dy) {
// prevent reverse
if (-dx === dir.x && -dy === dir.y) return;
nextDir = {x:dx, y:dy};
}
// Intro instruction
function showHintIfNeeded() {
// Show overlay if not started
}
// Rendering
function drawGrid() {
ctx.fillStyle = 'transparent';
// subtle grid lines
ctx.strokeStyle = 'rgba(255,255,255,.05)';
ctx.lineWidth = 1;
for (let i=0; i<=gridSize; i++) {
// vertical
ctx.beginPath();
ctx.moveTo(i*cellSize, 0);
ctx.lineTo(i*cellSize, gridSize*cellSize);
ctx.stroke();
// horizontal
ctx.beginPath();
ctx.moveTo(0, i*cellSize);
ctx.lineTo(gridSize*cellSize, i*cellSize);
ctx.stroke();
}
}
function clearBoard() {
ctx.fillStyle = '#0a0f1a';
ctx.fillRect(0,0, canvas.width, canvas.height);
}
function drawSnake() {
for (let i = 0; i < snake.length; i++) {
const s = snake[i];
const px = s.x * cellSize;
const py = s.y * cellSize;
// body
ctx.fillStyle = i === snake.length - 1 ? '#6ce17b' : '#4fb66b';
ctx.fillRect(px+2, py+2, cellSize-4, cellSize-4);
}
// head with eyes facing direction
const head = snake[snake.length-1];
const px = head.x * cellSize;
const py = head.y * cellSize;
// eyes
ctx.fillStyle = '#113';
const ex = head.x + (dir.x>0?1:0);
const ey = head.y + (dir.y>0?1:0);
// simple eyes as two small blacks on front
ctx.fillRect(px + cellSize*0.25, py + cellSize*0.25, 3, 3);
ctx.fillRect(px + cellSize*0.75 - 3, py + cellSize*0.25, 3, 3);
// decorative highlight
ctx.fillStyle = '#a9ffd8';
}
function drawFood() {
if (!food) return;
const px = food.x * cellSize;
const py = food.y * cellSize;
// glowing rounded square
const r = 6;
ctx.fillStyle = '#ff5a5a';
ctx.strokeStyle = '#fff';
ctx.lineWidth = 2;
roundRect(ctx, px+2, py+2, cellSize-4, cellSize-4, r, true, true);
}
// helper for rounded rect
function roundRect(ctx, x, y, w, h, r, fill, stroke) {
if (w < 2*r) r = w/2;
if (h < 2*r) r = h/2;
ctx.beginPath();
ctx.moveTo(x+r, y);
ctx.arcTo(x+w, y, x+w, y+h, r);
ctx.arcTo(x+w, y+h, x, y+h, r);
ctx.arcTo(x, y+h, x, y, r);
ctx.arcTo(x, y, x+w, y, r);
ctx.closePath();
if (fill) ctx.fill();
if (stroke) ctx.stroke();
}
// Particles burst when eating
const particles = [];
function burst(x, y) {
for (let i=0; i<12; i++) {
const a = Math.random()*Math.PI*2;
const speed = 0.8 + Math.random()*1.6;
particles.push({
x: x, y: y,
vx: Math.cos(a)*speed,
vy: Math.sin(a)*speed,
life: 40 + Math.random()*20
});
}
}
function updateParticles() {
for (let i = particles.length -1; i >= 0; i--) {
const p = particles[i];
p.x += p.vx;
p.y += p.vy;
p.vy += 0.04; // gravity-ish
p.life--;
if (p.life <= 0) particles.splice(i,1);
}
}
function drawParticles() {
for (const p of particles) {
ctx.fillStyle = 'rgba(255,230,160,' + (p.life/60).toFixed(2) + ')';
ctx.fillRect(p.x, p.y, 2, 2);
}
}
// Game loop
function step(dt) {
if (!running || paused) return;
frameAccumulator += dt;
const interval = 1 / speed;
while (frameAccumulator >= interval) {
frameAccumulator -= interval;
update();
}
}
function update() {
// apply direction
dir = nextDir;
// compute next head
const head = snake[snake.length-1];
const nx = head.x + dir.x;
const ny = head.y + dir.y;
// wall collision
if (nx < 0 || ny < 0 || nx >= gridSize || ny >= gridSize) {
endGame();
return;
}
// self collision
if (snake.some(s => s.x === nx && s.y === ny)) {
endGame();
return;
}
// move
snake.push({x: nx, y: ny});
if (growing > 0) {
growing--;
} else {
snake.shift();
}
// food check
if (food && nx === food.x && ny === food.y) {
score += SCORE_PER_EAT;
growing += 1;
placeFood();
burst(nx*cellSize + cellSize/2, ny*cellSize + cellSize/2);
// could increase speed gradually
// speed += ACCEL_PER_EAT;
updateHUD();
}
// render
render();
}
function render() {
clearBoard();
// grid
drawGrid();
// food
drawFood();
// snake
drawSnake();
// particles
updateParticles();
drawParticles();
}
function endGame() {
running = false;
gameOver = true;
if (score > best) best = score;
updateHUD();
// show overlay with score
document.getElementById('overlayScore').textContent = String(score);
document.getElementById('overlay').style.display = 'grid';
}
function startGame() {
resetGame();
// initial render
render();
// countdown-like start: allow first frame after a moment to observe
running = true;
gameOver = false;
updateHUD();
document.getElementById('overlay').style.display = 'none';
lastTime = performance.now();
}
function restartFromOverlay() {
document.getElementById('overlay').style.display = 'none';
startGame();
}
function togglePause() {
if (!running) return;
paused = !paused;
updateHUD();
}
// initial overlay: click to start
const overlay = document.getElementById('overlay');
overlay.addEventListener('click', () => {
if (!running) startGame();
});
// Start state hints
document.addEventListener('visibilitychange', () => {
// optional pause when not visible
});
// Animation loop
function loop(now) {
if (!lastTime) lastTime = now;
const dt = (now - lastTime) / 1000;
lastTime = now;
step(dt);
requestAnimationFrame(loop);
}
// Initialize
function init() {
// init best from memory? disallowed to use storage; so zeroed per session
best = 0;
updateHUD();
// render initial static board
render();
// show start hint
document.getElementById('stateLabel').textContent = 'Click or press a key to start';
// focus canvas to capture keyboard after click
canvas.setAttribute('tabindex','0');
canvas.addEventListener('click', () => canvas.focus(), {passive:true});
}
init();
requestAnimationFrame(loop);
// Ensure keyboard events are delivered only after user interaction
// Show explicit start prompt if needed
})();
</script>
</body>
</html>