Live Updated 2026-09

GameLook

A platform that helps returning players understand what changed in games since they last played them.

TypeScript Next.js Node.js PostgreSQL Redis BullMQ
yoizuki.dev

Problem

Game update information is fragmented and noisy. Players returning to a game after months or years usually have to dig through patch notes, announcement posts, DLC listings, store pages, and release timelines.

The result is high friction and poor discoverability. Even when the data exists, it is not stored in a way that makes historical understanding easy.

Goal

Build a system that can collect game updates continuously, structure them into readable timelines, avoid repeated expensive crawling, support followed games, and summarize large volumes of change in a usable way.

Source Monitoring Model

The system continuously monitors external sources for changes. The challenge is deciding which games deserve proactive crawling versus on-demand crawling - and structuring update history so it remains useful over time.

The catalogue now holds over 700 tracked games, with more than 500 carried all the way through extraction and summarization. Volume per game varies by orders of magnitude: a finished single-player title may have a few dozen recorded events, while a live-service game accumulates thousands.

Hot vs Cold Game Strategy

Without a tiered crawl strategy, the platform risks wasting resources by treating every game equally. A small number of games drive most traffic and need low-latency answers. Everything else can tolerate slower on-demand processing.

  • Hot games - popular titles worth pre-crawling and keeping warm
  • Cold games - niche titles crawled on demand

This split allows lower infrastructure cost, better perceived responsiveness for popular games, controlled crawl frequency, and better reuse of stored results.

Data Storage and Cache Reuse

Storage and cache reuse were optimized to reduce repeated parsing and fetches. The system focuses on structured historical records instead of one-off summaries, and treats freshness, cost, and latency as competing concerns that need explicit trade-offs.

Change events are stored normalized and immutable, with an importance score used both by the “main milestones” filter in the UI and by the planner that decides which summaries need regenerating. Summaries are cached per game and per covered period, so a regeneration is an upsert rather than a new row. The planner skips any period with no new events since its last summary, which keeps steady-state cost close to zero for games that have gone quiet.

Model Routing and Token Allowance

Summarization is the most expensive step in the pipeline, and it is the one most likely to run away quietly. Extraction and summarization are routed independently, so the cheaper model can keep handling high-volume extraction while summaries - where quality is most visible - run on a stronger reasoning model.

The spend guard sits in front of every call rather than behind it. Estimated input tokens plus the requested completion ceiling are reserved atomically before the request, then reconciled against the API’s reported usage afterwards. Ambiguous outcomes keep the full conservative reservation instead of undercounting, so a transport failure cannot silently create headroom that was never really free.

The same accounting works in reverse. When no games are waiting on a first refresh and the pipeline is otherwise idle, leftover daily allowance is spent backfilling - upgrading summaries still marked as generated by the older, cheaper model. Unused budget becomes quality instead of evaporating at midnight.

Year Pages

Summaries are generated per year, which makes the year itself a useful unit to publish. Each tracked game gets standalone pages answering “what changed in this game in 2026” - the full set of updates for that period, with counts for total updates and main milestones, and the date range actually covered.

These pages fall out of the storage model rather than being built on top of it, and they match the question people actually search for.

Following Games and Email Digests

An account layer lets someone follow games and record when they last played. That turns the platform from something you visit into something that can reach you: a background coordinator batches per-user digests of what changed across followed games and hands them to a delivery worker.

Digests carry only changes at or above the same importance threshold the “main milestones” filter uses, and they render from stored change events rather than making another model call - the expensive work already happened during the refresh.

Delivery state is tracked properly - bounces and complaints arrive via provider webhooks, and unsubscribe links are signed so they work without a session. The cost of getting email wrong is not a broken page, it is a damaged sending reputation, so it is worth more care than the feature size suggests.

Steam Companion Extension

The web app remains the main GameLook experience. A lightweight Chromium extension brings a focused slice of that experience directly into Steam, letting someone inspect a game’s milestones and recent change history without leaving its store page.

The extension reads the Steam app ID and basic page metadata, then asks GameLook’s API for the exact catalog match. Its popup presents the same structured summaries and change events produced by the main platform, with links back to the full game page for deeper exploration. If the game is not tracked yet, the user can submit a pre-filled request from the popup.

Keeping the extension thin was deliberate: crawling, parsing, matching, and durable storage stay server-side. The browser surface prefetches supported Steam pages, coalesces duplicate requests, and keeps a bounded cache of recent lookups so the popup feels immediate without duplicating the platform’s core logic.

Install the GameLook extension from the Chrome Web Store.

Trade-offs

The obvious but bad version of this system is: fetch often, parse everything repeatedly, summarize on demand every time. That becomes expensive fast.

This project forced careful thinking around cache invalidation, data freshness, crawl prioritization, immutable history vs reprocessed summaries, and cost-aware architecture. Adding model routing sharpened the last one: every guardrail that protects spend also has to fail in a direction that does not corrupt stored history. A blocked summary leaves the previous row untouched and retries later, rather than writing a degraded one.

Lessons Learned

The hardest part is not building the crawler - it is deciding when and how often to crawl. Every decision about freshness has a cost implication, and every caching decision has a correctness implication. The system design is the product.

Model spend behaves the same way. Treating a token budget as something to reserve before the call and reconcile after - instead of a number to check afterwards - is the difference between a limit and a hope.