A reason to come back

The daily challenge gets a real midnight boundary instead of an arbitrary UTC one, the wait for it becomes a countdown instead of a dead end, and web push notifications arrive, built around the one hard constraint the hosting platform imposes on anything running on a schedule.

Two days turned the daily challenge from a puzzle that simply existed into one with an actual rhythm: a reset boundary tied to where its players actually are, a wait that counts down instead of dead-ending, cross-promotion in both directions between daily and duel, and web push notifications so a player does not have to remember to come back on their own.

Whose midnight#

The challenge had rolled over at UTC midnight since the first server deploy, tolerated rather than designed: a two-day grace window already existed just to cover a game spanning that boundary. The day before, the boundary itself moved to where the audience actually is, Oslo midnight, deliberately computed as a fixed point rather than tracking daylight saving. A single pure utility now owns that calculation, and everything that used to compute a day boundary on its own, the challenge seed, the client’s stale-cache check, the cache header capping how long a response can be reused, reads it from the same place instead.

Waiting becomes part of the design#

The already-played screen stopped being a dead end and started counting down: a minute-accurate “3h 42m” ticking toward the same Oslo-midnight instant, labelled plainly as “midnight (Oslo time)” for every visitor regardless of their own timezone, an honest simplification over guessing where in the world someone actually is. The same countdown, in miniature, now also appears the other way around, in the duel game-over screen, pointing back at the daily the moment it becomes available again instead of showing a link that goes nowhere useful outside that window.

A reminder that does not need an open tab#

Push notifications were the deep part of the two days, and the design decision underneath it was about delivery, not urgency: a browser-standard push subscription, keyed per player, delivers a notification through the browser vendor’s own infrastructure even with the game closed, which ruled out anything requiring an open connection to the server.

On the settings side, a small state machine tracks whether notifications are active, paused, or denied at the browser level, reconciled against the real permission every time the page loads so a permission revoked outside the app does not go unnoticed. A soft nudge appears once a daily game finishes, offering to turn notifications on, and stops asking after being dismissed a few times rather than repeating the same request forever.

Where this leaves things#

None of it reaches a real player yet without the one-time setup still ahead: a key pair to generate and hand to Railway, and the scheduled job wired up to actually run. The daily challenge now has an actual shape to its day, though, reset, wait, and a reason to come back, rather than a page that is simply there or not.