Fablebloom: no hidden cards, no timers, no packs

A cozy strategy card game in the browser. Eighty-four cards, four families, three glades, and a rulebook that refuses hidden information, turn timers and randomised packs.

Client
Nuits
Year
2026
Status
Pre-launch
Role
Solo founder, Game design, Product design, Engineering
Stack
TanStack Start, TanStack Router, Vite, Cloudflare Workers, PWA, Umami
Fablebloom: no hidden cards, no timers, no packs

Context

Fablebloom is a strategy card game that runs in a browser. Two Keepers compete to wake a sleeping garden: you settle small creatures called Fablings across three Glades, and the first Keeper to restore two of them ends the season on the spot. A season lasts at most eight Moons, which is roughly ten minutes.

Eighty-four cards, four families, three Glades. Free to play, cosmetics only in the shop, and no account needed to start.

It is a Nuits product, so it is mine end to end, and it is not finished. The name and the artwork are placeholders pending trademark and domain clearance, and the legal pages are structural drafts. That is written on the site itself, because a page called Our Promise that hides its own status is not a promise.

The problem

Digital card games reach for three tools a paper game mostly cannot use, and all three are load-bearing. Hidden information manufactures tension for free. A turn timer keeps a match moving without asking either player to be considerate. Randomised packs turn a card set into a revenue line a spreadsheet can model.

Each one also makes the game worse to sit with. Hidden information means the interesting decision happened while you were not looking. A timer punishes you for thinking, which is the thing the game claims to be about. A pack economy means the strongest deck is a purchase, so winning a close match is never quite a compliment.

What I wanted was the pace of a board game on a table, played by two people who like each other, in ten minutes, in a tab.

My role

Everything, solo. Nuits has never hired anyone.

Game design and the card set, the interface, the front end, the deployment, the marketing site, and the copy on it, including the paragraph admitting the name is a placeholder.

Approach

Three refusals, written into the rules rather than onto a values page: no hidden information a paper player could not hold in hand, no timers inside the rules, and no randomness beyond shuffling and drawing.

Everything else had to be built to survive them. With no timer, the match needs a natural clock, so a season is eight Moons and each Moon ends when both Keepers rest in a row. With no pack economy, the card set has to be finite and knowable, so there are eighty-four cards, all of them earned by playing, and the Trading Post sells frames and board themes and nothing that touches a rule.

The resource is where the tension went instead. Dew refills to the number of the current Moon and does not carry over, so Moon one is a single decision and Moon seven is a plan. Nobody can save their way to a blowout, and the player who is behind gets a bigger budget at exactly the point where the game is still winnable.

Each of the three Glades carries a bloom threshold and one rule of its own. Sunny Meadow draws a card for the first Keeper to settle there. Ember Clearing tires everything standing in it at the end of every Moon. Firefly Hollow pays a card the first time each Moon that one of your Fablings is tuckered out. Restoring two Glades ends the season, so the board is a question about which two, not a race for points.

The four families are separated by how they play, not by which colour they are, though they have colours. Grove takes root and outlasts the hurry. Ember races to an early lead and tires you before you settle. Pond rallies the tired, so answering it once is not answering it. Sky drifts and shoos, and you will think you know where it stands.

Because no rule contains a timer, asynchronous play came almost free. A match can be one turn a day over a private link sent to one person, and nothing in the game notices the difference.

Building it

It started on Next.js, deployed to Cloudflare Workers through OpenNext, so the game and the marketing site in front of it are one codebase rather than two. It installs as an app if you want that and stays a page if you do not.

It runs on TanStack Start now, which is a Vite build handed to the Cloudflare Vite plugin, producing a Worker with nothing in between. The reason for the move was that the framework was answering a question the game does not ask. Fablebloom is a client application wearing a marketing site as a hat: a board with no hidden information is state a browser can hold in full, so once a match starts there is nothing on the server worth waiting for.

Three symptoms, in the order they became annoying.

Cold starts. OpenNext shipped the Next.js server itself into the Worker along with a Node compatibility layer, so the first request to a fresh isolate paid to boot a framework before it could answer. A warm response left in about 200ms and a cold one took closer to 800ms, and the person who paid that was whoever opened the tab first.

Navigations that went to the network for things the client already knew. The response headers carried vary: rsc, next-router-state-tree, next-router-prefetch, which is the App Router telling every cache that a route change is a server round trip. Moving between the rules, a Glade and the card list cost one each time, for pages whose content had not changed since the build.

Weight arriving before the game did. The marketing home shipped around 220KB of compressed JavaScript across twelve chunks before a single card was drawn, most of it framework, and then hydrated a server-rendered tree that the client took ownership of immediately anyway.

TanStack Start is the smaller shape of the same idea. The marketing pages prerender, the router resolves on the client, and server functions cover the two or three things that genuinely need a server: a match link, a save, the sign-in behind the play route. The boundary between client and server is drawn where the app actually has one instead of where the framework assumed it would be.

Analytics is Umami, self-hosted rather than handed to an analytics company, which is the same arrangement every other Nuits product runs.

The interesting constraint is the first refusal. A game with no hidden information has nowhere to keep a mistake: both players see the whole board at all times, so any disagreement between what the rules say and what the screen shows is visible to both of them at once. That pushes the ruleset towards being small enough to state completely. Eight Moons, one action per turn, four kinds of card, three Glade rules, and Dew that resets. A player who suspects the game got something wrong can check it, which is only possible because there is not much to check.

Outcome

Playable and pre-launch. The site is up, the rules are written, the card set is complete, and the name on all of it is temporary. Trademark and domain clearance come before a launch rather than after one, and until that clears there is no point pretending the front door is finished.

What I’d do differently

I designed eighty-four cards and four families before the name was cleared. If clearance goes badly, none of the card design is wasted and all of the artwork is, which is an avoidable order of operations. The boring legal check is cheap early and expensive late.

I also picked the framework before I knew the shape of the app. Next.js is a good answer to a question about pages, and Fablebloom turned out to be a question about a board. Choosing a render model first and finding out months later that the thing you built is a client application is a mistake that only ever shows up as latency, and the migration was the bill for it.

I also published the legal pages as structural drafts carrying placeholder text. I still think shipping the skeleton with a visible “not yet binding” notice beats shipping nothing at all, but it is a strange thing to have live next to a page describing a shop. The honest version of that sentence is that the storefront was written before the paperwork was.