FINDMY HOI SUM
AI Engineering
A Hong Kong life sim that hands you the fantasy of getting rich, then quietly bills you for it in the one currency it never shows you.
01The problem
Hong Kong has one defining private anxiety: the gap between a monthly salary and a down payment, a gap most people can name in years rather than dollars. The obvious product response is another budgeting app that tells you to save harder. Every one of them starts from the same posture, which is why almost nobody opens them twice.
This took the opposite route. Grant the fantasy instead of scolding it, then follow it all the way to its end. The player is a working adult who wants, for ten minutes, to watch the numbers move in their favour. What the game plays with is the hedonic treadmill: the tenth flat feels like less than the first, and the resource actually being spent was never money.
02What I built
A playable single-player life simulation that opens as a personal onboarding ritual rather than a title screen. The player pastes a snippet, drops a CV, or fills a thirty-second form; a serverless step estimates their real monthly salary and returns a years-to-first-flat countdown with a line of black humour attached. The game then multiplies that same salary by a hundred and drops them into a town where one real day is one game month, so a countdown that reads twenty-three years in life resolves in a few days of opening the app.
- Real-identity onboarding by screenshot, CV, pasted text or manual quick-fill, ending in a shareable countdown report card.
- A daily-tick economy: opening the app settles every elapsed real day as a game month, replaying salary, rent, market movement and events as a montage.
- Acceleration at the top: the rate rises to two and then three months per real day at high net worth, so the rich player visibly runs out of life faster.
- An explorable pixel town rendered entirely from code with no image assets, walkable streets, enterable shops and dialogue that uses the player's nickname.
- A hidden emotion engine: the gauge labelled life satisfaction is a pleasure meter with decaying returns and an accelerating natural decay.
- Two endings gated on behaviour rather than score — give everything away in time, or reach the end of the age clock still holding it all.
Drawn, not captured. The bar at the top is the gauge the game labels life satisfaction, the middle band is the walkable street with the player as the one filled square, and the panel below is the in-game phone beside an owned-items list. Everything here is a placeholder, and every brand, shop and price in the game itself is invented.
03As a product
- Who buys it
- Nobody, by design. The audience is Hong Kong working adults roughly twenty-five to forty-five who would share it rather than purchase it. Its commercial reading is as a portfolio piece and as a demonstration format for a brand or NGO that needs to say something uncomfortable about money without lecturing.
- Value
- It converts a statistic people already know into a felt experience, then earns the right to make an argument about what comes after. The onboarding output — your own number, your own job title, one line of gallows humour — is the share unit; the game is what happens after you screenshot it.
- Positioning
- Personal finance apps moralise, idle games reward accumulation forever, and narrative games announce their message on the box. This runs an idle-game reward curve on an anti-idle-game thesis, with the thesis hidden until the player has already spent their own attention proving it.
- Status
- Live, playable and deliberately unmonetised. No accounts, no payments, no analytics on player behaviour; saves live in the browser and nowhere else.
04How it works
- Stack
- React and TypeScript on Vite with Tailwind, one serverless function calling a model for identity analysis, client-side PDF parsing, deployed as a static single-page app. No database, no accounts, no server-side state: saves live in local storage.
- Shape
- A pure reducer over a deterministic seed. Every tick is a function of the saved state, so a returning player is replayed rather than simulated live, and the catch-up path is capped so nobody is handed a hundred months at once.
Width uniform throughout — nothing along this path has ever been counted or instrumented, so every band is drawn at equal width and no quantity is claimed.
The branch that stops is the product decision. One call goes out, four numbers come back, and the document behind them is not kept — so the only copy of a player’s game is the one in their own browser, and every tick after this point is a replay of that save rather than a fresh draw.
The decision I spent longest on
Making the property market feel volatile while guaranteeing it never betrays the player. The first economy let event cards move the market freely; across eight seeded simulations the compounding ran to roughly twenty-five times a year and net worth stopped meaning anything. The fix was a mean-reversion corridor: each tick computes swings and shocks normally, then pulls the index back toward a fixed growth track. Short-term shocks stay legible, the long-term promise is enforced by the engine rather than by carefully balanced content, and a content author can no longer break the economy by accident.
05Retrieval architecture
There is nothing to retrieve here, and saying so plainly is more useful than dressing it up: the only model call in the product runs outward, reading a document the player supplies rather than any corpus of ours, and the server throws the payload away in the same request.
What sits in retrieval’s place is a constraint the game could not survive without. Every tick has to be reproducible from the save alone — a returning player is replayed, not simulated live — and any layer that fetched something the save did not already determine would have broken that in the first week.
ModelInverted — the retrieving model is not ours. The single call runs outward, reading a pasted profile or a CV and returning four fields; nothing of ours is ever searched, and the payload is discarded rather than stored.
- Corpus
- A fixed content library written into the source and compiled into the bundle: 23 property listings, 16 collectibles, 58 event cards, 21 narrative beats, three lottery tiers and ten milestones, alongside a town whose every tile, building and character is a string-array sprite rather than an image file. The only other body of data is the player’s own save, which lives in their browser and nowhere else.
- Ingestion
- There is no ingestion pipeline, because content enters by being written. A card or a listing is a typed constant in a source file and reaches players when the build ships, so nothing is fetched, scraped or scheduled. The one run-time input is the identity import — a screenshot, a pasted paragraph, or a CV parsed to text in the browser so that the file itself never leaves the device — and it is admitted once, at the start of a game, then never again.
- Index
- None. Nothing is precomputed, because a few dozen typed constants cannot repay it. The event deck is 58 entries and the marketplace 23; every draw walks the array in full and finishes far inside a frame. An index here would be a second copy of the content library, able to fall out of step with the first, bought with a build step, in exchange for saving a scan that costs nothing at all.
- Query
- None. Nothing arrives as language, so there is nothing to interpret. What drives a turn is a number — how many real days have passed since the save was written — and the conditions attached to content are structural: the turn index, the current phase, a once-only flag, whether the player already owns the thing. Each of those is already a predicate, and asking what the player meant would be answering a question nobody posed.
- Selection
- Filter, then a weighted draw from a seeded generator. Event cards are narrowed by turn, phase and once-only flags before being drawn by weight; narrative beats fire on trigger predicates rather than being chosen at all; the marketplace rotates a quota-limited sample of listings the player does not already own. Nothing that comes back is scored or reordered afterwards — the draw order is the order, and it has to be, because the same seed against the same save must produce the same montage on any device or the replay stops being a replay.
- Grounding
- The model is never trusted with a number it could get wrong. It is asked for four fields — job title, industry, an estimated monthly salary and an age — under an explicit anonymisation instruction, and everything it returns is then clamped into sane ranges, stripped of anything name-shaped, and fed into a down-payment formula the server applies itself rather than asking the model to do arithmetic. If the call fails, the flow falls back to a thirty-second form, so a bad answer is never allowed to become the player’s starting position.
Present — this layer exists and runs.Absent by decision — the layer is not there, and the sentence beside it is the reason. Every one of the six is answered on every system in this chapter, so the rows can be read across pages.
06Numbers
| Measure | Figure | Basis |
|---|---|---|
| Content library: listings, collectibles, cards, beats | 23 / 16 / 58 / 21 | Verified |
| Codebase across engine, UI, world and one function | ~12,700 lines | Verified |
| Documented playtest corrections cited in code | 13 | Verified |
| Image files shipped for the game world | 0 | Verified |
| Design document to first public deploy | 6 days | Verified |
| Time compression at the base rate | 1 day = 1 month | Verified |
| Real days of daily opens to the first flat | 3 to 5 | Projected |
Verified — counted in the shipped source, the build output and the repository history, and confirmed live on 2026-08-12. The zero is literal: every tile, building and character is a string-array sprite compiled at runtime. Projected — the session length is derived from design intent recorded in code comments — starting cash was chosen so the player must return for several days to afford the first property — combined with entry-tier prices in the content library. Never measured against a real player.
07Timeline
- 2026-07 Design document locked, with eight core decisions taken one at a time: the irony framework, the disguised pleasure gauge, a three-act slow burn, a turn-based loop, a short euphoria opening, an assets-always-rise risk model, a browse-and-buy marketplace, and an age clock as the ending trigger.
- 2026-07 Engine, content library and first interface built from scratch, with the type contract treated as immutable so two parallel workstreams could not collide.
- 2026-08 Second milestone: real-day time compression, identity onboarding with the countdown report, the pixel town, and the public deploy.
- 2026-08 Economy rebuilt twice on playtest evidence — starting cash cut, satisfaction decay inverted, market corridor added, early events suppressed.
- 2026-08 Live and playable at the public URL. Remaining work is emotional pacing, ending polish and an eventual native wrapper.
08Looking back
What broke
The economy needed rebuilding twice, and both failures were thesis failures rather than crashes. Starting cash of a few million made the sweetest moment in the game last three seconds, so it was cut to roughly the down payment figure the onboarding quotes. The satisfaction decay was originally backwards, letting the heaviest buyers stay happy longest, which inverted the entire argument the game exists to make. Two smaller ones: an early windfall card could hand the player a flat on day one and skip the whole opening act, and the town NPCs were addressing the developer rather than the player until a chosen nickname was threaded through the dialogue system.
What it changed
It settled a standing rule. Every bank, property portal, car and watch in the game is invented. Real trade dress was requested and refused on three grounds: passing off is a real legal exposure, store rules would flag it, and a convincing fake bank statement screen is a fraud tool regardless of the developer's intent. The realism now comes from typography, information density and restraint, which turned out to be the better craft problem anyway.
Where it stands
Live and playable, first two milestones complete, unmonetised on purpose. The next honest step is pacing tuned against players who are not me.