Models 44

GLM 5.3

by Z.aiReasoningClean
Open in new window

Running live in a sandboxed frame. Click inside it before using the keyboard.

How it went

Total time
40.0 s
Cost
$0.025
Output tokens
5,723
Spent thinking
672 tokens
Speed
143.9 tok/s
Console errors
0
First token after
225 ms
First visible text after
11.7 s
Prompt tokens
491
Code size
14.9 KB
Finish reason
stop
Served by
Inceptron
Model id
z-ai/glm-5.3
Reasoning sent
{"effort":"low"}
Max output
943,718 tokens
Generated
Nov 12, 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 · 493 lines

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Snake</title>
<style>
  * { margin: 0; padding: 0; box-sizing: border-box; }
  html, body { height: 100%; }
  body {
    font-family: "Segoe UI", system-ui, -apple-system, sans-serif;
    background: radial-gradient(1200px 800px at 50% -10%, #1b2940 0%, #0d1522 55%, #080d16 100%);
    color: #dfe9f5;
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 100vh;
    overflow: hidden;
  }
  .wrap {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 14px;
    padding: 16px;
    width: 100%;
    max-width: 640px;
  }
  h1 {
    font-size: 28px;
    font-weight: 700;
    letter-spacing: 6px;
    color: #eaf3ff;
    text-shadow: 0 0 24px rgba(90, 200, 160, .35);
  }
  h1 span { color: #57d9a3; }
  .hud {
    display: flex;
    gap: 14px;
    width: 100%;
    max-width: 576px;
  }
  .card {
    flex: 1;
    background: rgba(255,255,255,.045);
    border: 1px solid rgba(255,255,255,.08);
    border-radius: 12px;
    padding: 10px 16px;
    display: flex;
    flex-direction: column;
    align-items: center;
    backdrop-filter: blur(4px);
  }
  .card .label {
    font-size: 11px;
    letter-spacing: 3px;
    text-transform: uppercase;
    color: #8ea3bd;
  }
  .card .value {
    font-size: 26px;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    color: #eaf3ff;
    line-height: 1.15;
  }
  .card.best .value { color: #f2c14e; }
  .stage { position: relative; line-height: 0; }
  canvas {
    border-radius: 14px;
    background: #0b1220;
    box-shadow: 0 24px 60px rgba(0,0,0,.55), 0 0 0 1px rgba(255,255,255,.07), inset 0 0 60px rgba(0,0,0,.4);
    max-width: 100%;
    height: auto;
  }
  .overlay {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 14px;
    background: rgba(8, 13, 22, .72);
    backdrop-filter: blur(3px);
    opacity: 0;
    pointer-events: none;
    transition: opacity .25s ease;
  }
  .overlay.show { opacity: 1; pointer-events: auto; }
  .overlay .panel {
    text-align: center;
    padding: 28px 40px;
    background: rgba(15, 24, 38, .92);
    border: 1px solid rgba(255,255,255,.12);
    border-radius: 16px;
    box-shadow: 0 20px 50px rgba(0,0,0,.6);
  }
  .overlay .title {
    font-size: 24px;
    font-weight: 700;
    letter-spacing: 4px;
    margin-bottom: 10px;
  }
  .overlay .title.win { color: #ff6b6b; }
  .overlay .title.start { color: #57d9a3; }
  .overlay .sub { font-size: 14px; color: #9db2cc; line-height: 1.7; }
  .overlay .score-line { font-size: 18px; color: #eaf3ff; margin: 8px 0 4px; }
  .overlay .score-line b { color: #57d9a3; font-size: 26px; }
  .hint {
    font-size: 12px;
    color: #7488a3;
    letter-spacing: 1px;
    text-align: center;
    line-height: 1.8;
  }
  .hint b { color: #a9c1de; font-weight: 600; }
</style>
</head>
<body>
<div class="wrap">
  <h1>SNA<span>KE</span></h1>
  <div class="hud">
    <div class="card"><div class="label">Score</div><div class="value" id="score">0</div></div>
    <div class="card best"><div class="label">Best (session)</div><div class="value" id="best">0</div></div>
  </div>
  <div class="stage">
    <canvas id="game" width="576" height="576"></canvas>
    <div class="overlay" id="overlay">
      <div class="panel">
        <div class="title" id="ovTitle">SNAKE</div>
        <div class="score-line" id="ovScore"></div>
        <div class="sub" id="ovSub">Click or press a key to start</div>
      </div>
    </div>
  </div>
  <div class="hint"><b>Arrows / WASD</b> move &nbsp;·&nbsp; <b>P</b> pause &nbsp;·&nbsp; <b>Space / Enter</b> restart</div>
</div>

<script>
(() => {
  const COLS = 24, ROWS = 24, CELL = 24;
  const canvas = document.getElementById('game');
  const ctx = canvas.getContext('2d');
  const scoreEl = document.getElementById('score');
  const bestEl = document.getElementById('best');
  const overlay = document.getElementById('overlay');
  const ovTitle = document.getElementById('ovTitle');
  const ovScore = document.getElementById('ovScore');
  const ovSub = document.getElementById('ovSub');

  const TICK_BASE = 150, TICK_MIN = 70;
  let state = 'demo'; // demo | playing | paused | over
  let snake, prevSnake, dir, queue, food, score, best = 0, particles = [];
  let tickInterval, lastTick = 0, shake = 0;

  function reset() {
    snake = [{x: 11, y: 12}, {x: 10, y: 12}, {x: 9, y: 12}];
    prevSnake = snake.map(s => ({...s}));
    dir = {x: 1, y: 0};
    queue = [];
    score = 0;
    particles = [];
    tickInterval = TICK_BASE;
    spawnFood();
    updateHUD();
  }

  function updateHUD() {
    scoreEl.textContent = score;
    bestEl.textContent = best;
  }

  function spawnFood() {
    const free = [];
    const occ = new Set(snake.map(s => s.x + ',' + s.y));
    for (let y = 0; y < ROWS; y++)
      for (let x = 0; x < COLS; x++)
        if (!occ.has(x + ',' + y)) free.push({x, y});
    food = free.length ? free[(Math.random() * free.length) | 0] : null;
    foodSpawnTime = performance.now();
  }

  let foodSpawnTime = 0;

  function occupied(x, y, ignoreTail) {
    const n = ignoreTail ? snake.length - 1 : snake.length;
    for (let i = 0; i < n; i++)
      if (snake[i].x === x && snake[i].y === y) return true;
    return false;
  }

  function step() {
    prevSnake = snake.map(s => ({...s}));
    // choose direction
    if (state === 'demo') {
      const d = aiChoose();
      if (d) dir = d;
    } else if (queue.length) {
      dir = queue.shift();
    }
    const head = {x: snake[0].x + dir.x, y: snake[0].y + dir.y};
    const willEat = food && head.x === food.x && head.y === food.y;

    // collisions
    if (head.x < 0 || head.y < 0 || head.x >= COLS || head.y >= ROWS ||
        occupied(head.x, head.y, !willEat)) {
      gameOver();
      return;
    }
    snake.unshift(head);
    if (willEat) {
      score += 10;
      if (score > best) best = score;
      burst(head.x, head.y);
      tickInterval = Math.max(TICK_MIN, TICK_BASE - Math.floor(score / 30) * 8);
      spawnFood();
      updateHUD();
    } else {
      snake.pop();
    }
  }

  function gameOver() {
    shake = 1;
    if (state === 'demo') {
      reset(); // demo snake just restarts silently
      return;
    }
    state = 'over';
    ovTitle.textContent = 'GAME OVER';
    ovTitle.className = 'title win';
    ovScore.innerHTML = 'Score: <b>' + score + '</b>' + (score >= best && score > 0 ? ' &nbsp;·&nbsp; New best!' : '');
    ovSub.innerHTML = 'Press <b style="color:#a9c1de">Space</b> or <b style="color:#a9c1de">Enter</b> to restart';
    overlay.classList.add('show');
  }

  function startGame() {
    reset();
    state = 'playing';
    overlay.classList.remove('show');
    lastTick = performance.now();
  }

  // --- particles ---
  function burst(cx, cy) {
    const px = (cx + 0.5) * CELL, py = (cy + 0.5) * CELL;
    for (let i = 0; i < 16; i++) {
      const a = Math.random() * Math.PI * 2;
      const sp = 1.2 + Math.random() * 2.6;
      particles.push({
        x: px, y: py,
        vx: Math.cos(a) * sp, vy: Math.sin(a) * sp - 0.6,
        life: 1, size: 2 + Math.random() * 3,
        hue: 140 + Math.random() * 60
      });
    }
  }

  // --- demo AI ---
  function aiChoose() {
    const cands = [
      {x: dir.x, y: dir.y},
      {x: -dir.y, y: dir.x},
      {x: dir.y, y: -dir.x}
    ];
    let bestD = null, bestScore = -Infinity;
    for (const d of cands) {
      const nx = snake[0].x + d.x, ny = snake[0].y + d.y;
      if (nx < 0 || ny < 0 || nx >= COLS || ny >= ROWS) continue;
      if (occupied(nx, ny, true)) continue;
      let s = 0;
      if (food) s -= (Math.abs(nx - food.x) + Math.abs(ny - food.y)) * 2;
      // safety: free neighbors
      let free = 0;
      for (const e of [{x:1,y:0},{x:-1,y:0},{x:0,y:1},{x:0,y:-1}]) {
        const fx = nx + e.x, fy = ny + e.y;
        if (fx >= 0 && fy >= 0 && fx < COLS && fy < ROWS && !occupied(fx, fy, true)) free++;
      }
      s += free * 3 + Math.random();
      if (s > bestScore) { bestScore = s; bestD = d; }
    }
    return bestD;
  }

  // --- input ---
  function queueDir(d) {
    const last = queue.length ? queue[queue.length - 1] : dir;
    if (d.x === -last.x && d.y === -last.y) return; // no reverse
    if (d.x === last.x && d.y === last.y) return;  // no duplicate
    if (queue.length < 2) queue.push(d);           // honor quick successive presses
  }

  const KEYS = {
    ArrowUp: {x:0,y:-1}, ArrowDown: {x:0,y:1}, ArrowLeft: {x:-1,y:0}, ArrowRight: {x:1,y:0},
    KeyW: {x:0,y:-1}, KeyS: {x:0,y:1}, KeyA: {x:-1,y:0}, KeyD: {x:1,y:0}
  };

  window.addEventListener('keydown', e => {
    if (e.code in KEYS) e.preventDefault();
    if (state === 'demo') { startGame(); return; }
    if (state === 'over') {
      if (e.code === 'Space' || e.code === 'Enter') startGame();
      return;
    }
    if (e.code === 'KeyP') {
      if (state === 'paused') {
        state = 'playing';
        overlay.classList.remove('show');
        lastTick = performance.now();
      } else if (state === 'playing') {
        state = 'paused';
        ovTitle.textContent = 'PAUSED';
        ovTitle.className = 'title start';
        ovScore.innerHTML = 'Score: <b>' + score + '</b>';
        ovSub.innerHTML = 'Press <b style="color:#a9c1de">P</b> to resume';
        overlay.classList.add('show');
      }
      return;
    }
    if (state === 'playing' && e.code in KEYS) queueDir(KEYS[e.code]);
  });

  canvas.addEventListener('pointerdown', () => {
    if (state === 'demo' || state === 'over') startGame();
  });

  // --- rendering ---
  const lerp = (a, b, t) => a + (b - a) * t;

  function draw(t) {
    ctx.save();
    if (shake > 0) {
      ctx.translate((Math.random() - 0.5) * shake * 10, (Math.random() - 0.5) * shake * 10);
      shake *= 0.88;
      if (shake < 0.02) shake = 0;
    }
    // background
    ctx.fillStyle = '#0b1220';
    ctx.fillRect(-8, -8, canvas.width + 16, canvas.height + 16);
    // grid
    ctx.strokeStyle = 'rgba(255,255,255,.035)';
    ctx.lineWidth = 1;
    for (let i = 1; i < COLS; i++) {
      ctx.beginPath(); ctx.moveTo(i * CELL, 0); ctx.lineTo(i * CELL, canvas.height); ctx.stroke();
      ctx.beginPath(); ctx.moveTo(0, i * CELL); ctx.lineTo(canvas.width, i * CELL); ctx.stroke();
    }
    // border
    ctx.strokeStyle = 'rgba(87,217,163,.25)';
    ctx.lineWidth = 2;
    ctx.strokeRect(1, 1, canvas.width - 2, canvas.height - 2);

    // food
    if (food) {
      const pulse = 0.85 + 0.15 * Math.sin((performance.now() - foodSpawnTime) / 180);
      const fx = (food.x + 0.5) * CELL, fy = (food.y + 0.5) * CELL;
      const r = CELL * 0.32 * pulse;
      ctx.save();
      ctx.shadowColor = 'rgba(255, 110, 110, .8)';
      ctx.shadowBlur = 14;
      const g = ctx.createRadialGradient(fx - r/3, fy - r/3, r*0.15, fx, fy, r);
      g.addColorStop(0, '#ffd9c9');
      g.addColorStop(0.55, '#ff6b5e');
      g.addColorStop(1, '#d63c3c');
      ctx.fillStyle = g;
      ctx.beginPath(); ctx.arc(fx, fy, r, 0, Math.PI * 2); ctx.fill();
      ctx.restore();
    }

    // particles
    for (let i = particles.length - 1; i >= 0; i--) {
      const p = particles[i];
      p.x += p.vx; p.y += p.vy; p.vy += 0.08; p.life -= 0.025;
      if (p.life <= 0) { particles.splice(i, 1); continue; }
      ctx.globalAlpha = Math.max(0, p.life);
      ctx.fillStyle = `hsl(${p.hue}, 80%, 60%)`;
      ctx.beginPath(); ctx.arc(p.x, p.y, p.size * p.life, 0, Math.PI * 2); ctx.fill();
      ctx.globalAlpha = 1;
    }

    // snake (interpolated)
    const alpha = Math.min(1, t);
    const pts = snake.map((s, i) => {
      const p = prevSnake[Math.min(i, prevSnake.length - 1)] || s;
      return {
        x: lerp(p.x, s.x, alpha) * CELL,
        y: lerp(p.y, s.y, alpha) * CELL
      };
    });

    // body as a thick rounded path
    if (pts.length > 1) {
      ctx.lineJoin = 'round';
      ctx.lineCap = 'round';
      // dark outline
      ctx.strokeStyle = 'rgba(10, 30, 24, .9)';
      ctx.lineWidth = CELL * 0.86;
      tracePath(pts);
      // body gradient
      const grad = ctx.createLinearGradient(pts[0].x, pts[0].y, pts[pts.length-1].x, pts[pts.length-1].y);
      grad.addColorStop(0, '#5ee6a8');
      grad.addColorStop(0.5, '#3cba84');
      grad.addColorStop(1, '#25845d');
      ctx.strokeStyle = grad;
      ctx.lineWidth = CELL * 0.74;
      tracePath(pts);
      // glossy highlight
      ctx.strokeStyle = 'rgba(255,255,255,.14)';
      ctx.lineWidth = CELL * 0.2;
      tracePath(pts, -CELL * 0.13);
    }

    // head
    const h = pts[0];
    const hx = h.x + CELL / 2, hy = h.y + CELL / 2;
    const hr = CELL * 0.46;
    ctx.save();
    ctx.shadowColor = 'rgba(94, 230, 168, .5)';
    ctx.shadowBlur = 12;
    const hg = ctx.createRadialGradient(hx - hr/3, hy - hr/3, hr*0.2, hx, hy, hr);
    hg.addColorStop(0, '#a8ffd4');
    hg.addColorStop(0.6, '#5ee6a8');
    hg.addColorStop(1, '#2f9e70');
    ctx.fillStyle = hg;
    roundedSquare(h.x + 1, h.y + 1, CELL - 2, CELL - 2, 7);
    ctx.fill();
    ctx.restore();

    // eyes facing direction
    const ex = dir.x, ey = dir.y;
    const perpX = -ey, perpY = ex;
    const fwd = CELL * 0.18, side = CELL * 0.19;
    for (const s of [-1, 1]) {
      const cx = hx + ex * fwd + perpX * side * s;
      const cy = hy + ey * fwd + perpY * side * s;
      ctx.fillStyle = '#fff';
      ctx.beginPath(); ctx.arc(cx, cy, CELL * 0.135, 0, Math.PI * 2); ctx.fill();
      ctx.fillStyle = '#0b1220';
      ctx.beginPath(); ctx.arc(cx + ex * 1.6, cy + ey * 1.6, CELL * 0.07, 0, Math.PI * 2); ctx.fill();
    }
    ctx.restore();
  }

  function tracePath(pts, offset) {
    ctx.beginPath();
    if (offset) {
      ctx.moveTo(pts[0].x + CELL/2, pts[0].y + CELL/2 + offset);
      for (let i = 1; i < pts.length; i++)
        ctx.lineTo(pts[i].x + CELL/2, pts[i].y + CELL/2 + offset);
    } else {
      ctx.moveTo(pts[0].x + CELL/2, pts[0].y + CELL/2);
      for (let i = 1; i < pts.length; i++)
        ctx.lineTo(pts[i].x + CELL/2, pts[i].y + CELL/2);
    }
    ctx.stroke();
  }

  function roundedSquare(x, y, w, h, r) {
    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();
  }

  // --- main loop ---
  function loop(now) {
    if (state === 'playing' || state === 'demo') {
      if (now - lastTick >= tickInterval) {
        const t = (now - lastTick) / tickInterval;
        lastTick = now - Math.min(t - 1, 0.5) * tickInterval; // avoid drift buildup
        step();
      }
    }
    const t = (state === 'playing' || state === 'demo')
      ? Math.min(1, (now - lastTick) / tickInterval) : 1;
    draw(t);
    requestAnimationFrame(loop);
  }

  reset();
  lastTick = performance.now();
  ovTitle.textContent = 'SNAKE';
  ovTitle.className = 'title start';
  ovScore.innerHTML = 'Demo mode — watch the snake play';
  ovSub.innerHTML = 'Click or press a key to start';
  overlay.classList.add('show');
  requestAnimationFrame(loop);
})();
</script>
</body>
</html>
Reasoning trace (672 tokens)
Let me build a polished snake game. Key features: demo mode (AI plays) at start with "Click or press a key to start", smooth interpolated movement, particles, eyes, best score in-session (variable, no localStorage).

Let me write it carefully. Grid 24×24, canvas ~480px, responsive.

Design: dark elegant background, subtle grid, snake with rounded segments, gradient body, head with eyes facing direction, particles on eat.

Smooth movement: keep tick-based logic but interpolate positions between ticks for rendering.

Input queue: store up to 2 pending directions, reject reverses.

Demo AI: simple pathfinding — move toward food, avoid walls/self greedily with some safety check. Simple: prefer direction reducing distance to food, avoid cells occupied by snake or walls; add small lookahead.

Pause with P; restart with Space/Enter on game over.

Let me code.

Canvas 24×24 cells at 24px = 576px. Fit within 800 height minus HUD — ok.

Rendering: each frame, t = (now - lastTick)/tickInterval clamped. Draw snake segments interpolated: for each segment i, position = lerp(prev[i], curr[i], t). Head with eyes rotated per direction.

Implementation details:
- snake = array of {x,y}, head first.
- prevSnake copy before each move for interpolation.
- direction, queue.
- move(): newHead; check wall/self (excluding tail if not growing); eat food → score+=10, grow (don't pop tail), spawn food not on snake, particles; else pop tail.
- On death: gameover state, show overlay with score.
- Best score session variable.
- States: 'demo', 'playing', 'paused', 'over'.
- Demo AI plays when state==='demo' — same tick logic.

AI: choose dir among current, left/right turns... greedy: candidates = 3 non-reverse dirs; score each: next cell; if collision → -inf; else dist to food + safety (count free neighbors). Pick best. Works fine for demo.

Keyboard: keydown handler — any key in demo starts game (reset). In playing: arrows/WASD queue dir; P pause; in over: space/enter restart; click canvas focuses/starts.

Message overlay div with "Click or press a key to start".

Particles: on eat, spawn ~14 particles at food cell, colorful, fade out, drawn each frame with gravity.

Let me write the full HTML now, keeping it tidy.

Eyes: head direction from dir vector; two eyes offset perpendicular. Also interpolate head position for smoothness.

Edge case: growth — prevSnake length vs new length differ; when interpolating, for index i beyond prevSnake length use last prev element. Actually when growing, new array = [newHead, ...old without tail]. prev = old. prev[i] exists for i < old.length; new length = old.length (grow adds one: new length = old.length+1? old length L, after move new length = L (no grow) or L+1 (grow)). For i in new, prev index i-... just use prev[i] ?? prev[prev.length-1]. Fine.

Also food pulse animation.

Write it.