A drop-in endless-runner game for your 404 page. One script tag, zero config.
Think Chrome's offline dinosaur - except themed around the 404 you just hit. The "0" jumps over 404, 500, 503 and 418 obstacles. Spacebar or tap to jump. Persistent high score. Light/dark theme follows the user's system preference.
Pick the path that matches your stack.
<divdata-404-runner></div><scriptsrc="https://cdn.jsdelivr.net/npm/runner-404"></script>That's it. The script finds every element with data-404-runner and mounts the game inside.
<scriptsrc="https://cdn.jsdelivr.net/npm/runner-404"></script><runner-404theme="auto"></runner-404>npm install runner-404import{mount}from'runner-404';constgame=mount('#game-container',{theme: 'dark'});// later, if you need to:game.destroy();All three embed paths accept the same options. Names differ between paths:
| Option | <div data-404-runner> attribute | <runner-404> attribute | mount() option |
|---|---|---|---|
| Theme | data-theme="auto" | theme="auto" | theme |
| Width (px) | data-width="600" | width="600" | width |
| Height (px) | data-height="200" | height="200" | height |
| Auto-start | data-autostart | autostart | autoStart |
| Tap anywhere | data-tap-anywhere="false" | tap-anywhere="false" | tapAnywhere |
theme accepts 'light', 'dark', or 'auto' (follows prefers-color-scheme, default).
tapAnywhere defaults to true: any tap on the page jumps, except on links, buttons, inputs, and other interactive elements. Set to false to constrain touch input to the game canvas only.
constgame=mount(target,options);game.start();// begin the loopgame.stop();// pause the loopgame.reset();// back to ready stategame.destroy();// remove canvas, detach listenersgame.score;// current scoregame.highScore;// persisted besttarget can be a CSS selector string or an HTMLElement.
- Tap Space / ↑ / W for a short hop (good for landing on platforms)
- Hold the same keys for a full jump (good for clearing tall walls)
- Tap / hold anywhere on the page on mobile - interactive elements (links, buttons, inputs) are excluded so they still work normally
- Canvas is
role="img"with a descriptivearia-label - Respects
prefers-reduced-motion - Keyboard input is global once mounted — players don't have to click first
Modern evergreen browsers (Chrome, Firefox, Safari, Edge). Uses canvas 2D, custom elements, and prefers-color-scheme. No polyfills needed for the supported targets.
Approximately 5 KB gzipped (IIFE build, minified). No runtime dependencies.
npm install
npm run dev # tsup watch mode
npm test# vitest
npm run build # produces dist/ (ESM + CJS + IIFE)Open the demo/ HTML files after a build to play locally.
MIT