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.
<div data-404-runner></div>
<script src="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.
<script src="https://cdn.jsdelivr.net/npm/runner-404"></script>
<runner-404 theme="auto"></runner-404>npm install runner-404import { mount } from 'runner-404';
const game = 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.
const game = mount(target, options);
game.start(); // begin the loop
game.stop(); // pause the loop
game.reset(); // back to ready state
game.destroy(); // remove canvas, detach listeners
game.score; // current score
game.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