DEV Community

Akbo Ichou
Akbo Ichou

Posted on

A "Daily Challenge" That Resets at Midnight: Deterministic Picks Without a Backend

Daily challenges are one of the simplest engagement features you can add to a games site — Wordle made the pattern famous. On Classroom Games, a collection of free learning games for teachers (math, typing, spelling, reading comprehension, geography, fractions, multiplication, sight words, brain breaks and more), the homepage shows one "Today's Pick" with a points goal. It resets at midnight, and there's one pick per day.

The interesting part: you can build this without a server. Here's the approach.

1. Derive "today" from the local date, not a timestamp

A daily feature should flip at the player's midnight, not UTC midnight. A class in California and a class in Paris should each get a fresh challenge when their day starts.

function localDateKey(d = new Date()) {
  const y = d.getFullYear();
  const m = String(d.getMonth() + 1).padStart(2, "0");
  const day = String(d.getDate()).padStart(2, "0");
  return `${y}-${m}-${day}`; // "2026-09-25"
}
Enter fullscreen mode Exit fullscreen mode

Using getFullYear/getMonth/getDate (not the UTC variants) is the whole trick.

2. Pick deterministically from the date

Everyone on the same local date should see the same pick, and refreshing shouldn't reroll it. Hash the date string into an index:

// FNV-1a: tiny, fast, good enough for picking an item
function hash(str) {
  let h = 0x811c9dc5;
  for (let i = 0; i < str.length; i++) {
    h ^= str.charCodeAt(i);
    h = Math.imul(h, 0x01000193);
  }
  return h >>> 0;
}

const CATEGORIES = [
  "math", "typing", "reading", "spelling", "puzzles", "memory",
  "logic", "trivia", "geography", "science", "words", "vocabulary",
  "grammar", "sight-words", "multiplication", "fractions",
  "counting", "time-money", "brain-breaks",
];

export function todaysPick(date = new Date()) {
  return CATEGORIES[hash(localDateKey(date)) % CATEGORIES.length];
}
Enter fullscreen mode Exit fullscreen mode

No database, no cron job, no API. The "schedule" is a pure function of the date.

3. Avoid back-to-back repeats

Pure hashing can land on the same category two days in a row, which feels broken to users even though it's random. A small fix is to check yesterday and step forward on a collision:

export function todaysPickNoRepeat(date = new Date()) {
  const y = new Date(date); y.setDate(y.getDate() - 1);
  let i = hash(localDateKey(date)) % CATEGORIES.length;
  if (CATEGORIES[i] === todaysPick(y)) i = (i + 1) % CATEGORIES.length;
  return CATEGORIES[i];
}
Enter fullscreen mode Exit fullscreen mode

4. Store progress locally, keyed by date

The status ("Not started", in progress, goal reached) only matters for today, so it lives in localStorage under the date key. Old keys are naturally ignored.

function loadStatus() {
  try {
    const saved = JSON.parse(localStorage.getItem("daily") || "{}");
    return saved.date === localDateKey() ? saved : { date: localDateKey(), points: 0 };
  } catch {
    return { date: localDateKey(), points: 0 }; // private mode, blocked storage, etc.
  }
}
Enter fullscreen mode Exit fullscreen mode

Wrapping storage in try/catch matters in schools: managed browsers sometimes block or wipe site storage, and the page should still work.
javascript,webdev,gamedev,education,

5. Handle the tab left open overnight

Classroom machines often stay on with the same tab open. Re-check the date key when the page becomes visible again:

document.addEventListener("visibilitychange", () => {
  if (document.visibilityState === "visible" && loadStatus().points === 0) {
    render(todaysPickNoRepeat());
  }
});
Enter fullscreen mode Exit fullscreen mode

Why this works well for classrooms

  • No accounts — nothing to sign up for, nothing to get approved.
  • Same pick for the whole class on the same day, which makes it easy to use as a warm-up.
  • A clear goal (e.g. 50+ points) gives students a target without a leaderboard.

You can try today's pick at classroomgames.app. Have you built daily features without a backend? I'd like to hear how you handled time zones.

Top comments (0)