Bloomlink — project story

What inspired it

My girlfriend loves flowers. Not the idea of flowers — actual flowers, chosen carefully, handed over in person. The problem is I travel a lot, and I'm bad at timing, and florists close, and somehow the moment always passes. I've shown up to more than one occasion empty-handed not because I didn't care, but because life moved faster than I did.

I wanted to fix that — not with a delivery app, but with something that turned the waiting itself into the gift. The inspiration came from two places: the Japanese concept of kotodama — the idea that words carry living spirit — and the simple act of tending a plant. Both require showing up repeatedly with no immediate reward. Both produce something you couldn't have rushed.

The question became: what if the time you spent caring for something became the gift itself? What if missing the moment in real life could be answered by something that proved you were thinking of her every single day leading up to it?


What I built

Bloomlink is a slow gifting game where you grow flowers by watering them daily and writing them notes. When a flower blooms, you assemble a bouquet and send it — but what the recipient receives isn't just flowers. It's a letter, assembled from every note you wrote while tending the garden over days or weeks.

The core loop is:

$$\text{daily care} + \text{written notes} \xrightarrow{\text{time}} \text{bloom} \xrightarrow{\text{bouquet}} \text{letter}$$

The growth model rewards consistency over intensity. A streak multiplier compounds daily care:

$$\text{growth} = \text{base_reward} \times \left(1 + \frac{\text{streak}}{20}\right) \times \text{time_multiplier}$$

So someone who shows up every day for two weeks grows their flower significantly faster than someone who waters ten times in a single day — which is intentional. The game is about habit, not grinding.

The stack is React + TypeScript for the UI, React Three Fiber with Three.js for the 3D garden and bouquet scenes, and localStorage for persistence. No backend — everything lives on the device, which felt right for something this personal.


How I built it

The project has three distinct 3D environments that each had to feel tonally different:

The garden — an overhead view of potted flowers on a green lawn. Flowers grow visibly over time, moving through four stages: seed → sprout → budding → bloom. Each stage has distinct geometry. The camera sits close enough that individual stems and petals read clearly.

The bouquet builder — a still-life table scene with a single warm key light from the upper left, a cool fill from the right, and a linen-coloured wall behind to catch shadows. Flowers are arranged in hand-tuned concentric positions so no two stems overlap. Wrapping and ribbon update live in 3D as you make choices in the panel.

The letter reveal — a full-screen ceremonial screen. An envelope opens, a letter unfolds, and note lines appear sequentially — one every 0.6 seconds — as petals fall from the top of the viewport. Each line the player sees is something they actually wrote, weeks earlier, not knowing exactly when it would be read.

The growth engine uses per-ritual daily cooldowns so watering and writing each have meaning independently. Notes contribute more growth than water alone because they require more of the player — and because their content matters downstream.


Challenges

The 3D bouquet composition was the hardest technical problem. Early versions rendered each flower in its own individual pot, spread across a table in a straight line — nothing like a real bouquet. Getting flowers to cluster naturally inside a wrapping cone required throwing out formula-based arc positioning entirely and hand-tuning fixed layouts for each flower count from 1 to 7. The wrapping cone radius, ribbon height, and camera target all had to be recalibrated together.

Foliage rendering caused consistent z-fighting and transparency sorting artefacts. The GLTF foliage assets are alpha-blended billboard quads from a texture atlas. Using them as stems — which seemed logical given they already carried leaf geometry — produced flat horizontal planes instead of upright stems when rotated. The fix was to replace foliage-based stems with simple TubeGeometry along a CatmullRomCurve3, giving each flower a unique gentle S-curve with correct taper.

The emotional design was harder to get right than any of the technical problems. It's easy to build a plant-watering mechanic. It's much harder to make a player feel that the time they spent actually meant something. The letter reveal screen went through several versions before landing on the current approach — no UI chrome, no buttons, just the letter appearing line by line in silence, with music, until it's done. Unhurried. The player can't skip it. That turned out to be the right call.


What I learned

That constraints are the point. Limiting watering to once per day, limiting bouquet size to 3–7 flowers, limiting the reveal to one slow screen with no interaction — every restriction I added made the emotional payoff stronger, not weaker. The game works because you can't rush it.

I also learned that building something for one specific person — someone you know, whose face you can picture receiving it — produces better design decisions than building for a generic user. Every time I wasn't sure whether a feature was worth adding, I asked myself: would this make her smile when she opens it? That turned out to be a better design filter than any framework.

Slow games are genuinely underexplored on mobile. Most mobile design optimises for sessions measured in seconds. Bloomlink optimises for a relationship measured in weeks. That's a different design problem entirely — and a more personal one.

Built With

  • medo
Share this project:

Updates