Challenge
Games

A small student collection of browser games — hockey, racing, and hangman — built to open and play with no install and no build step.

Today’s challenge Loading… No attempts yet Scroll to play

Pick a game

Three titles. Click one and jump in.

How it works

A quick look at a few real pieces of this project — plain language for students, easy for a teacher to skim.

Hub page structure

index.html

The landing page is one HTML file with two big sections: an overview at the top, then a catalog of game cards. Each card is a normal link to that game’s page.

<header class="overview">…</header>
<section class="catalog" id="games">
  <a class="game" href="rifthockey.html">…</a>
  <a class="game" href="street-racing/">…</a>
  <a class="game" href="hangman.html">…</a>
</section>

Daily challenge rotation

index.html · script

Today’s featured game is chosen from the calendar date (epochDay % 3). Your best attempt is stored in the browser with localStorage under a key like cg-daily-2026-10-07.

function challengeForDate(d) {
  var epochDay = Math.floor(
    Date.UTC(d.getFullYear(), d.getMonth(), d.getDate())
    / 86400000
  );
  return CHALLENGES[epochDay % CHALLENGES.length];
}

Easter egg → terminal mode

index.html · script + CSS

Type ryan (or click the logo five times). That adds a terminal-mode class on <body> for about 10 seconds — green mono colors via CSS, then it turns off.

var CODE = "ryan";
// key buffer builds "ryan" → activateTerminal()
document.body.classList.add("terminal-mode");
setTimeout(function () {
  document.body.classList.remove("terminal-mode");
}, 10000);

Rift Hockey game loop

rifthockey.html

The hockey game draws on a <canvas>. A loop asks the browser for the next animation frame, measures elapsed time (dt), updates physics, then redraws.

function loop(ts) {
  let dt = (ts - lastTs) / 1000;
  dt = Math.min(0.033, dt);
  tick(dt);                 // move puck, mallets…
  requestAnimationFrame(loop);
}

Curveball letter guessing

hangman.html

Each click (or key press) calls guess(letter). Hits stay in a Set; misses add a strike. Six strikes ends the round. “Throw Curveball” reveals one random hidden letter once per game.

function guess(letter) {
  if (state.over || state.guessed.has(letter)) return;
  state.guessed.add(letter);
  if (state.answer.includes(letter)) {
    if (checkWin()) endGame(true);
  } else {
    state.strikes++;
    if (state.strikes >= MAX_STRIKES) endGame(false);
  }
}

Open index.html locally, or serve this folder with any static file server.

TERMINAL MODE // 10s