• Updated 30 Oct, 21:11 UTC
  • Next run 31 Oct, 05:17 UTCNext 31 Oct 05:17
  • Last 7 days: 4 new models · 475 runs7 d: 4 models · 475 runs
Playable Breakout

GLM 5.3Z.ai

Network offOpen alone (new tab)

Run details

Model
z-ai/glm-5.3
Route
OpenRouter API, one user message
Served by
Wafer
Effort
max, OpenRouter's default for this model
Limits
60 min · 943,718 output tokens (the model's limit)
Date
24 Oct 2026
Time
31 s
Size
18 kB
Tokens
88 in, 6,709 out (1,896 of them thinking)
List price
$0.024 at today's price ($0.08 in / $3.6 out per M)
Paid
$0.011

Raw files: output · meta.json

Exact prompt

Make a Breakout game I can play right now with the keyboard or the mouse. Give it several levels, a score and some juice.

It must be one complete, self-contained HTML file: inline all CSS and JavaScript, and make no network requests of any kind (no CDNs, web fonts or remote images). Reply with the file in a single ```html code block.

Source

18 kB 556 lines Raw

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>NEON BREAKER</title>
<style>
  * { margin: 0; padding: 0; box-sizing: border-box; }
  html, body {
    height: 100%;
    background: #0a0a12;
    overflow: hidden;
    font-family: 'Courier New', monospace;
    user-select: none;
  }
  #wrap {
    position: relative;
    width: 100vw; height: 100vh;
    display: flex; align-items: center; justify-content: center;
  }
  canvas { display: block; }
  #hud {
    position: absolute; top: 14px; left: 50%; transform: translateX(-50%);
    display: flex; gap: 42px;
    color: #e8e8f0; font-size: 15px; letter-spacing: 3px;
    font-weight: bold; pointer-events: none; text-transform: uppercase;
    text-shadow: 0 0 12px rgba(120,140,255,.35);
  }
  #hud span b { color: #ffd166; }
  #msg {
    position: absolute; top: 50%; left: 50%;
    transform: translate(-50%,-50%);
    color: #fff; text-align: center; pointer-events: none;
    font-size: 34px; letter-spacing: 8px; font-weight: bold;
    text-shadow: 0 0 24px rgba(150,120,255,.7), 0 0 60px rgba(150,120,255,.4);
    transition: opacity .4s;
    white-space: pre-line; line-height: 1.7;
  }
  #msg small { font-size: 14px; letter-spacing: 4px; color: #a9a9c8; display:block; }
</style>
</head>
<body>
<div id="wrap">
  <canvas id="c"></canvas>
  <div id="hud">
    <span>Score <b id="score">0</b></span>
    <span>Level <b id="level">1</b></span>
    <span>Lives <b id="lives">3</b></span>
    <span>Best <b id="best">0</b></span>
  </div>
  <div id="msg"></div>
</div>

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

// ---------- Setup ----------
const cvs = document.getElementById('c');
const ctx = cvs.getContext('2d');
const W = 900, H = 640;
cvs.width = W; cvs.height = H;

function fit() {
  const s = Math.min(innerWidth / W, innerHeight / H) * 0.96;
  cvs.style.width  = W * s + 'px';
  cvs.style.height = H * s + 'px';
}
addEventListener('resize', fit); fit();

const scoreEl = document.getElementById('score');
const levelEl = document.getElementById('level');
const livesEl = document.getElementById('lives');
const bestEl  = document.getElementById('best');
const msgEl   = document.getElementById('msg');

let best = +localStorage.getItem('nb_best') || 0;
bestEl.textContent = best;

// ---------- Audio (WebAudio, no assets) ----------
let AC = null;
function audio() { if (!AC) AC = new (window.AudioContext || window.webkitAudioContext)(); return AC; }
function beep(freq, dur = .08, type = 'square', vol = .12, slide = 0) {
  try {
    const a = audio(), t = a.currentTime;
    const o = a.createOscillator(), g = a.createGain();
    o.type = type; o.frequency.setValueAtTime(freq, t);
    if (slide) o.frequency.exponentialRampToValueAtTime(Math.max(30, freq + slide), t + dur);
    g.gain.setValueAtTime(vol, t);
    g.gain.exponentialRampToValueAtTime(.0001, t + dur);
    o.connect(g); g.connect(a.destination);
    o.start(t); o.stop(t + dur + .02);
  } catch(e){}
}
const SFX = {
  hit:   () => beep(520 + Math.random()*160, .05, 'square', .09),
  paddle:() => beep(220, .07, 'triangle', .14, 60),
  wall:  () => beep(180, .04, 'square', .06),
  brick: (n) => beep(300 + Math.min(n,10)*45, .07, 'square', .1),
  die:   () => beep(300, .5, 'sawtooth', .16, -240),
  level: () => { beep(440,.12,'square',.1); setTimeout(()=>beep(660,.12,'square',.1),120); setTimeout(()=>beep(880,.2,'square',.1),240); },
  power: () => beep(700,.15,'sine',.14, 400),
};

// ---------- Level data ----------
const PALETTE = ['#ff5c7a','#ffb86c','#ffe66d','#5ef3ab','#59c3ff','#c17bff'];
// rows bottom-up per level; digits = hits needed (0 = empty)
const LEVELS = [
  ["00001111111100000000",
   "00001111111100000000",
   "00000111111100000000",
   "00000001111100000000"],
  ["01010101010101010101",
   "10101010101010101010",
   "01010101010101010101"],
  ["00000011111100000000",
   "00111111111111110000",
   "11111111111111111111",
   "00111111111111110000",
   "00000011111100000000"],
  ["11111100000000111111",
   "11110000111100001111",
   "11100011111100001111",
   "11000111111100011111",
   "10001111111111001111"],
  ["00200200200200200200",
   "01210121012102101210",
   "00200200200200200200"],
  ["00000111111100000000",
   "00011122222111000000",
   "00112222222221100000",
   "01112222222221110000",
   "11111222222211111000",
   "00000111111100000000"],
];

const COLS = 20, BW = W / COLS, BH = 26, TOP = 70;

let state, score, lives, level, combo, shake, flashT, particles, powerups, floats;

function makeLevel(n) {
  const pat = LEVELS[n % LEVELS.length];
  const bricks = [];
  pat.forEach((row, ri) => {
    for (let ci = 0; ci < COLS; ci++) {
      const v = +row[ci];
      if (v > 0) bricks.push({
        x: ci * BW, y: TOP + ri * (BH + 4),
        w: BW - 4, h: BH,
        hp: v, max: v,
        color: PALETTE[(ri + n) % PALETTE.length],
        alive: true, wobble: 0
      });
    }
  });
  return bricks;
}

function reset() {
  score = 0; lives = 3; level = 0;
  newBoard();
}
function newBoard() {
  state.bricks = makeLevel(level);
  state.balls = [];
  state.paddle = { x: W/2, w: 130, targetW: 130, h: 16 };
  powerups = [];
  addBall(true);
  state.stuck = true;
  combo = 0;
}
function addBall(held) {
  state.balls.push({ x: state.paddle.x, y: H - 60, vx: 0, vy: 0, r: 8, held: !!held, trail: [] });
}

// ---------- Input ----------
const keys = {};
addEventListener('keydown', e => {
  keys[e.code] = true;
  if (e.code === 'Space') { launch(); e.preventDefault(); }
  if (e.code === 'KeyP') paused = !paused;
});
addEventListener('keyup', e => keys[e.code] = false);
cvs.addEventListener('mousemove', e => {
  const r = cvs.getBoundingClientRect();
  mouse.x = (e.clientX - r.left) / r.width * W;
  mouse.active = true;
});
cvs.addEventListener('mousedown', launch);
cvs.addEventListener('touchmove', e => {
  const r = cvs.getBoundingClientRect();
  mouse.x = (e.touches[0].clientX - r.left) / r.width * W;
  mouse.active = true; e.preventDefault();
}, {passive:false});
cvs.addEventListener('touchstart', e => { cvs.dispatchEvent(new MouseEvent('mousemove',{clientX:e.touches[0].clientX,clientY:e.touches[0].clientY})); launch(); });
const mouse = { x: W/2, active: false };
let paused = false;

function launch() {
  audio();
  if (msgMode !== 'play') { if (msgMode !== 'start') { reset(); startLevel(); } return; }
  state.balls.forEach(b => {
    if (b.held) {
      b.held = false;
      const ang = -Math.PI/2 + (Math.random()-.5) * .5;
      const sp = baseSpeed();
      b.vx = Math.cos(ang)*sp; b.vy = Math.sin(ang)*sp;
      SFX.paddle();
    }
  });
}
function baseSpeed() { return 6.2 + level * 0.45; }

function showMessage(title, sub, mode) {
  msgEl.innerHTML = title + (sub ? `\n<small>${sub}</small>` : '');
  msgEl.style.opacity = 1;
  msgMode = mode;
}
function hideMsg() { msgEl.style.opacity = 0; msgMode = 'play'; }
let msgMode = 'start';

// ---------- Particles / juice ----------
function burst(x, y, color, n = 14, spd = 5) {
  for (let i = 0; i < n; i++) {
    const a = Math.random() * Math.PI * 2, s = Math.random()*spd + 1;
    particles.push({ x, y, vx: Math.cos(a)*s, vy: Math.sin(a)*s - 1.5,
      life: 1, size: Math.random()*4+2, color });
  }
}
function floatText(x, y, txt, color) {
  floats.push({ x, y, txt, color, life: 1 });
}
function addShake(n) { shake = Math.min(shake + n, 18); }

// ---------- Game loop ----------
reset();
particles = []; floats = []; shake = 0; flashT = 0;
state.mode = 'play';
showMessage('NEON BREAKER', 'move: mouse / ← →   •   launch: click / space', 'start');

let last = performance.now();
function frame(now) {
  let dt = Math.min((now - last) / 16.667, 2.5); last = now;
  if (!paused && msgMode === 'play') update(dt);
  draw(dt);
  requestAnimationFrame(frame);
}

function update(dt) {
  const p = state.paddle;

  // paddle control
  if (keys.ArrowLeft || keys.KeyA) { p.x -= 9 * dt; mouse.active = false; }
  if (keys.ArrowRight || keys.KeyD) { p.x += 9 * dt; mouse.active = false; }
  if (mouse.active) p.x += (mouse.x - p.x) * Math.min(1, .35 * dt);
  p.x = Math.max(p.w/2 + 8, Math.min(W - p.w/2 - 8, p.x));
  p.w += (p.targetW - p.w) * .1;

  // balls
  for (const b of state.balls) {
    if (b.held) { b.x = p.x; b.y = H - 60 - p.h/2 - b.r; continue; }
    b.trail.push({x: b.x, y: b.y});
    if (b.trail.length > 12) b.trail.shift();

    const steps = Math.ceil(Math.hypot(b.vx, b.vy) * dt / 6) || 1;
    for (let s = 0; s < steps; s++) {
      const f = dt / steps;
      b.x += b.vx * f; b.y += b.vy * f;

      // walls
      if (b.x < b.r + 4) { b.x = b.r + 4; b.vx = Math.abs(b.vx); SFX.wall(); burst(b.x, b.y, '#59c3ff', 4, 3); }
      if (b.x > W - b.r - 4) { b.x = W - b.r - 4; b.vx = -Math.abs(b.vx); SFX.wall(); burst(b.x, b.y, '#59c3ff', 4, 3); }
      if (b.y < b.r + 4) { b.y = b.r + 4; b.vy = Math.abs(b.vy); SFX.wall(); }

      // paddle
      const py = H - 60;
      if (b.vy > 0 && b.y + b.r > py && b.y - b.r < py + p.h &&
          b.x > p.x - p.w/2 - b.r && b.x < p.x + p.w/2 + b.r) {
        b.y = py - b.r;
        const hit = (b.x - p.x) / (p.w/2);
        const ang = hit * 1.05 - Math.PI/2 * 0; // reflect angle
        const speed = Math.min(Math.hypot(b.vx, b.vy) * 1.015, baseSpeed() + 3.5);
        const theta = -Math.PI/2 + hit * 1.05;
        b.vx = Math.cos(theta) * speed * Math.max(.35, Math.abs(Math.cos(theta)) * 0 + 1) * (hit < 0 ? -1 : 1) * Math.abs(Math.cos(theta));
        b.vy = Math.sin(theta) * speed;
        if (Math.abs(b.vy) < speed * .4) b.vy = -speed * .4;
        b.vx = Math.cos(theta) * speed;
        SFX.paddle();
        combo = 0;
        burst(b.x, py, '#c17bff', 8, 4);
        addShake(2);
      }

      // bricks
      for (const br of state.bricks) {
        if (!br.alive) continue;
        if (b.x + b.r > br.x && b.x - b.r < br.x + br.w &&
            b.y + b.r > br.y && b.y - b.r < br.y + br.h) {
          // resolve side
          const ox = Math.min(b.x + b.r - br.x, br.x + br.w - (b.x - b.r));
          const oy = Math.min(b.y + b.r - br.y, br.y + br.h - (b.y - b.r));
          if (ox < oy) b.vx = -b.vx; else b.vy = -b.vy;

          br.hp--; br.wobble = 1;
          combo++;
          const gain = 10 * Math.max(1, combo);
          score += gain;
          SFX.brick(combo);
          burst(b.x, b.y, br.color, br.hp <= 0 ? 16 : 8, br.hp <= 0 ? 6 : 3.5);
          if (combo >= 3) floatText(b.x, b.y - 10, '+' + gain, '#ffe66d');
          addShake(br.hp <= 0 ? 3 : 1.5);
          if (br.hp <= 0) {
            br.alive = false;
            if (Math.random() < .11) spawnPower(br.x + br.w/2, br.y + br.h/2);
          } else {
            br.color2 = '#fff';
          }
          updateHUD();
          break;
        }
      }
      // lose
      if (b.y > H + 40) { b.dead = true; break; }
    }
  }
  state.balls = state.balls.filter(b => !b.dead);

  if (state.balls.length === 0) {
    lives--;
    SFX.die(); addShake(10); flashT = .5;
    updateHUD();
    if (lives <= 0) {
      if (score > best) { best = score; localStorage.setItem('nb_best', best); }
      showMessage('GAME OVER', `score ${score} — press / click to restart`, 'over');
      state.over = true;
    } else {
      addBall(true); state.stuck = true; state.paddle.targetW = 130;
    }
  }

  // bricks cleared?
  if (state.bricks.every(b => !b.alive)) {
    level++;
    score += 250;
    SFX.level();
    flashT = .6; addShake(6);
    updateHUD();
    newBoard();
    setTimeout(() => { if (msgMode === 'play') {} }, 0);
  }

  // powerups
  for (const pu of powerups) {
    pu.y += 2.6 * dt; pu.rot += .08 * dt;
    if (pu.y > H) pu.dead = true;
    if (pu.y > H - 60 - 8 && pu.y < H - 60 + 20 && Math.abs(pu.x - p.x) < p.w/2 + 12) {
      pu.dead = true;
      applyPower(pu.kind);
    }
  }
  powerups = powerups.filter(p => !p.dead);

  // wobble decay
  for (const br of state.bricks) if (br.wobble > 0) br.wobble -= .1 * dt;

  // particles / floats
  for (const pt of particles) {
    pt.x += pt.vx * dt; pt.y += pt.vy * dt; pt.vy += .18 * dt;
    pt.life -= .03 * dt;
  }
  particles = particles.filter(p => p.life > 0);
  for (const f of floats) { f.y -= 1 * dt; f.life -= .025 * dt; }
  floats = floats.filter(f => f.life > 0);

  shake *= Math.pow(.85, dt);
  flashT = Math.max(0, flashT - .03 * dt);
}

function spawnPower(x, y) {
  const kinds = ['wide','multi','slow','life'];
  const r = Math.random();
  const kind = r < .38 ? 'wide' : r < .68 ? 'multi' : r < .9 ? 'slow' : 'life';
  powerups.push({ x, y, kind, rot: 0 });
}
function applyPower(kind) {
  SFX.power();
  const px = state.paddle.x;
  if (kind === 'wide') { state.paddle.targetW = Math.min(210, state.paddle.targetW + 40); floatText(px, H - 90, 'WIDE!', '#59c3ff'); }
  if (kind === 'multi') {
    const src = state.balls[0];
    if (src) for (let i = 0; i < 2; i++) {
      const a = Math.atan2(src.vy || -4, src.vx || 0) + (i ? .6 : -.6);
      const sp = baseSpeed();
      state.balls.push({ x: src.x, y: src.y, vx: Math.cos(a)*sp, vy: Math.sin(a)*sp, r: 8, held: false, trail: [] });
    }
    floatText(px, H - 90, 'MULTIBALL!', '#ff5c7a');
  }
  if (kind === 'slow') { state.balls.forEach(b => { b.vx *= .72; b.vy *= .72; }); floatText(px, H - 90, 'SLOW!', '#5ef3ab'); }
  if (kind === 'life') { lives++; updateHUD(); floatText(px, H - 90, '+1 LIFE', '#ffe66d'); }
  burst(px, H - 90, '#fff', 20, 5);
}

function updateHUD() {
  scoreEl.textContent = score;
  levelEl.textContent = level + 1;
  livesEl.textContent = lives >= 0 ? '♥'.repeat(Math.min(lives, 5)) || '0' : 0;
  bestEl.textContent = Math.max(best, score);
}

// ---------- Drawing ----------
let bgT = 0;
function draw(dt) {
  bgT += dt;
  ctx.save();
  if (shake > .4) ctx.translate((Math.random()-.5)*shake, (Math.random()-.5)*shake);

  // background
  const g = ctx.createLinearGradient(0, 0, 0, H);
  g.addColorStop(0, '#0d0d1c'); g.addColorStop(1, '#08080f');
  ctx.fillStyle = g; ctx.fillRect(-20, -20, W + 40, H + 40);

  // subtle grid
  ctx.strokeStyle = 'rgba(90,90,160,.07)'; ctx.lineWidth = 1;
  ctx.beginPath();
  for (let x = 0; x <= W; x += 45) { ctx.moveTo(x, 0); ctx.lineTo(x, H); }
  for (let y = 0; y <= H; y += 45) { ctx.moveTo(0, y); ctx.lineTo(W, y); }
  ctx.stroke();

  // frame walls
  ctx.fillStyle = 'rgba(140,150,255,.15)';
  ctx.fillRect(0, 0, 4, H); ctx.fillRect(W-4, 0, 4, H); ctx.fillRect(0, 0, W, 4);

  // bricks
  for (const br of state.bricks) {
    if (!br.alive) continue;
    const wob = br.wobble > 0 ? Math.sin(br.wobble * 30) * br.wobble * 3 : 0;
    ctx.save();
    ctx.translate(br.x + br.w/2 + wob, br.y + br.h/2);
    const dmg = br.hp / br.max;
    ctx.fillStyle = br.color;
    ctx.globalAlpha = br.hp > 1 ? .95 : .8;
    ctx.shadowColor = br.color; ctx.shadowBlur = 10;
    roundRect(-br.w/2, -br.h/2, br.w, br.h, 4); ctx.fill();
    if (br.hp > 1) {
      ctx.shadowBlur = 0;
      ctx.fillStyle = 'rgba(255,255,255,.75)';
      roundRect(-br.w/2 + 4, -br.h/2 + 4, br.w - 8, br.h - 8, 3); ctx.fill();
      ctx.fillStyle = br.color;
      ctx.font = 'bold 12px Courier New'; ctx.textAlign = 'center'; ctx.textBaseline = 'middle';
      ctx.fillText(br.hp, 0, 1);
    }
    ctx.restore();
  }

  // powerups
  for (const pu of powerups) {
    ctx.save();
    ctx.translate(pu.x, pu.y); ctx.rotate(pu.rot);
    const col = { wide:'#59c3ff', multi:'#ff5c7a', slow:'#5ef3ab', life:'#ffe66d' }[pu.kind];
    ctx.fillStyle = col; ctx.shadowColor = col; ctx.shadowBlur = 14;
    roundRect(-10, -10, 20, 20, 5); ctx.fill();
    ctx.shadowBlur = 0; ctx.fillStyle = '#0a0a12';
    ctx.font = 'bold 13px Courier New'; ctx.textAlign = 'center'; ctx.textBaseline = 'middle';
    ctx.fillText({wide:'W',multi:'3',slow:'S',life:'♥'}[pu.kind], 0, 1);
    ctx.restore();
  }

  // particles
  for (const pt of particles) {
    ctx.globalAlpha = Math.max(0, pt.life);
    ctx.fillStyle = pt.color;
    ctx.fillRect(pt.x - pt.size/2, pt.y - pt.size/2, pt.size, pt.size);
  }
  ctx.globalAlpha = 1;

  // floats
  ctx.font = 'bold 16px Courier New'; ctx.textAlign = 'center';
  for (const f of floats) {
    ctx.globalAlpha = Math.max(0, Math.min(1, f.life));
    ctx.fillStyle = f.color;
    ctx.fillText(f.txt, f.x, f.y);
  }
  ctx.globalAlpha = 1;

  // paddle
  const p = state.paddle;
  const py = H - 60;
  ctx.save();
  const pg = ctx.createLinearGradient(p.x - p.w/2, 0, p.x + p.w/2, 0);
  pg.addColorStop(0, '#c17bff'); pg.addColorStop(1, '#59c3ff');
  ctx.fillStyle = pg;
  ctx.shadowColor = '#8a6cff'; ctx.shadowBlur = 18;
  roundRect(p.x - p.w/2, py, p.w, p.h, 8); ctx.fill();
  ctx.shadowBlur = 0;
  ctx.fillStyle = 'rgba(255,255,255,.5)';
  roundRect(p.x - p.w/2 + 5, py + 4, p.w - 10, 4, 2); ctx.fill();
  ctx.restore();

  // balls + trails
  for (const b of state.balls) {
    for (let i = 0; i < b.trail.length; i++) {
      const t = b.trail[i];
      ctx.globalAlpha = (i / b.trail.length) * .35;
      ctx.fillStyle = '#fff';
      ctx.beginPath(); ctx.arc(t.x, t.y, b.r * (i / b.trail.length) * .8, 0, 7); ctx.fill();
    }
    ctx.globalAlpha = 1;
    ctx.fillStyle = '#fff';
    ctx.shadowColor = '#bfe8ff'; ctx.shadowBlur = 16;
    ctx.beginPath(); ctx.arc(b.x, b.y, b.r, 0, 7); ctx.fill();
    ctx.shadowBlur = 0;
    if (b.held) {
      ctx.globalAlpha = .4 + Math.sin(bgT * .12) * .3;
      ctx.strokeStyle = '#fff'; ctx.lineWidth = 1;
      ctx.setLineDash([4, 6]);
      ctx.beginPath(); ctx.moveTo(b.x, b.y - b.r);
      ctx.lineTo(b.x, b.y - 120); ctx.stroke();
      ctx.setLineDash([]);
      ctx.globalAlpha = 1;
    }
  }

  // flash
  if (flashT > 0) {
    ctx.fillStyle = `rgba(255,255,255,${flashT * .25})`;
    ctx.fillRect(0, 0, W, H);
  }

  // scanlines
  ctx.fillStyle = 'rgba(0,0,0,.12)';
  for (let y = 0; y < H; y += 3) ctx.fillRect(0, y, W, 1);

  if (paused && msgMode === 'play') {
    ctx.fillStyle = 'rgba(5,5,15,.6)'; ctx.fillRect(0, 0, W, H);
    ctx.fillStyle = '#fff'; ctx.font = 'bold 30px Courier New';
    ctx.textAlign = 'center';
    ctx.fillText('PAUSED', W/2, H/2);
  }
  ctx.restore();
}

function roundRect(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();
}

requestAnimationFrame(frame);
})();
</script>
</body>
</html>

Compare with

GLM 5.3: other attempts