Core Loop
- The runner auto-moves forward at constant base speed.
- Player can jump or slide using space / tap / swipe controls.
- Obstacles spawn from the right at increasing frequency and speed.
- Collectibles appear in patterns that reward timing and positioning.
- Collision ends the run and shows final score plus restart.
Procedural Generation
- Use seeded random or time based values to place obstacles in lanes.
- Gaps between obstacles grow smaller as distance increases per [[Ramp Rate]].
- Occasional powerup pickups temporarily slow obstacles or grant double jump.
- Background elements scroll at parallax speeds to sell forward motion.
Controls and Feedback
- Keyboard: Space or Up to jump, Down or S to slide.
- Touch: Tap upper half to jump, lower half to slide.
- Visual feedback includes squash/stretch on the character during actions.
- Sound cues (optional) for jump, slide, pickup, and collision.
Scoring and Progression
- Distance traveled is the primary score, shown as meters or points.
- Combo multiplier increases with consecutive pickups without hitting anything.
- High score persists in localStorage across sessions.
- Milestones every 500 units unlock new [[Theme]] visual variants for the character.
Win / Lose and Restart
- Lose condition is any obstacle collision or falling off the world floor.
- On death overlay shows score, best, and distance breakdown.
- Restart instantly reloads the run with fresh seed but same parameters.
- A practice mode toggle disables game over for learning the patterns.
Themed Art and Feel
- Apply [[Theme]] palette to floor, sky, character, and obstacle sprites via CSS filters or simple rect fills.
- Particles emit on jump landing and collectible pickup.
- Screen shake intensity scales with near-miss and speed.
- HUD uses bold chunky numbers and minimal chrome.
Full Playable Demo
<!DOCTYPE html>
<html><head><meta charset="utf-8"><title>[[Theme]] Runner</title>
<style>body{margin:0;background:#111;color:#fff;font-family:sans-serif}canvas{display:block;margin:20px auto;border:4px solid #333}</style>
</head><body>
<h1 style="text-align:center">[[Theme]] Endless Runner - [[Main Character]]</h1>
<canvas id="c" width="720" height="320"></canvas>
<p style="text-align:center">Space/↑/Tap = Jump • ↓/S = Slide • R = Restart</p>
<script>
const c=document.getElementById('c'),x=c.getContext('2d');
let d=0,s=0,alive=true,vy=0,onG=true,slide=false,obs=[],coins=[],hs=localStorage.getItem('runhs')||0;
function reset(){d=0;s=0;alive=true;vy=0;onG=true;slide=false;obs=[];coins=[]; }
window.onkeydown=e=>{if(e.key==='r'||e.key==='R')reset(); if(!alive)return; if((e.key===' '||e.key==='ArrowUp')&&onG){vy=-11;onG=false;} if(e.key==='ArrowDown')slide=true;};
window.onkeyup=e=>{if(e.key==='ArrowDown')slide=false;};
c.onclick=()=>{if(!alive)reset(); else if(onG){vy=-11;onG=false;} };
function spawn(){
if(Math.random()<0.08) obs.push({x:800,y:240,w:30,h:40});
if(Math.random()<0.12) coins.push({x:800,y:180+Math.random()*50});
}
function upd(){
if(!alive)return;
d+=1.6; s=Math.floor(d);
vy+=0.6; let py=220+vy; if(py>240){py=240;vy=0;onG=true;}
if(slide) py=250;
for(let o of obs){ o.x-=3.8; if(o.x<-40)obs.shift(); if(Math.abs(o.x-80)<35 && Math.abs(py-o.y)<30) alive=false; }
for(let i=coins.length-1;i>=0;i--){ coins[i].x-=3.8; if(coins[i].x<-20)coins.splice(i,1); if(Math.abs(coins[i].x-80)<25 && Math.abs(py-coins[i].y)<25){s+=50;coins.splice(i,1);} }
if(Math.random()<0.9)spawn();
if(d>500 && Math.random()<0.03)obs.push({x:820,y:250,w:60,h:20}); // low wall
}
function drw(){
x.fillStyle='#0a0f1e';x.fillRect(0,0,720,320);
x.fillStyle='#334155';x.fillRect(0,260,720,60);
x.fillStyle='#64748b'; for(let i=0;i<8;i++) x.fillRect((i*110 - (d%110)),255,60,8);
x.fillStyle='#f59e0b'; x.save(); x.translate(80, onG&&!slide?220+vy : slide?250:220+vy);
x.fillRect(-14,-30,28,30); // simple char
x.restore();
x.fillStyle='#ef4444'; for(let o of obs) x.fillRect(o.x,o.y,o.w,o.h);
x.fillStyle='#eab308'; for(let k of coins) x.beginPath(),x.arc(k.x,k.y,8,0,7),x.fill();
x.fillStyle='#fff';x.font='20px monospace';x.fillText('DIST '+s,20,30); x.fillText('BEST '+hs,520,30);
if(!alive){ x.fillText('GAME OVER - press R or click',220,140); if(s>hs){hs=s;localStorage.setItem('runhs',hs);} }
}
function loop(){upd();drw();requestAnimationFrame(loop);}
reset();loop();
</script></body></html>
Difficulty Tuning
- Base scroll speed starts moderate and ramps by 0.02 per 100 distance.
- Spawn rate increases logarithmically after 300 distance.
- Add more low obstacles as [[Ramp Rate]] parameter grows.
- Provide a beginner toggle that halves spawn frequency.
Polish and Juice
- Trail particles behind the character on high speed.
- Camera bob using subtle vertical shift synced to run cycle.
- Color shift the sky and floor tint as distance climbs for sense of progress.
- Mute button and simple key legend in the corner.
Edge Cases Handled
- Rapid double jump prevented by onG flag.
- Obstacles despawn cleanly when off left edge.
- Score never goes negative.
- High score only updates on death with a new record.
This endless runner is immediately fun, easy to skin with [[Theme]], and demonstrates all the required mechanics in a compact runnable package. Extend spawn logic and add powerups for richer generator output.
Extended Design Notes
- Consider adding variable lane widths that force the player to change horizontal position.
- Weather or time-of-day cycles can be layered on top of distance for visual variety.
- Leaderboards can be faked client-side or connected to a simple backend later.
- Mobile vibration on near-miss adds tactile juice without extra dependencies.
Common Mistakes to Avoid in Generated Games
- Do not spawn obstacles that require pixel-perfect timing on the first 30 seconds.
- Avoid invisible walls; always communicate boundaries with clear visual language.
- Keep the character silhouette distinct against every background variant.
- Never let the speed ramp become so fast that reaction time drops below 200ms on average hardware.
Replay and Monetization Hooks
- Daily challenge seeds that are the same for all players on a given calendar day.
- Cosmetic skins for [[Main Character]] unlocked via cumulative distance milestones.
- Ad-supported extra continues that do not affect core fairness.
- Export run replay as GIF or short video clip using canvas capture for sharing.
Technical Implementation Guidance
- Use requestAnimationFrame for smooth 60 fps updates.
- Separate physics step from render step for future deterministic replays.
- Preload any audio assets and gracefully degrade when WebAudio is unavailable.
- Store only the minimal seed + distance in save files to keep persistence tiny.
Testing Checklist Before Shipping a Generated Build
- Play 20 consecutive runs and confirm no unbeatable patterns appear before 400 distance.
- Verify high score saves and restores after browser restart.
- Test on both desktop keyboard and mobile touch devices.
- Confirm that the demo remains under 15 KB of gzipped source for fast loads.
The complete specification plus embedded demo gives a production quality starting point for any [[Theme]] endless runner. Add art, sound, and new obstacle types to produce an infinite family of variants.
---
Endless Runner Maker template. Auto forward motion, tight controls, procedural challenge.