Train snakeOne-line
GPT-6.1 Terra
by OpenAIReasoningClean
Running live in a sandboxed frame. Click inside it before using the keyboard.
How it went
- Total time
- 1 min 21 s
- Cost
- $0.035
- Output tokens
- 6,839
- Spent thinking
- 0 tokens
- Speed
- 85.6 tok/s
- Console errors
- 0
- First token after
- 1.2 s
- First visible text after
- 1.2 s
- Prompt tokens
- 361
- Code size
- 19.2 KB
- Finish reason
- stop
- Served by
- OpenAI
- Model id
openai/gpt-6.1-terra- Reasoning sent
{"effort":"none"}- Max output
- 128,000 tokens
- Generated
- Nov 12, 2026
What it was asked
The prompt
“make me a snake game where the snake is a little train with a locomotive and colorful wagons, picking up passengers waiting on the platform”
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 · 419 lines
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Platform Express</title>
<style>
:root {
--ink: #233f3a;
--muted: #758780;
--cream: #f7f5ec;
--green: #287a5c;
--gold: #edb957;
}
* { box-sizing: border-box; }
html, body { margin: 0; min-height: 100%; }
body {
color: var(--ink);
background: #e6eee5;
font-family: "Trebuchet MS", Arial, sans-serif;
padding: 24px;
}
.shell {
max-width: 1180px;
margin: auto;
background: var(--cream);
border: 1px solid rgba(53, 88, 69, .12);
border-radius: 24px;
overflow: hidden;
box-shadow: 0 20px 60px rgba(44, 75, 55, .12);
}
header {
height: 84px;
padding: 0 32px;
display: flex;
align-items: center;
justify-content: space-between;
gap: 20px;
background: #fffef8;
border-bottom: 1px solid #e7eadf;
}
.brand { display: flex; align-items: center; gap: 13px; }
.brand-icon {
width: 42px; height: 42px; border-radius: 13px;
background: #dceee0; display: grid; place-items: center;
}
.brand-icon svg { width: 25px; height: 25px; }
h1 { margin: 0; font-size: 21px; letter-spacing: -.8px; line-height: 1.05; }
.brand small { display: block; margin-top: 5px; color: #93a39a; font-size: 10px; font-weight: 800; letter-spacing: 1.8px; }
.header-right { display: flex; align-items: center; gap: 12px; }
.live { color: #4c8569; font-size: 11px; font-weight: bold; letter-spacing: .8px; }
.live:before { content: ""; display: inline-block; width: 7px; height: 7px; border-radius: 50%; background: #77b987; margin-right: 7px; vertical-align: 1px; }
.sound {
border: 1px solid #e5eae0; color: #668176; background: #fff; border-radius: 11px;
width: 38px; height: 38px; font-size: 17px; cursor: pointer;
}
main { padding: 27px 32px 25px; }
.intro { display: flex; justify-content: space-between; align-items: end; gap: 20px; margin-bottom: 20px; }
.eyebrow { color: #d49a46; font-size: 10px; font-weight: 900; letter-spacing: 2px; margin-bottom: 7px; }
h2 { margin: 0; font-size: 27px; letter-spacing: -1px; }
.intro p { margin: 6px 0 0; color: #87958b; font-size: 13px; }
.scores { display: flex; gap: 9px; }
.score {
min-width: 112px; padding: 10px 15px; background: #fffef9; border: 1px solid #e9ece2;
border-radius: 12px; display: flex; align-items: center; gap: 10px;
}
.score-icon { font-size: 18px; }
.score-label { color: #a0ada3; font-size: 9px; font-weight: 900; letter-spacing: 1px; white-space: nowrap; }
.score-value { color: #325f4b; font-size: 19px; font-weight: 900; line-height: 1.1; margin-top: 2px; }
.game-wrap { position: relative; overflow: hidden; border-radius: 16px; background: #dbe8d3; border: 1px solid #cfddca; }
canvas { display: block; width: 100%; height: auto; aspect-ratio: 22 / 11; }
.game-overlay {
position: absolute; left: 50%; bottom: 20px; transform: translateX(-50%);
display: flex; align-items: center; gap: 11px; padding: 10px 15px;
background: rgba(255, 254, 247, .95); border: 1px solid rgba(255,255,255,.9);
border-radius: 13px; box-shadow: 0 5px 18px rgba(48, 80, 52, .15);
white-space: nowrap; pointer-events: none;
}
.play-circle { width: 30px; height: 30px; background: #317e5e; color: white; display: grid; place-items: center; border-radius: 9px; font-size: 11px; padding-left: 2px; }
.overlay-title { font-size: 12px; font-weight: 900; color: #345d4b; }
.overlay-sub { margin-top: 3px; font-size: 10px; color: #90a095; }
.footer { display: flex; align-items: center; justify-content: space-between; gap: 18px; margin-top: 18px; }
.instructions { display: flex; align-items: center; gap: 12px; color: #7e8f83; font-size: 12px; }
.keys { display: flex; gap: 3px; }
kbd { width: 25px; height: 25px; display: grid; place-items: center; border: 1px solid #e1e7dc; border-bottom-width: 2px; border-radius: 6px; background: #fffef9; font: bold 12px Arial; color: #728b7b; }
.instructions strong { color: #52715f; }
.legend { display: flex; align-items: center; gap: 8px; color: #95a298; font-size: 11px; }
.legend-dot { width: 9px; height: 9px; border-radius: 50%; background: #ecb257; }
.restart { border: 0; background: transparent; color: #4a8468; font: bold 12px "Trebuchet MS", sans-serif; cursor: pointer; padding: 8px 0 8px 8px; }
.mobile-controls { display: none; justify-content: center; gap: 6px; margin-top: 15px; }
.mobile-controls button { width: 48px; height: 39px; border: 1px solid #dce6d8; border-radius: 9px; background: #fffef9; color: #4c7961; font-size: 19px; cursor: pointer; touch-action: manipulation; }
@media (max-width: 700px) {
body { padding: 10px; }
.shell { border-radius: 18px; }
header { height: 68px; padding: 0 16px; }
h1 { font-size: 18px; }
.live { display: none; }
main { padding: 20px 14px; }
.intro { align-items: start; }
h2 { font-size: 23px; }
.intro p { font-size: 12px; max-width: 220px; line-height: 1.4; }
.scores { gap: 5px; }
.score { min-width: 65px; padding: 8px; gap: 5px; }
.score-icon { font-size: 14px; }
.score-label { font-size: 8px; letter-spacing: .4px; }
.score-value { font-size: 16px; }
.game-wrap { border-radius: 12px; }
.game-overlay { bottom: 8px; padding: 7px 10px; gap: 7px; }
.play-circle { width: 25px; height: 25px; }
.overlay-title { font-size: 10px; }
.overlay-sub { font-size: 9px; }
.footer { margin-top: 12px; }
.instructions { font-size: 11px; gap: 7px; }
.keys { display: none; }
.legend { display: none; }
.mobile-controls { display: flex; }
}
@media (max-width: 430px) {
.intro { gap: 5px; }
h2 { font-size: 21px; }
.intro p { max-width: 175px; }
.score { min-width: 58px; }
.score-icon { display: none; }
.brand small { font-size: 8px; }
}
</style>
</head>
<body>
<div class="shell">
<header>
<div class="brand">
<div class="brand-icon">
<svg viewBox="0 0 32 32" fill="none" aria-hidden="true"><rect x="6" y="4" width="20" height="22" rx="6" fill="#398263"/><rect x="9" y="8" width="14" height="8" rx="2" fill="#E5F2E5"/><circle cx="11" cy="21" r="1.7" fill="#F4C36C"/><circle cx="21" cy="21" r="1.7" fill="#F4C36C"/><path d="M10 26l-2 3M22 26l2 3M10 28h12" stroke="#398263" stroke-width="2" stroke-linecap="round"/></svg>
</div>
<div><h1>Platform Express</h1><small>A LITTLE JOURNEY</small></div>
</div>
<div class="header-right">
<span class="live">ALL ABOARD</span>
<button class="sound" id="sound" aria-label="Toggle sound" title="Toggle sound">♪</button>
</div>
</header>
<main>
<div class="intro">
<div>
<div class="eyebrow">WELCOME TO THE STATION</div>
<h2>All aboard!</h2>
<p>Pick up passengers and watch your little train grow.</p>
</div>
<div class="scores">
<div class="score"><span class="score-icon">🎟️</span><div><div class="score-label">PASSENGERS</div><div class="score-value" id="score">0</div></div></div>
<div class="score"><span class="score-icon">✦</span><div><div class="score-label">BEST</div><div class="score-value" id="best">0</div></div></div>
</div>
</div>
<div class="game-wrap" id="game">
<canvas id="canvas" width="1100" height="550" aria-label="Snake game with a train picking up passengers"></canvas>
<div class="game-overlay" id="overlay">
<div class="play-circle">▶</div>
<div><div class="overlay-title" id="overlayTitle">Click or press a key to start</div><div class="overlay-sub" id="overlaySub">Enjoy the ride in demo mode</div></div>
</div>
</div>
<div class="footer">
<div class="instructions">
<div class="keys"><kbd>←</kbd><kbd>↑</kbd><kbd>↓</kbd><kbd>→</kbd></div>
<span><strong>Steer your train</strong> with arrow keys or WASD</span>
</div>
<div class="legend"><span class="legend-dot"></span> Pick up passengers <span style="padding:0 3px;color:#ccd5c9">•</span> Avoid your wagons</div>
<button class="restart" id="restart">↻ Restart</button>
</div>
<div class="mobile-controls" aria-label="Touch controls">
<button data-dir="left" aria-label="Left">←</button><button data-dir="up" aria-label="Up">↑</button><button data-dir="down" aria-label="Down">↓</button><button data-dir="right" aria-label="Right">→</button>
</div>
</main>
</div>
<script>
(() => {
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
const overlay = document.getElementById('overlay');
const title = document.getElementById('overlayTitle');
const sub = document.getElementById('overlaySub');
const scoreEl = document.getElementById('score');
const bestEl = document.getElementById('best');
const COLS = 22, ROWS = 11, S = 50;
const colors = ['#e9ac58', '#dc7770', '#70a9a2', '#aa8db2', '#e4b35e', '#7da979', '#d5856d'];
const dirs = {right:{x:1,y:0}, left:{x:-1,y:0}, up:{x:0,y:-1}, down:{x:0,y:1}};
let snake, direction, queued, passenger, score = 0, best = 0, mode = 'demo', last = 0, tickCount = 0, soundOn = false, audioCtx;
let demoTargets = [{x:15,y:4},{x:18,y:7},{x:10,y:8},{x:5,y:5},{x:8,y:2},{x:16,y:3},{x:19,y:8},{x:8,y:8}];
let targetIndex = 0;
function reset(demo) {
snake = [{x:8,y:4},{x:7,y:4},{x:6,y:4},{x:5,y:4},{x:4,y:4}];
direction = dirs.right; queued = dirs.right;
score = 0; tickCount = 0; targetIndex = 0;
passenger = demo ? {x:15,y:4} : {x:15,y:4};
scoreEl.textContent = score;
mode = demo ? 'demo' : 'playing';
overlay.style.display = demo ? 'flex' : 'none';
title.textContent = 'Click or press a key to start';
sub.textContent = 'Enjoy the ride in demo mode';
last = performance.now();
}
function roundRect(x,y,w,h,r,fill,stroke) {
ctx.beginPath(); ctx.roundRect(x,y,w,h,r);
if(fill){ctx.fillStyle=fill;ctx.fill();}
if(stroke){ctx.strokeStyle=stroke;ctx.stroke();}
}
function line(x1,y1,x2,y2,color,width=1) {
ctx.strokeStyle=color; ctx.lineWidth=width; ctx.beginPath();ctx.moveTo(x1,y1);ctx.lineTo(x2,y2);ctx.stroke();
}
function ellipse(x,y,rx,ry,color) {
ctx.fillStyle=color;ctx.beginPath();ctx.ellipse(x,y,rx,ry,0,0,Math.PI*2);ctx.fill();
}
function tree(x,y,scale=1) {
ctx.save(); ctx.translate(x,y); ctx.scale(scale,scale);
ellipse(0,14,19,6,'rgba(77,115,74,.10)');
roundRect(-3,0,6,17,2,'#a98d69');
ellipse(-8,-5,13,12,'#a5c49a'); ellipse(7,-7,14,13,'#91b789'); ellipse(0,-14,13,12,'#a9cba0');
ctx.restore();
}
function drawBackground() {
ctx.fillStyle='#e4eddc';ctx.fillRect(0,0,1100,550);
// Checkerboard lawn gives a gentle sense of the game grid.
for(let y=0;y<ROWS;y++) for(let x=0;x<COLS;x++) {
ctx.fillStyle=(x+y)%2===0?'#e1ebda':'#e6efdf';
ctx.fillRect(x*S,y*S,S,S);
}
// Station platform across the top.
ctx.fillStyle='#d4d7c1'; ctx.fillRect(0,0,1100,55);
ctx.fillStyle='#eee9d8'; ctx.fillRect(0,0,1100,46);
ctx.fillStyle='#d7ccb0';ctx.fillRect(0,46,1100,4);
ctx.fillStyle='#fff5cf';ctx.fillRect(0,51,1100,4);
for(let x=0;x<1100;x+=74) {
line(x,0,x,46,'rgba(176,171,150,.22)');
line(x+37,0,x+37,22,'rgba(176,171,150,.12)');
}
// A small station sign and platform details.
roundRect(33,8,126,28,5,'#4e806a');
ctx.fillStyle='#f8f8e9';ctx.font='bold 11px Trebuchet MS, sans-serif';ctx.textAlign='center';ctx.letterSpacing='2px';
ctx.fillText('GREENFIELD',96,26);ctx.letterSpacing='0px';
line(760,13,760,38,'#a5ad98',3); line(824,13,824,38,'#a5ad98',3);
roundRect(754,12,76,7,3,'#ba9d77'); roundRect(754,28,76,7,3,'#ba9d77');
roundRect(1002,9,48,29,4,'#d7e3cf');
ctx.fillStyle='#648b70';ctx.font='bold 9px Trebuchet MS';ctx.fillText('PLATFORM',1026,21);ctx.font='bold 11px Trebuchet MS';ctx.fillText('01',1026,33);
// Decorative greenery at the corners.
tree(35,112,.85); tree(1072,122,.8); tree(35,500,.85); tree(1064,495,1);
// little tufts and flowers
const plants=[[115,115],[235,95],[360,493],[933,111],[984,451],[160,446],[871,488],[518,97],[104,295],[1020,310]];
plants.forEach((p,i)=>{
ctx.strokeStyle='#bad1b2';ctx.lineWidth=2;ctx.beginPath();ctx.moveTo(p[0],p[1]+5);ctx.lineTo(p[0]-3,p[1]);ctx.moveTo(p[0],p[1]+5);ctx.lineTo(p[0]+4,p[1]-2);ctx.stroke();
if(i%3===0){ellipse(p[0]+6,p[1]-3,2,2,'#f5d39a');ellipse(p[0]+1,p[1]-5,2,2,'#f5d39a');}
});
// Small dashed boundary, like a garden railway.
ctx.setLineDash([4,7]);ctx.strokeStyle='rgba(130,167,126,.32)';ctx.lineWidth=1;
ctx.strokeRect(25,75,1050,450);ctx.setLineDash([]);
}
function drawPassenger(p, time) {
const x=p.x*S+25,y=p.y*S+25;
const bob=Math.sin(time/350)*1.7;
// A platform tile beneath the waiting passenger.
ellipse(x,y+19,22,8,'rgba(81,115,75,.12)');
roundRect(x-23,y-20,46,41,9,'#f8f1d8','#e6d5ae');
ctx.fillStyle='#e9bc61';
for(let i=0;i<4;i++)ctx.fillRect(x-19+i*10,y+15,5,3);
ctx.save();ctx.translate(x,y+bob);
// A tiny suitcase
roundRect(10,3,8,11,2,'#d79859');line(12,2,16,2,'#b97d48',1.5);
// legs, body, head, hair
line(-4,8,-5,14,'#5a6c62',3); line(4,8,5,14,'#5a6c62',3);
roundRect(-8,-3,16,13,5,'#d87364');
line(-7,0,-11,6,'#d68f71',3);line(7,0,10,5,'#d68f71',3);
ellipse(0,-9,7,7,'#efbc92');
ctx.fillStyle='#614e44';ctx.beginPath();ctx.arc(0,-10,7,Math.PI,Math.PI*2);ctx.fill();
ctx.restore();
// Floating attention mark.
ellipse(x+18,y-19,9,9,'#f5bd61');
ctx.fillStyle='#fffdf0';ctx.font='bold 13px Arial';ctx.textAlign='center';ctx.fillText('!',x+18,y-14);
}
function angleFor(i) {
let d;
if(i===0) d=direction;
else {
const prev=snake[i-1], cur=snake[i];
let dx=prev.x-cur.x,dy=prev.y-cur.y;
if(Math.abs(dx)>1)dx=-Math.sign(dx);
if(Math.abs(dy)>1)dy=-Math.sign(dy);
d={x:dx,y:dy};
}
return Math.atan2(d.y,d.x);
}
function drawCar(seg,i) {
const x=seg.x*S+25,y=seg.y*S+25;
ctx.save();ctx.translate(x,y);ctx.rotate(angleFor(i));
ellipse(0,16,23,6,'rgba(67,100,65,.15)');
// couplers
roundRect(-27,-3,9,6,2,'#6f7770');
if(i>0) roundRect(18,-3,9,6,2,'#6f7770');
// wheels
roundRect(-16,-18,9,5,2,'#53675b');roundRect(7,-18,9,5,2,'#53675b');
roundRect(-16,13,9,5,2,'#53675b');roundRect(7,13,9,5,2,'#53675b');
if(i===0) {
roundRect(-21,-14,42,28,7,'#387e60');
roundRect(-19,-12,37,24,6,'#43916d');
roundRect(-19,-11,15,22,4,'#2f7155');
roundRect(-15,-8,9,16,2,'#bfe0d0');
roundRect(-1,-10,15,20,5,'#5ca17a');
roundRect(6,-7,9,14,3,'#477b60');
roundRect(16,-12,6,24,3,'#315f4d');
roundRect(19,-9,4,5,2,'#f5d580');roundRect(19,4,4,5,2,'#f5d580');
roundRect(-21,-12,3,24,1,'#e5b860');
ellipse(3,0,4,4,'#315f4d');
} else {
const col=colors[(i-1)%colors.length];
roundRect(-21,-14,42,28,6,col);
roundRect(-18,-11,36,22,4,'rgba(255,255,255,.13)');
roundRect(-14,-8,10,16,3,'#dcefe5');
roundRect(4,-8,10,16,3,'#dcefe5');
line(-18,10,18,10,'rgba(106,83,64,.18)',1);
// passengers peeking through the windows
if(i%2===1){ellipse(-9,-1,3,3,'#e7b59a');ellipse(9,1,3,3,'#d8a881');}
else {ellipse(-9,1,3,3,'#d5a37d');ellipse(9,-1,3,3,'#edc5a0');}
}
ctx.restore();
}
function render(time) {
drawBackground();
drawPassenger(passenger,time);
for(let i=snake.length-1;i>=0;i--) drawCar(snake[i],i);
if(mode==='demo') {
roundRect(20,74,75,24,12,'rgba(255,255,249,.83)');
ellipse(33,86,3.5,3.5,'#85b98b');
ctx.fillStyle='#77947f';ctx.textAlign='left';ctx.font='bold 9px Trebuchet MS';ctx.fillText('DEMO RIDE',42,89);
}
}
function occupied(x,y,ignoreTail=false) {
return snake.slice(0,ignoreTail?-1:undefined).some(s=>s.x===x&&s.y===y);
}
function spawnPassenger() {
if(mode==='demo') {
for(let n=0;n<demoTargets.length;n++) {
targetIndex=(targetIndex+1)%demoTargets.length;
const p=demoTargets[targetIndex];
if(!occupied(p.x,p.y)){passenger={...p};return;}
}
}
const options=[];
for(let y=1;y<ROWS;y++) for(let x=1;x<COLS-1;x++) if(!occupied(x,y)) options.push({x,y});
if(options.length) passenger=options[Math.floor(Math.random()*options.length)];
}
function playTone() {
if(!soundOn)return;
try {
audioCtx=audioCtx||new (window.AudioContext||window.webkitAudioContext)();
const osc=audioCtx.createOscillator(), gain=audioCtx.createGain();
osc.type='sine';osc.frequency.setValueAtTime(660,audioCtx.currentTime);
osc.frequency.exponentialRampToValueAtTime(880,audioCtx.currentTime+.1);
gain.gain.setValueAtTime(.045,audioCtx.currentTime);gain.gain.exponentialRampToValueAtTime(.001,audioCtx.currentTime+.18);
osc.connect(gain);gain.connect(audioCtx.destination);osc.start();osc.stop(audioCtx.currentTime+.18);
} catch(e) {}
}
function autoDirection() {
const head=snake[0];
const candidates=Object.values(dirs).filter(d=>!(d.x===-direction.x&&d.y===-direction.y));
candidates.sort((a,b)=>{
const dist=d=>Math.abs(head.x+d.x-passenger.x)+Math.abs(head.y+d.y-passenger.y);
return dist(a)-dist(b);
});
for(const d of candidates) {
const nx=head.x+d.x,ny=head.y+d.y;
if(nx>=0&&nx<COLS&&ny>=1&&ny<ROWS&&!occupied(nx,ny,true)) return d;
}
return direction;
}
function step() {
if(mode==='over') return;
direction = mode==='demo' ? autoDirection() : queued;
const head={x:snake[0].x+direction.x,y:snake[0].y+direction.y};
const eating=head.x===passenger.x&&head.y===passenger.y;
if(head.x<0||head.x>=COLS||head.y<1||head.y>=ROWS||occupied(head.x,head.y,!eating)) {
if(mode==='demo'){reset(true);return;}
mode='over'; overlay.style.display='flex';title.textContent='End of the line!';
sub.textContent='Click or press a key to ride again';return;
}
snake.unshift(head);
if(eating){
score++;scoreEl.textContent=score;
if(mode==='playing'&&score>best){best=score;bestEl.textContent=best;}
playTone();spawnPassenger();
} else snake.pop();
tickCount++;
}
function frame(time) {
if(time-last>(mode==='demo'?220:180)){step();last=time;}
render(time);requestAnimationFrame(frame);
}
function start() { if(mode!=='playing') reset(false); }
function setDirection(d) {
if(mode!=='playing') start();
if(d.x!==-direction.x||d.y!==-direction.y) queued=d;
}
document.addEventListener('keydown',e=>{
const key=e.key.toLowerCase();
const map={arrowleft:'left',a:'left',arrowright:'right',d:'right',arrowup:'up',w:'up',arrowdown:'down',s:'down'};
if(map[key]){e.preventDefault();setDirection(dirs[map[key]]);}
else if(mode!=='playing') start();
});
document.getElementById('game').addEventListener('click',()=>{if(mode!=='playing') start();});
document.getElementById('restart').addEventListener('click',()=>reset(false));
document.querySelectorAll('[data-dir]').forEach(btn=>btn.addEventListener('click',()=>setDirection(dirs[btn.dataset.dir])));
document.getElementById('sound').addEventListener('click',e=>{
soundOn=!soundOn;e.currentTarget.textContent=soundOn?'♫':'♪';
e.currentTarget.style.color=soundOn?'#2e805b':'#668176';
e.currentTarget.setAttribute('aria-label',soundOn?'Mute sound':'Enable sound');
if(soundOn)playTone();
});
reset(true); requestAnimationFrame(frame);
})();
</script>
</body>
</html>