I just generated this simple game from a prompt that Prompt Smith created based on my very rough request: “I want a prompt that can get ChatGPT to generate a small game for me.” The game took about five minutes to generate, required no corrections, and the code ran without errors.
<!doctype html>
<!--
How to Play
Goal: Collect as many teal shards as you can before time runs out; avoid red seekers.
Controls: Arrow keys move. On touch/mouse, press and hold to guide the player.
Pause/Resume: Press P or Space. Restart: Press R or click Restart.
Toggle sound (muted by default) with the Sound button (or M).
-->
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover" />
<title>Shard Sprint</title>
<style>
:root{
--bg:#121212; --ink:#ffffff; --accent1:#4ecdc4; --accent2:#ff6b6b; --muted:#2a2a2a;
}
*{box-sizing:border-box}
html,body{height:100%;margin:0;background:var(--bg);color:var(--ink);font:16px/1.4 system-ui,Segoe UI,Roboto,Helvetica,Arial,sans-serif}
header,footer{max-width:880px;margin:0 auto;padding:12px 16px}
header{display:flex;gap:16px;align-items:center;justify-content:space-between}
#hud{display:flex;gap:16px;align-items:center;flex-wrap:wrap}
.pill{padding:8px 12px;border-radius:999px;background:var(--muted);min-width:120px;text-align:center}
.controls{display:flex;gap:8px;flex-wrap:wrap}
button{
padding:8px 12px;border-radius:12px;border:2px solid var(--ink);background:transparent;color:var(--ink);
cursor:pointer;min-width:104px;font-weight:600;letter-spacing:.2px
}
button:focus{outline:3px solid var(--accent1);outline-offset:2px}
button[aria-pressed="true"]{border-color:var(--accent1)}
main{max-width:880px;margin:0 auto;padding:0 16px 16px}
#wrap{position:relative; width:100%; aspect-ratio:16/9; background:#0b0b0b; border-radius:16px; overflow:hidden; box-shadow:0 8px 24px rgba(0,0,0,.5)}
canvas{width:100%;height:100%;touch-action:none;display:block}
#overlay{
position:absolute;inset:0;display:flex;align-items:center;justify-content:center;
pointer-events:none
}
.banner{
background:rgba(0,0,0,.72);border:2px solid var(--ink);color:var(--ink);
padding:16px 24px;border-radius:16px;text-align:center;max-width:80%;
}
.banner h2{margin:0 0 8px;font-size:24px}
.banner p{margin:4px 0}
.accent {color:var(--accent1)}
.warn {color:var(--accent2)}
footer{opacity:.75;font-size:12px;text-align:center}
@media (max-width:480px){
.pill{min-width:auto}
button{min-width:auto}
}
</style>
</head>
<body>
<header>
<div id="hud">
<div id="score" class="pill" aria-live="polite">Score: 0</div>
<div id="time" class="pill" aria-live="polite">Time: 60.0</div>
<div id="status" class="pill">Running</div>
</div>
<div class="controls">
<button id="pauseBtn" aria-pressed="false" title="Pause/Resume (P or Space)">Pause</button>
<button id="restartBtn" title="Restart (R)">Restart</button>
<button id="soundBtn" aria-pressed="false" title="Toggle Sound (M)">Sound: Off</button>
</div>
</header>
<main>
<div id="wrap" role="application" aria-label="Shard Sprint game area">
<canvas id="c" tabindex="0" aria-label="Game Canvas"></canvas>
<div id="overlay" aria-hidden="true">
<div class="banner" id="banner" hidden>
<h2 id="bannerTitle">Game Over</h2>
<p id="bannerMsg">Press <span class="accent">R</span> to restart.</p>
</div>
</div>
</div>
</main>
<footer>Colorblind-safe palette · High contrast UI · Keyboard-first controls</footer>
<script>
(()=>{"use strict";
// --- Short helpers (no per-frame allocations) ---
const $=sel=>document.querySelector(sel);
const clamp=(v,a,b)=>v<a?a:(v>b?b:v);
const rand=(a,b)=>Math.random()*(b-a)+a;
const dist=(ax,ay,bx,by)=>Math.hypot(ax-bx,ay-by);
const sign=(x)=>x?x<0?-1:1:0;
// --- Canvas & DPI scaling ---
const canvas=$("#c"), ctx=canvas.getContext("2d");
const wrap=$("#wrap");
let W=800,H=450,dpr=1;
function resize(){
const r=wrap.getBoundingClientRect();
dpr=window.devicePixelRatio||1;
canvas.width=Math.floor(r.width*dpr);
canvas.height=Math.floor(r.height*dpr);
W=r.width; H=r.height;
ctx.setTransform(dpr,0,0,dpr,0,0);
}
window.addEventListener("resize", resize,{passive:true}); resize();
// --- UI elements ---
const scoreEl=$("#score"), timeEl=$("#time"), statusEl=$("#status");
const pauseBtn=$("#pauseBtn"), restartBtn=$("#restartBtn"), soundBtn=$("#soundBtn");
const banner=$("#banner"), bannerTitle=$("#bannerTitle"), bannerMsg=$("#bannerMsg");
// --- Audio (muted by default) ---
let ac=null, muted=true;
function ensureAC(){ if(!ac){ try{ ac=new (window.AudioContext||window.webkitAudioContext)(); }catch{} } }
function beep(freq=440, dur=0.07, type="sine", vol=0.07){
if(muted||!ac) return;
const t=ac.currentTime;
const o=ac.createOscillator(), g=ac.createGain();
o.type=type; o.frequency.value=freq;
g.gain.setValueAtTime(0, t); g.gain.linearRampToValueAtTime(vol, t+0.005);
g.gain.exponentialRampToValueAtTime(0.0001, t+dur);
o.connect(g).connect(ac.destination); o.start(t); o.stop(t+dur+0.02);
}
// --- Game state ---
const state={
running:true, over:false, win:false,
score:0, time:60, target:25, difficulty:1,
player:{x:0,y:0,r:10,speed:190,vx:0,vy:0},
shards:[], enemies:[], input:{left:0,right:0,up:0,down:0},
pointer:{active:false,x:0,y:0}
};
// --- Game setup/reset ---
function reset(){
state.running=true; state.over=false; state.win=false;
banner.hidden=true; statusEl.textContent="Running";
state.score=0; state.time=60; state.difficulty=1;
state.player.r=Math.max(8,Math.min(W,H)*0.02);
state.player.x=W*0.5; state.player.y=H*0.5; state.player.vx=0; state.player.vy=0;
state.shards.length=0; state.enemies.length=0;
for(let i=0;i<5;i++) spawnShard();
for(let i=0;i<2;i++) spawnEnemy();
updateHUD();
canvas.focus();
}
// --- Entities creation ---
function spawnShard(){
// Keep away from player spawn
let x,y,tries=0;
do{ x=rand(16,W-16); y=rand(16,H-16); tries++; }while(dist(x,y,state.player.x,state.player.y)<64 && tries<25);
state.shards.push({x,y,r:8});
}
function spawnEnemy(){
// Spawn at edges aiming inward
const side=Math.floor(rand(0,4));
const pad=16;
const pos=[
{x:rand(pad,W-pad), y:pad}, {x:rand(pad,W-pad), y:H-pad},
{x:pad, y:rand(pad,H-pad)}, {x:W-pad, y:rand(pad,H-pad)}
][side];
state.enemies.push({x:pos.x,y:pos.y,r:10,base:70+rand(0,30)});
}
// --- Input: keyboard, mouse, touch ---
function key(e,down){
const k=e.key;
if(["ArrowUp","ArrowDown","ArrowLeft","ArrowRight"," "].includes(k)) e.preventDefault();
if(k==="ArrowLeft") state.input.left=down?1:0;
if(k==="ArrowRight") state.input.right=down?1:0;
if(k==="ArrowUp") state.input.up=down?1:0;
if(k==="ArrowDown") state.input.down=down?1:0;
if(down && (k==="p"||k==="P"||k===" ")) togglePause();
if(down && (k==="r"||k==="R")) reset();
if(down && (k==="m"||k==="M")) toggleSound();
}
document.addEventListener("keydown", e=>key(e,true));
document.addEventListener("keyup", e=>key(e,false));
// Pointer to guide the player smoothly
function setPointer(e,active){
const rect=canvas.getBoundingClientRect();
const x=(e.clientX!==undefined?e.clientX:e.touches[0].clientX)-rect.left;
const y=(e.clientY!==undefined?e.clientY:e.touches[0].clientY)-rect.top;
state.pointer.active=active; state.pointer.x=x; state.pointer.y=y;
}
canvas.addEventListener("pointerdown", e=>{ ensureAC(); setPointer(e,true); canvas.setPointerCapture(e.pointerId); });
canvas.addEventListener("pointermove", e=>{ if(state.pointer.active) setPointer(e,true); });
canvas.addEventListener("pointerup", e=>{ state.pointer.active=false; });
canvas.addEventListener("touchstart", e=>{ ensureAC(); setPointer(e,true); },{passive:false});
canvas.addEventListener("touchmove", e=>{ if(state.pointer.active) setPointer(e,true); },{passive:false});
canvas.addEventListener("touchend", e=>{ state.pointer.active=false; },{passive:true});
// --- Buttons ---
pauseBtn.addEventListener("click", togglePause);
restartBtn.addEventListener("click", reset);
soundBtn.addEventListener("click", toggleSound);
function togglePause(){
if(state.over) return;
state.running=!state.running;
pauseBtn.setAttribute("aria-pressed", String(!state.running));
pauseBtn.textContent=state.running?"Pause":"Resume";
statusEl.textContent=state.running?"Running":"Paused";
if(state.running) last=performance.now();
}
function toggleSound(){
ensureAC();
muted=!muted; if(ac && ac.state==="suspended" && !muted) ac.resume();
soundBtn.setAttribute("aria-pressed", String(!muted));
soundBtn.textContent=muted?"Sound: Off":"Sound: On";
if(!muted) beep(600,0.05,"square",0.03);
}
// --- HUD update ---
function updateHUD(){
scoreEl.textContent="Score: "+state.score;
timeEl.textContent="Time: "+state.time.toFixed(1);
}
// --- Difficulty ramp ---
function rampDifficulty(){
// Increase with time and score; spawn enemies periodically
const s=state.score, t=state.time;
const targetEnemies=2+Math.floor((25 - t)/10)+Math.floor(s/6);
while(state.enemies.length<targetEnemies) spawnEnemy();
state.difficulty=1 + (25 - t)*0.03 + s*0.05; // grows as time dwindles / score rises
}
// --- Update loop ---
let last=performance.now();
function update(dt){
// Timer
state.time=Math.max(0, state.time - dt);
if(state.time<=0 && !state.over){ end(false, "Time's up!"); }
// Movement: keyboard
const p=state.player, sp=state.player.speed;
let ax=state.input.right-state.input.left;
let ay=state.input.down-state.input.up;
// Movement: pointer guidance
if(state.pointer.active){
const dx=state.pointer.x - p.x, dy=state.pointer.y - p.y;
const d=Math.hypot(dx,dy);
if(d>1){ ax = dx/d; ay = dy/d; }
}
const mag=Math.hypot(ax,ay)||1;
p.vx = (ax/mag)*sp;
p.vy = (ay/mag)*sp;
p.x = clamp(p.x + p.vx*dt, p.r, W-p.r);
p.y = clamp(p.y + p.vy*dt, p.r, H-p.r);
// Shard collection
for(let i=state.shards.length-1;i>=0;i--){
const s=state.shards[i];
if(dist(p.x,p.y,s.x,s.y) < p.r + s.r){
state.shards.splice(i,1);
state.score++;
beep(740,0.06,"triangle",0.05);
spawnShard();
if(state.score%5===0) spawnEnemy();
if(state.score>=state.target && !state.over){ end(true, "You reached "+state.target+"!"); }
}
}
// Enemies chase
for(let i=0;i<state.enemies.length;i++){
const e=state.enemies[i];
const dx=p.x-e.x, dy=p.y-e.y, d=Math.hypot(dx,dy)||1;
const speed=(e.base + 30*state.difficulty);
e.x += (dx/d)*speed*dt;
e.y += (dy/d)*speed*dt;
// Keep within bounds
e.x=clamp(e.x, e.r, W-e.r); e.y=clamp(e.y, e.r, H-e.r);
// Collision with player -> lose
if(dist(p.x,p.y,e.x,e.y) < p.r + e.r){ end(false,"Caught by a seeker!"); break; }
}
rampDifficulty();
updateHUD();
}
// --- Draw loop ---
function draw(){
// Background grid for subtle motion cue
ctx.clearRect(0,0,W,H);
ctx.fillStyle="#0b0b0b"; ctx.fillRect(0,0,W,H);
ctx.lineWidth=1; ctx.strokeStyle="rgba(255,255,255,0.06)";
const step=16;
for(let x=0;x<=W;x+=step){ ctx.beginPath(); ctx.moveTo(x,0); ctx.lineTo(x,H); ctx.stroke(); }
for(let y=0;y<=H;y+=step){ ctx.beginPath(); ctx.moveTo(0,y); ctx.lineTo(W,y); ctx.stroke(); }
// Shards (teal squares)
ctx.fillStyle=getComputedStyle(document.documentElement).getPropertyValue('--accent1');
for(let i=0;i<state.shards.length;i++){
const s=state.shards[i]; ctx.fillRect(s.x-6,s.y-6,12,12);
}
// Player (ring)
ctx.beginPath(); ctx.arc(state.player.x,state.player.y,state.player.r,0,Math.PI*2);
ctx.strokeStyle="#ffffff"; ctx.lineWidth=3; ctx.stroke();
// Enemies (red triangles)
ctx.fillStyle=getComputedStyle(document.documentElement).getPropertyValue('--accent2');
for(let i=0;i<state.enemies.length;i++){
const e=state.enemies[i];
const dx=state.player.x-e.x, dy=state.player.y-e.y, a=Math.atan2(dy,dx);
const r=12;
ctx.beginPath();
ctx.moveTo(e.x + Math.cos(a)*r, e.y + Math.sin(a)*r);
ctx.lineTo(e.x + Math.cos(a+2.5)*r, e.y + Math.sin(a+2.5)*r);
ctx.lineTo(e.x + Math.cos(a-2.5)*r, e.y + Math.sin(a-2.5)*r);
ctx.closePath(); ctx.fill();
}
// Edge warnings if near timeout
if(state.time<10 && !state.over){
ctx.fillStyle="rgba(255,107,107,0.08)";
ctx.fillRect(0,0,W,H);
}
}
// --- End game ---
function end(win,msg){
state.over=true; state.running=false; state.win=win;
pauseBtn.textContent="Resume"; pauseBtn.setAttribute("aria-pressed","true");
statusEl.textContent="Ended";
banner.hidden=false;
bannerTitle.textContent=win?"You Win!":"Game Over";
bannerMsg.innerHTML=(msg||"")+'<br>Press <span class="accent">R</span> to restart.';
beep(win?880:220,0.2, win?"sawtooth":"square",0.05);
}
// --- Main loop ---
function loop(t){
if(!last) last=t;
const dt = state.running ? Math.min(0.05,(t-last)/1000) : 0;
if(state.running) update(dt);
draw();
last=t; requestAnimationFrame(loop);
}
// --- Start ---
reset(); requestAnimationFrame(loop);
})();
</script>
</body>
</html>