Models 44

GPT-6.1 Terra

by OpenAIReasoningClean
Open in new window

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

How it went

Total time
3 min 42 s
Cost
$0.092
Output tokens
18,327
Spent thinking
9,175 tokens
Speed
84.8 tok/s
Console errors
0
First token after
6.1 s
First visible text after
2 min 27 s
Prompt tokens
493
Code size
27.5 KB
Finish reason
stop
Served by
OpenAI
Model id
openai/gpt-6.1-terra
Reasoning sent
{"effort":"xhigh"}
Max output
128,000 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 · 912 lines

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Snake — The Little Arcade</title>
<style>
  :root {
    color-scheme: dark;
    --bg: #091712;
    --panel: #10231c;
    --line: rgba(181, 220, 174, .13);
    --muted: #829c88;
    --soft: #b7cbb6;
    --cream: #edf3df;
    --lime: #c2ed8b;
  }

  * { box-sizing: border-box; }

  body {
    margin: 0;
    min-width: 0;
    min-height: 100vh;
    color: var(--cream);
    font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
    background:
      radial-gradient(ellipse at 50% 45%, rgba(42, 91, 57, .24), transparent 48%),
      radial-gradient(ellipse at 8% 5%, rgba(31, 75, 50, .19), transparent 30%),
      #091712;
  }

  body::before {
    content: "";
    position: fixed;
    inset: 0;
    pointer-events: none;
    background: linear-gradient(90deg, transparent 8%, rgba(198, 233, 185, .025) 8.05%, transparent 8.1%, transparent 91.9%, rgba(198, 233, 185, .025) 91.95%, transparent 92%);
  }

  .page {
    position: relative;
    padding: 22px 28px 24px;
    max-width: 1400px;
    margin: 0 auto;
  }

  .site-header {
    max-width: 1050px;
    margin: 0 auto;
    display: flex;
    align-items: center;
    justify-content: space-between;
  }

  .brand { display: flex; align-items: center; gap: 10px; }
  .brand-mark {
    width: 35px;
    height: 35px;
    border-radius: 11px;
    display: grid;
    place-items: center;
    background: var(--lime);
    box-shadow: 0 4px 20px rgba(168, 229, 112, .12);
  }
  .brand-mark svg { width: 23px; height: 23px; }
  .brand-name {
    font-size: 10px;
    font-weight: 850;
    letter-spacing: .16em;
    line-height: 1.4;
  }
  .brand-sub {
    color: #78927d;
    font-size: 8px;
    font-weight: 700;
    letter-spacing: .12em;
    margin-top: 2px;
  }
  .header-right {
    display: flex;
    align-items: center;
    gap: 12px;
    color: #829b86;
    font-size: 9px;
    font-weight: 750;
    letter-spacing: .14em;
  }
  .header-number {
    color: #c7ddbf;
    border: 1px solid rgba(191, 224, 184, .2);
    border-radius: 20px;
    padding: 6px 9px;
    font-size: 9px;
    letter-spacing: .08em;
  }

  .intro { text-align: center; margin-top: 18px; }
  .eyebrow {
    color: #a4c68e;
    font-size: 9px;
    font-weight: 800;
    letter-spacing: .2em;
    text-transform: uppercase;
  }
  h1 {
    margin: 4px 0 1px;
    font-family: Georgia, "Times New Roman", serif;
    font-style: italic;
    font-weight: 700;
    font-size: 49px;
    line-height: 1;
    letter-spacing: -.055em;
  }
  h1 span { color: var(--lime); }
  .subtitle {
    color: #91a994;
    font-size: 12px;
    margin: 7px 0 0;
    letter-spacing: .01em;
  }

  .play-layout {
    width: 100%;
    max-width: 1020px;
    margin: 19px auto 0;
    display: grid;
    grid-template-columns: 180px minmax(0, 530px) 180px;
    gap: 30px;
    justify-content: center;
    align-items: center;
  }

  .panel {
    border: 1px solid var(--line);
    background: linear-gradient(145deg, rgba(24, 48, 35, .78), rgba(14, 32, 24, .78));
    border-radius: 16px;
    padding: 20px;
    box-shadow: 0 15px 35px rgba(0, 0, 0, .1);
  }
  .panel-label {
    color: #8ca994;
    font-size: 9px;
    font-weight: 800;
    letter-spacing: .15em;
    text-transform: uppercase;
  }
  .label-row { display: flex; align-items: center; gap: 7px; }
  .tiny-dot {
    width: 6px;
    height: 6px;
    flex: 0 0 auto;
    border-radius: 50%;
    background: var(--lime);
    box-shadow: 0 0 10px rgba(194, 237, 139, .6);
  }
  .score-value {
    margin-top: 8px;
    color: #f0f6e7;
    font-size: 46px;
    line-height: 1;
    font-weight: 750;
    letter-spacing: -.06em;
    font-variant-numeric: tabular-nums;
  }
  .panel-divider { height: 1px; background: var(--line); margin: 20px 0; }
  .best-value {
    margin-top: 8px;
    color: #d3e3cc;
    font-size: 27px;
    line-height: 1;
    font-weight: 700;
    letter-spacing: -.04em;
    font-variant-numeric: tabular-nums;
  }
  .score-note {
    display: flex;
    align-items: center;
    gap: 7px;
    margin-top: 23px;
    color: #91ad94;
    font-size: 10px;
    white-space: nowrap;
  }
  .fruit-dot {
    width: 9px;
    height: 9px;
    background: #f19871;
    border-radius: 50%;
    box-shadow: 0 0 9px rgba(255, 142, 100, .4);
  }

  .board-column { min-width: 0; }
  .board-meta, .board-footer {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    color: #8da894;
    font-size: 9px;
    font-weight: 800;
    letter-spacing: .14em;
    text-transform: uppercase;
  }
  .board-meta { margin: 0 2px 10px; }
  .board-meta .status { display: flex; align-items: center; gap: 7px; color: #b9d9ad; }
  .board-footer { margin: 12px 2px 0; color: #728d79; font-size: 8px; }
  .board-frame {
    position: relative;
    width: 100%;
    overflow: hidden;
    border: 1px solid rgba(177, 220, 169, .24);
    border-radius: 17px;
    background: #10291e;
    box-shadow: 0 25px 65px rgba(0, 0, 0, .3), 0 0 0 5px rgba(151, 198, 150, .025);
  }
  canvas { display: block; width: 100%; height: auto; aspect-ratio: 1; }

  .overlay {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: flex-end;
    justify-content: center;
    padding: 20px;
  }
  .overlay.demo { pointer-events: none; }
  .demo-prompt {
    display: flex;
    align-items: center;
    gap: 11px;
    padding: 11px 15px;
    border: 1px solid rgba(198, 232, 181, .24);
    border-radius: 12px;
    background: rgba(10, 27, 20, .9);
    box-shadow: 0 12px 30px rgba(0, 0, 0, .3);
    backdrop-filter: blur(10px);
  }
  .prompt-icon {
    width: 31px;
    height: 31px;
    flex: 0 0 auto;
    display: grid;
    place-items: center;
    border-radius: 9px;
    color: var(--lime);
    background: rgba(191, 237, 143, .1);
  }
  .prompt-icon svg { width: 17px; height: 17px; }
  .prompt-kicker {
    color: #a7c994;
    font-size: 8px;
    font-weight: 850;
    letter-spacing: .14em;
  }
  .prompt-text { margin-top: 4px; color: #f1f7e9; font-size: 11px; font-weight: 650; white-space: nowrap; }
  .state-card { display: none; }
  .overlay.state {
    align-items: center;
    background: rgba(5, 18, 12, .72);
    backdrop-filter: blur(4px);
  }
  .overlay.state .demo-prompt { display: none; }
  .overlay.state .state-card { display: block; }
  .overlay.hidden { display: none; }
  .state-card {
    width: min(290px, 100%);
    text-align: center;
    padding: 27px 24px 23px;
    border: 1px solid rgba(194, 237, 166, .23);
    border-radius: 17px;
    background: #142c20;
    box-shadow: 0 20px 55px rgba(0, 0, 0, .35);
  }
  .state-title {
    margin: 9px 0 3px;
    font: italic 700 36px/1 Georgia, serif;
    letter-spacing: -.04em;
  }
  .state-detail { color: #acc4ab; font-size: 12px; line-height: 1.5; }
  .state-score {
    margin: 16px 0;
    color: #eaf4e3;
    font-size: 13px;
    font-weight: 700;
  }
  .state-score strong { color: var(--lime); font-size: 22px; margin-left: 5px; }
  .state-button {
    width: 100%;
    border: 0;
    border-radius: 9px;
    background: var(--lime);
    color: #14271a;
    padding: 11px 14px;
    font: inherit;
    font-size: 11px;
    font-weight: 850;
    cursor: pointer;
  }
  .state-button:hover { background: #d3f5a7; }
  .state-button:focus-visible { outline: 2px solid white; outline-offset: 3px; }
  .state-hint { margin-top: 11px; color: #8da88f; font-size: 10px; }

  .help-panel .panel-label { margin-bottom: 17px; }
  .control-title { color: #deead7; font-size: 11px; font-weight: 750; }
  .control-sub { color: #8da692; font-size: 10px; line-height: 1.5; margin-top: 5px; }
  .keymap {
    display: grid;
    grid-template-columns: repeat(3, 25px);
    gap: 4px;
    margin: 12px 0 0;
  }
  kbd {
    min-width: 25px;
    height: 24px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 1px solid rgba(191, 223, 180, .2);
    border-bottom-color: rgba(191, 223, 180, .32);
    border-radius: 5px;
    background: rgba(203, 235, 191, .06);
    color: #d7e8d0;
    font: 700 11px/1 inherit;
    font-family: inherit;
  }
  .pause-row { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
  .help-bottom {
    color: #728e79;
    font-size: 9px;
    line-height: 1.55;
    margin-top: 18px;
  }

  @media (max-width: 960px) {
    .play-layout {
      max-width: 530px;
      grid-template-columns: minmax(0, 1fr);
      gap: 12px;
    }
    .score-panel { display: flex; align-items: center; padding: 13px 17px; }
    .stat { width: 50%; }
    .stat.best-stat { border-left: 1px solid var(--line); padding-left: 18px; }
    .score-value, .best-value { margin-top: 5px; font-size: 27px; }
    .score-panel .panel-divider, .score-note { display: none; }
    .help-panel { display: flex; align-items: center; justify-content: space-between; gap: 15px; padding: 14px 17px; }
    .help-panel > .panel-label, .help-panel .panel-divider, .keymap, .help-bottom { display: none; }
    .control-block { flex: 1; }
    .pause-row { flex: 0 0 auto; gap: 13px; }
    .control-sub { margin-top: 3px; }
    .board-meta { margin-top: 2px; }
  }

  @media (max-width: 500px) {
    .page { padding: 15px 14px 24px; }
    .brand-mark { width: 32px; height: 32px; border-radius: 9px; }
    .brand-sub { font-size: 7px; }
    .header-right > span:first-child { display: none; }
    .intro { margin-top: 16px; }
    h1 { font-size: 40px; }
    .subtitle { font-size: 11px; }
    .play-layout { margin-top: 15px; gap: 10px; }
    .score-panel { padding: 10px 14px; }
    .score-value, .best-value { font-size: 23px; }
    .panel-label { font-size: 8px; }
    .board-frame { border-radius: 13px; }
    .board-meta { font-size: 8px; margin-bottom: 8px; }
    .board-footer { font-size: 7px; margin-top: 9px; letter-spacing: .1em; }
    .overlay { padding: 12px; }
    .demo-prompt { padding: 9px 11px; gap: 9px; }
    .prompt-icon { width: 28px; height: 28px; }
    .prompt-text { font-size: 10px; }
    .help-panel { padding: 11px 14px; }
  }
</style>
</head>
<body>
<div class="page">
  <header class="site-header">
    <div class="brand">
      <div class="brand-mark" aria-hidden="true">
        <svg viewBox="0 0 24 24" fill="none">
          <path d="M6 17.5h7.1a4.4 4.4 0 0 0 0-8.8H9.8a2.7 2.7 0 1 1 0-5.4h5.4" stroke="#193522" stroke-width="2.8" stroke-linecap="round"/>
          <circle cx="16.7" cy="3.3" r="2.2" fill="#193522"/>
          <circle cx="17.2" cy="2.7" r=".45" fill="#c2ed8b"/>
        </svg>
      </div>
      <div>
        <div class="brand-name">THE LITTLE ARCADE</div>
        <div class="brand-sub">A LITTLE PLAY GOES A LONG WAY</div>
      </div>
    </div>
    <div class="header-right"><span>AN EVERYDAY CLASSIC</span><span class="header-number">NO. 01</span></div>
  </header>

  <section class="intro">
    <div class="eyebrow">A LITTLE GAME FOR YOUR BREAK</div>
    <h1>snake<span>.</span></h1>
    <p class="subtitle">A classic game with a fresh appetite.</p>
  </section>

  <main class="play-layout">
    <aside class="panel score-panel" aria-label="Scores">
      <div class="stat">
        <div class="label-row"><span class="tiny-dot"></span><span class="panel-label" id="scoreLabel">DEMO SCORE</span></div>
        <div class="score-value" id="score">00</div>
      </div>
      <div class="panel-divider"></div>
      <div class="stat best-stat">
        <div class="panel-label">SESSION BEST</div>
        <div class="best-value" id="best">00</div>
      </div>
      <div class="score-note"><span class="fruit-dot"></span> 10 points per fruit</div>
    </aside>

    <section class="board-column" aria-label="Snake game">
      <div class="board-meta">
        <span class="status"><span class="tiny-dot"></span><span id="status">DEMO PLAYING</span></span>
        <span>22 × 22 FIELD</span>
      </div>
      <div class="board-frame">
        <canvas id="game" width="660" height="660" aria-label="Snake game board"></canvas>
        <div class="overlay demo" id="overlay">
          <div class="demo-prompt">
            <div class="prompt-icon" aria-hidden="true">
              <svg viewBox="0 0 24 24" fill="none">
                <rect x="3" y="6" width="18" height="12" rx="2.5" stroke="currentColor" stroke-width="1.5"/>
                <path d="M7 10h1m2 0h1m2 0h1m2 0h1M8 14h8" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
              </svg>
            </div>
            <div>
              <div class="prompt-kicker">WATCHING THE DEMO</div>
              <div class="prompt-text">Click or press a key to start</div>
            </div>
          </div>
          <div class="state-card">
            <div class="eyebrow" id="stateEyebrow">GAME OVER</div>
            <div class="state-title" id="stateTitle">Nice run.</div>
            <div class="state-detail" id="stateDetail">Every little bite counts.</div>
            <div class="state-score" id="stateScore">YOUR SCORE <strong>00</strong></div>
            <button class="state-button" id="stateButton" type="button">Play again</button>
            <div class="state-hint" id="stateHint">Press Space or Enter to restart</div>
          </div>
        </div>
      </div>
      <div class="board-footer"><span>A LITTLE SNAKE, A BIG APPETITE</span><span>FRUIT IS WORTH +10</span></div>
    </section>

    <aside class="panel help-panel">
      <div class="panel-label">HOW TO PLAY</div>
      <div class="control-block">
        <div class="control-title">Make your move</div>
        <div class="control-sub">Arrow keys or WASD</div>
        <div class="keymap" aria-hidden="true">
          <span></span><kbd>↑</kbd><span></span>
          <kbd>←</kbd><kbd>↓</kbd><kbd>→</kbd>
        </div>
      </div>
      <div class="panel-divider"></div>
      <div class="pause-row">
        <div><div class="control-title">Take a pause</div><div class="control-sub">Press to pause</div></div>
        <kbd>P</kbd>
      </div>
      <div class="help-bottom">Gather fruit, grow longer,<br>and avoid the edges.</div>
    </aside>
  </main>
</div>

<script>
(() => {
  "use strict";

  const grid = 22;
  const size = 660;
  const cell = size / grid;
  const tickMs = 125;
  const canvas = document.getElementById("game");
  const ctx = canvas.getContext("2d");
  const ratio = Math.min(window.devicePixelRatio || 1, 2);
  canvas.width = size * ratio;
  canvas.height = size * ratio;
  ctx.setTransform(ratio, 0, 0, ratio, 0, 0);

  const scoreEl = document.getElementById("score");
  const bestEl = document.getElementById("best");
  const scoreLabel = document.getElementById("scoreLabel");
  const statusEl = document.getElementById("status");
  const overlay = document.getElementById("overlay");
  const stateEyebrow = document.getElementById("stateEyebrow");
  const stateTitle = document.getElementById("stateTitle");
  const stateDetail = document.getElementById("stateDetail");
  const stateScore = document.getElementById("stateScore");
  const stateButton = document.getElementById("stateButton");
  const stateHint = document.getElementById("stateHint");

  let snake, previous, direction, queue, food, particles, score;
  let best = 0;
  let mode = "demo";
  let accumulator = 0;
  let lastTime = performance.now();
  let demoFoodIndex = 0;
  let won = false;

  const demoFoods = [
    { x: 17, y: 13 }, { x: 12, y: 15 }, { x: 5, y: 15 },
    { x: 4, y: 7 }, { x: 12, y: 5 }, { x: 18, y: 7 },
    { x: 17, y: 17 }, { x: 7, y: 18 }, { x: 3, y: 10 }
  ];

  const format = n => String(n).padStart(2, "0");
  const copySnake = s => s.map(part => ({ x: part.x, y: part.y }));
  const same = (a, b) => a.x === b.x && a.y === b.y;
  const opposite = (a, b) => a.x === -b.x && a.y === -b.y;

  function updateUI() {
    scoreEl.textContent = format(score);
    bestEl.textContent = format(best);
    scoreLabel.textContent = mode === "demo" ? "DEMO SCORE" : "YOUR SCORE";
    statusEl.textContent = mode === "demo" ? "DEMO PLAYING" :
      mode === "playing" ? "IN PLAY" : mode === "paused" ? "PAUSED" : "GAME OVER";

    if (mode === "demo") {
      overlay.className = "overlay demo";
    } else if (mode === "playing") {
      overlay.className = "overlay hidden";
    } else {
      overlay.className = "overlay state";
      if (mode === "paused") {
        stateEyebrow.textContent = "TAKE YOUR TIME";
        stateTitle.textContent = "Paused.";
        stateDetail.textContent = "Your little snake can wait.";
        stateScore.innerHTML = "YOUR SCORE <strong>" + format(score) + "</strong>";
        stateButton.textContent = "Keep playing";
        stateHint.textContent = "Press P to resume";
      } else {
        stateEyebrow.textContent = won ? "FIELD CLEARED" : "GAME OVER";
        stateTitle.textContent = won ? "You did it." : "Nice run.";
        stateDetail.textContent = won ? "That's one very impressive snake." : "Every little bite counts.";
        stateScore.innerHTML = "YOUR SCORE <strong>" + format(score) + "</strong>";
        stateButton.textContent = "Play again";
        stateHint.textContent = "Press Space or Enter to restart";
      }
    }
  }

  function reset(nextMode) {
    mode = nextMode;
    snake = [
      { x: 11, y: 9 }, { x: 10, y: 9 }, { x: 9, y: 9 },
      { x: 8, y: 9 }, { x: 7, y: 9 }, { x: 7, y: 10 },
      { x: 7, y: 11 }, { x: 6, y: 11 }, { x: 5, y: 11 },
      { x: 4, y: 11 }
    ];
    previous = copySnake(snake);
    direction = { x: 1, y: 0 };
    queue = [];
    food = { x: 15, y: 9 };
    particles = [];
    score = 0;
    won = false;
    demoFoodIndex = 0;
    accumulator = 0;
    lastTime = performance.now();
    updateUI();
  }

  function spawnFood() {
    const occupied = new Set(snake.map(p => p.y * grid + p.x));
    if (occupied.size === grid * grid) return null;

    if (mode === "demo") {
      for (let i = 0; i < demoFoods.length; i++) {
        const candidate = demoFoods[demoFoodIndex++ % demoFoods.length];
        if (!occupied.has(candidate.y * grid + candidate.x)) return { ...candidate };
      }
    }

    const free = [];
    for (let y = 0; y < grid; y++) {
      for (let x = 0; x < grid; x++) {
        if (!occupied.has(y * grid + x)) free.push({ x, y });
      }
    }
    return free[Math.floor(Math.random() * free.length)];
  }

  function enqueue(next) {
    const last = queue.length ? queue[queue.length - 1] : direction;
    if (same(last, next) || opposite(last, next)) return;
    queue.push(next);
  }

  function demoDirection() {
    const blocked = new Set(snake.slice(1, -1).map(p => p.y * grid + p.x));
    const dirs = [
      direction,
      { x: -direction.y, y: direction.x },
      { x: direction.y, y: -direction.x },
      { x: -direction.x, y: -direction.y }
    ];
    const visited = new Uint8Array(grid * grid);
    const head = snake[0];
    const search = [{ x: head.x, y: head.y, first: null }];
    visited[head.y * grid + head.x] = 1;

    for (let i = 0; i < search.length; i++) {
      const node = search[i];
      for (const d of dirs) {
        if (!node.first && opposite(d, direction)) continue;
        const x = node.x + d.x;
        const y = node.y + d.y;
        if (x < 0 || x >= grid || y < 0 || y >= grid) continue;
        const key = y * grid + x;
        if (visited[key] || blocked.has(key)) continue;
        const first = node.first || d;
        if (food && x === food.x && y === food.y) return first;
        visited[key] = 1;
        search.push({ x, y, first });
      }
    }

    for (const d of dirs) {
      if (opposite(d, direction)) continue;
      const x = head.x + d.x;
      const y = head.y + d.y;
      if (x >= 0 && x < grid && y >= 0 && y < grid && !blocked.has(y * grid + x)) return d;
    }
    return direction;
  }

  function burst(x, y) {
    for (let i = 0; i < 16; i++) {
      const angle = Math.PI * 2 * i / 16 + Math.random() * .25;
      const speed = .045 + Math.random() * .12;
      particles.push({
        x: (x + .5) * cell, y: (y + .5) * cell,
        vx: Math.cos(angle) * speed, vy: Math.sin(angle) * speed,
        life: 350 + Math.random() * 250, max: 600,
        radius: 1.5 + Math.random() * 2.8,
        color: i % 3 === 0 ? "#ffe1a0" : i % 3 === 1 ? "#ff9b75" : "#c5ef8c"
      });
    }
  }

  function step() {
    if (mode === "demo") direction = demoDirection();
    else if (queue.length) direction = queue.shift();

    const head = { x: snake[0].x + direction.x, y: snake[0].y + direction.y };
    const eating = food && same(head, food);
    const outside = head.x < 0 || head.x >= grid || head.y < 0 || head.y >= grid;
    const hitSelf = snake.slice(0, eating ? snake.length : -1).some(p => same(p, head));

    if (outside || hitSelf) {
      if (mode === "demo") reset("demo");
      else {
        mode = "over";
        updateUI();
      }
      return;
    }

    previous = copySnake(snake);
    snake.unshift(head);
    if (eating) {
      burst(food.x, food.y);
      score += 10;
      if (mode !== "demo") best = Math.max(best, score);
      food = spawnFood();
      if (!food) {
        won = true;
        mode = "over";
      }
      updateUI();
    } else {
      snake.pop();
    }
  }

  function drawBackground() {
    ctx.fillStyle = "#10291e";
    ctx.fillRect(0, 0, size, size);

    const glow = ctx.createRadialGradient(size * .48, size * .39, 10, size * .48, size * .45, size * .7);
    glow.addColorStop(0, "rgba(52, 105, 64, .28)");
    glow.addColorStop(1, "rgba(4, 19, 12, .28)");
    ctx.fillStyle = glow;
    ctx.fillRect(0, 0, size, size);

    ctx.strokeStyle = "rgba(190, 226, 176, .065)";
    ctx.lineWidth = 1;
    ctx.beginPath();
    for (let i = 1; i < grid; i++) {
      const p = i * cell + .5;
      ctx.moveTo(p, 0);
      ctx.lineTo(p, size);
      ctx.moveTo(0, p);
      ctx.lineTo(size, p);
    }
    ctx.stroke();

    ctx.fillStyle = "rgba(196, 231, 181, .15)";
    for (let y = 3; y < grid; y += 5) {
      for (let x = 3; x < grid; x += 5) {
        ctx.fillRect(x * cell - 1, y * cell - 1, 2, 2);
      }
    }
  }

  function drawFood(time) {
    if (!food) return;
    const x = (food.x + .5) * cell;
    const y = (food.y + .5) * cell;
    const pulse = 1 + Math.sin(time / 350) * .04;

    const halo = ctx.createRadialGradient(x, y, 0, x, y, cell * .85);
    halo.addColorStop(0, "rgba(255, 145, 98, .26)");
    halo.addColorStop(1, "rgba(255, 145, 98, 0)");
    ctx.fillStyle = halo;
    ctx.beginPath();
    ctx.arc(x, y, cell * .85, 0, Math.PI * 2);
    ctx.fill();

    ctx.strokeStyle = "rgba(255, 190, 147, .2)";
    ctx.lineWidth = 1;
    ctx.beginPath();
    ctx.arc(x, y, cell * .49, 0, Math.PI * 2);
    ctx.stroke();

    const fruit = ctx.createRadialGradient(x - 4, y - 5, 1, x, y, cell * .4);
    fruit.addColorStop(0, "#ffd0a8");
    fruit.addColorStop(.35, "#ff9c78");
    fruit.addColorStop(1, "#e56655");
    ctx.fillStyle = fruit;
    ctx.beginPath();
    ctx.arc(x, y + 1, cell * .32 * pulse, 0, Math.PI * 2);
    ctx.fill();

    ctx.fillStyle = "#c7eb96";
    ctx.beginPath();
    ctx.ellipse(x + 4, y - cell * .34, 5, 2.6, -.55, 0, Math.PI * 2);
    ctx.fill();
    ctx.fillStyle = "rgba(255,255,255,.7)";
    ctx.beginPath();
    ctx.arc(x - 4, y - 4, 1.7, 0, Math.PI * 2);
    ctx.fill();
  }

  function drawSnake(alpha) {
    const points = snake.map((part, i) => {
      const from = previous[Math.min(i, previous.length - 1)];
      return {
        x: (from.x + (part.x - from.x) * alpha + .5) * cell,
        y: (from.y + (part.y - from.y) * alpha + .5) * cell
      };
    });

    function trace() {
      ctx.beginPath();
      ctx.moveTo(points[points.length - 1].x, points[points.length - 1].y);
      for (let i = points.length - 2; i >= 0; i--) ctx.lineTo(points[i].x, points[i].y);
    }

    ctx.lineCap = "round";
    ctx.lineJoin = "round";
    ctx.shadowColor = "rgba(155, 224, 99, .18)";
    ctx.shadowBlur = 15;
    ctx.strokeStyle = "#426d3d";
    ctx.lineWidth = cell * .91;
    trace();
    ctx.stroke();
    ctx.shadowBlur = 0;

    const bodyGradient = ctx.createLinearGradient(0, 0, size, size);
    bodyGradient.addColorStop(0, "#bce985");
    bodyGradient.addColorStop(.55, "#95d86c");
    bodyGradient.addColorStop(1, "#72bd60");
    ctx.strokeStyle = bodyGradient;
    ctx.lineWidth = cell * .76;
    trace();
    ctx.stroke();

    for (let i = points.length - 1; i > 0; i--) {
      const p = points[i];
      ctx.fillStyle = "rgba(230, 255, 190, .11)";
      ctx.beginPath();
      ctx.arc(p.x, p.y, cell * .27, 0, Math.PI * 2);
      ctx.fill();
      ctx.fillStyle = "rgba(239, 255, 211, .11)";
      ctx.beginPath();
      ctx.arc(p.x - cell * .08, p.y - cell * .1, cell * .09, 0, Math.PI * 2);
      ctx.fill();
    }

    const h = points[0];
    const headSize = cell * .88;
    const headGradient = ctx.createLinearGradient(h.x - 12, h.y - 12, h.x + 12, h.y + 12);
    headGradient.addColorStop(0, "#d3f5a3");
    headGradient.addColorStop(1, "#a6e478");
    ctx.fillStyle = headGradient;
    ctx.beginPath();
    ctx.roundRect(h.x - headSize / 2, h.y - headSize / 2, headSize, headSize, cell * .3);
    ctx.fill();

    const perpendicular = { x: -direction.y, y: direction.x };
    for (const side of [-1, 1]) {
      const ex = h.x + direction.x * cell * .13 + perpendicular.x * cell * .19 * side;
      const ey = h.y + direction.y * cell * .13 + perpendicular.y * cell * .19 * side;
      ctx.fillStyle = "#193c27";
      ctx.beginPath();
      ctx.arc(ex, ey, cell * .085, 0, Math.PI * 2);
      ctx.fill();
      ctx.fillStyle = "rgba(255,255,255,.85)";
      ctx.beginPath();
      ctx.arc(ex - .8, ey - 1, cell * .025, 0, Math.PI * 2);
      ctx.fill();
    }
  }

  function drawParticles(dt) {
    for (let i = particles.length - 1; i >= 0; i--) {
      const p = particles[i];
      if (mode === "demo" || mode === "playing") {
        p.x += p.vx * dt;
        p.y += p.vy * dt;
        p.vx *= .985;
        p.vy *= .985;
        p.life -= dt;
      }
      if (p.life <= 0) {
        particles.splice(i, 1);
        continue;
      }
      ctx.globalAlpha = Math.min(1, p.life / 250);
      ctx.fillStyle = p.color;
      ctx.beginPath();
      ctx.arc(p.x, p.y, p.radius, 0, Math.PI * 2);
      ctx.fill();
    }
    ctx.globalAlpha = 1;
  }

  function frame(time) {
    const dt = Math.min(50, Math.max(0, time - lastTime));
    lastTime = time;
    if (mode === "demo" || mode === "playing") {
      accumulator += dt;
      while (accumulator >= tickMs && (mode === "demo" || mode === "playing")) {
        accumulator -= tickMs;
        step();
      }
    }
    drawBackground();
    drawFood(time);
    drawSnake(Math.min(1, Math.max(0, accumulator / tickMs)));
    drawParticles(dt);
    requestAnimationFrame(frame);
  }

  const keys = {
    arrowup: { x: 0, y: -1 }, w: { x: 0, y: -1 },
    arrowdown: { x: 0, y: 1 }, s: { x: 0, y: 1 },
    arrowleft: { x: -1, y: 0 }, a: { x: -1, y: 0 },
    arrowright: { x: 1, y: 0 }, d: { x: 1, y: 0 }
  };

  document.addEventListener("keydown", event => {
    const key = event.key.toLowerCase();
    const move = keys[key];
    if (move || key === " " || key === "enter" || key === "p") event.preventDefault();

    if (mode === "demo") {
      reset("playing");
      if (move) enqueue(move);
      if (key === "p") {
        mode = "paused";
        updateUI();
      }
      return;
    }
    if (mode === "over") {
      if ((key === " " || key === "enter") && !event.repeat) reset("playing");
      return;
    }
    if (key === "p" && !event.repeat) {
      mode = mode === "paused" ? "playing" : "paused";
      lastTime = performance.now();
      updateUI();
      return;
    }
    if (mode === "playing" && move) enqueue(move);
  });

  document.addEventListener("pointerdown", () => {
    if (mode === "demo") reset("playing");
  });

  stateButton.addEventListener("click", () => {
    if (mode === "over") reset("playing");
    else if (mode === "paused") {
      mode = "playing";
      lastTime = performance.now();
      updateUI();
    }
  });

  reset("demo");
  requestAnimationFrame(frame);
})();
</script>
</body>
</html>
Reasoning trace (9,175 tokens)
**Designing HTML Structure**

I need to construct a full HTML layout that considers visual elements, aiming for a polished self-playing demo. The game needs to be centered at 20x20 and effectively adapt to different screen sizes like 1280x800 and 375. No external resources should be used, and I want to ensure a robust input queue. I'll choose a beautiful dark navy background with refined cream, turquoise, and lime accents while focusing on the game board's centering and visual dominance.**Structuring the Game Layout**

I need to center the canvas board and use decorative side panels around it using absolute positioning or grid columns, ensuring the main board is perfectly centered. The header also needs centering, and I’m working with a board size around 520. For visuals, I’ll include a score display above the board, and I should design a top bar for branding and a title like "SNAKE." It’s important to have an overlay for user interaction while ensuring gameplay remains visible in the background during the demo.**Planning the Self-Playing Demo**

I need to set up a self-playing demo that has a clear but minimal overlay positioned near the bottom of the board, so the game remains visible. I want to add a "DEMO MODE" label above this overlay. The initial snake should be long and visually appealing while making sure the food is visible and relevant during the demo. I’ll aim for a board size of 22x22 or 24x24 and ensure smooth movement through continuous interpolation. I'll also need to manage collisions and allow for quick input processing without reversing directions.**Planning game demo features**

I'm working on restarting the game board and showing a message like "Click or press a key to start." The user can control the initial direction with arrow keys or WASD. I aim for a smooth demo where the autoplayer seeks food without crashing. I'll need an initial snake size around 7-9 cells and visible food. My goal is to make sure the demo doesn't interfere with the actual session scores and to display the best session score subtly. Let's ensure everything runs coherently!**Refining demo scoring system**

I’m rethinking how the scoring works during the demo. If I show a score of 10 and a session best of 0, that feels odd. It might make sense to keep the demo from modifying the score display until the user starts playing. I'm considering displaying scores as "—" during the demo instead. It could clarify that demos don’t impact best session scores. I think labeling demo scores clearly and ensuring they don't affect session rankings would create a fair experience!**Enhancing demo functionality**

I’m noticing that as the demo score increases, it becomes tricky for users to judge their performance. People typically want to see only the active player's record. Maybe I could update the displayed scores to show the maximum between the best and demo scores, resetting when the actual game starts. However, it feels odd for the record to disappear when the game begins. I need to ensure smooth movement for the snake while managing the rendering between the previous and current states at each tick.**Designing a Snake Game**

I'm outlining the visuals for a snake game, focusing on a rounded thickness for the snake's body and highlights. The head will be larger and squared with facial features, while the body will consist of lighter, translucent segments. Initially, I'll set the snake's direction to the right with specific coordinates for its segments.

Also, I want to ensure that the AI-controlled snake can navigate effectively without colliding with walls. BFS will help in this pathfinding challenge, making sure it can find food efficiently.**Planning a Hamiltonian Cycle for Snake**

I’m thinking about an alternative Hamiltonian cycle on a 22x22 board, where the snake follows a preplanned route. I want to place food strategically on this path to ensure the snake eats frequently and enjoys smooth turns. However, implementing AI with BFS seems better, especially for dynamic movements. The snake's body will act as an obstacle for pathfinding, ensuring it doesn't crash. If the game reaches a game-over state, it will reset after 500 milliseconds. This should keep the gameplay engaging!**Designing canvas layout**

I’m thinking about the canvas board layout. There’s a status strip at the top, so I need to ensure that's in a separate heading.  

For the columns, the top bar should have a width of 1100 pixels, with the title “THE ORIGINAL / ARCADE NO. 01” centered at the top. The word “Snake” should be big, in white with a small green star next to it. Also, there should be a subtext, “A little nostalgia, a lot of movement.” Below that, I want a centered flex layout for the central board and side panels.**Designing layout elements**

I’m considering the layout for the top bar. I think it should be centered with a maximum width of 1100 pixels. The logo, "SNAKE CLUB," could be positioned on the left side, while on the right side, we might include the tagline "A MINI ARCADE EXPERIENCE" along with a "SOUND OFF" option. Having no sound initially seems important to avoid any issues. It's all about creating a neat, user-friendly experience!**Designing overlay message**

I'm thinking about the overlay design. At the bottom, I need a centered message with a glass card that's about 270 in width, positioned 30 from the bottom. I want it to be visible without any dark blanket. There should be a small green dot indicating that "AUTO-PLAY IS ON," along with a prominent "Your turn?" text. Plus, I want to include instructions like "Click or press a key to start," possibly one line big and a secondary description.**Planning game controls**

I'm thinking about how to start the demo for the snake game. I should set things up so that initial instructions appear, like "Click or press a key to start." I want to ensure that any key works to begin, except maybe those disallowed ones. So, if a key press triggers it, I want the game to remember the initial direction without allowing any reverse movement. If clicking starts it, it won’t have a direction, and I’ll make space or enter work for starting too!**Considering canvas rendering**

I’m exploring the idea of using devicePixelRatio to set fixed view units for a canvas. If I set the size to 660, I'll calculate the width as 660 times the devicePixelRatio. So, I’d use ctx.setTransform to manage the scaling properly. For the board grid, each cell can be 30 units, and I’ll want to establish a visual style with specific colors for gradients and borders. Plus, I’m thinking about adding some subtle details like cross markers and a glowing effect for the food.**Structuring the canvas layout**

I’m thinking about creating a dynamic title for the canvas and adding board information as a thin bar at the top. I want it to display something like "THE PLAYING FIELD" along with its dimensions. For layout, the left card can have a width of 190 pixels on desktops showing score and best session details, while the right card holds controls. The overall header should not exceed 1050 pixels in width, and I’ll figure out padding and margins to keep everything organized, including aligning the sidebar and side cards efficiently.**Exploring panel design**

I’m considering adding a `.panel` element with a subtle radial background to enhance the visual appeal. It would also have rounded borders, perhaps with a radius of 18 pixels, to give it a softer, more inviting look. This could really help in creating a more polished and aesthetically pleasing overall design. I’m curious how these design choices will affect the user experience and whether they’ll tie in nicely with the rest of the layout!**Designing the game layout**

I need to create a polished page background with a gradient and tiny radial spots, and maybe faint vertical lines. The main background should be rich dark, with a lighter canvas grid in the center. I'll use a neon accent color of #b9eb79 and off-white text #edf4db. The font will be system sans, like Inter or Arial, and Georgia italic for the h1, giving a strong look. For the title, I’ll use Georgia bold italic at 51px.**Considering visual rendering**

I'm thinking about how to render the snake's tail tip, which should be tapered, while keeping the base thick and full. The tail might slightly decrease in size towards the end if the path rounds off. The initial body should look segmented. I'll begin by showing the current positions and draw the tail based on the visible portions of the snake's body. For the best visuals, I should format the score with two decimal places, but only increment it when the player collects food instead of at game over.**Considering game mechanics**

I'm thinking about how to update the game DOM. It seems like I should only do it when the player eats or resets. The button needs to be focused when clicked, and I want to ensure there's no conflict with active keys. If the Enter key is pressed, it should restart the game — important to use `event.preventDefault` so clicks don't trigger unexpectedly. I'll also make sure that accessibility isn’t a problem. And I need to manage collision rules carefully and avoid any self-collisions.**Reviewing food spawning logic**

I’m considering how to handle food spawning in the game. I need to ensure it spawns randomly in free cells, avoiding any filled areas. If the board is full and there are no free cells, it seems like I should show a "YOU WIN" overlay. Eating the last food requires checking the mode — if it’s over, special handling is necessary. Food must never spawn on the snake itself, and if the snake fills the grid, the game should display the win conditions. I’m also thinking about setting up demo presets and directions for the AI.**Considering BFS expansion**

I'm thinking about the breadth-first search (BFS) expansion and how it should avoid any reverse moves while exploring all four other directions. I need to ensure that the tail isn’t included in any blocked paths, and I might use JSON strings or index references for tracking. Using `Uint8Array` for visited cells seems like a smart choice. Also, there’s a consideration for checking if the first cell is the same as the food — that should never happen to maintain proper gameplay logic.**Calculating canvas dimensions**

I’m working through the dimensions for a board canvas. The width is set at 510. The header height is 22 for site padding, then 34 for the brand, which brings it to 56 at the bottom, and then 16 for the intro adds up to 72. I've added an eyebrow of 11, and the h1 increases this to 129. With the subtitle at 15, the total comes to 150. Alright, I’m noting all these adjustments for a nice layout!**Planning canvas redraw**

I'm making sure we handle the board redraw and food effects before any overlay is applied. I’ll use `rgba` colors to draw the shape of a `roundRect`, which is well-supported in Chrome. For this, I’ll create a local function `roundedRect` via `ctx.roundRect`. I'm focused on the latest version of Chrome and considering device pixel ratios between 1 and 2, with a canvas size of 660 multiplied by the `DPR`. This should set us up nicely!