Daisey Traynham.
← Back to portfolio

Case study // Personal project, no NDA, all mine

let's bloom together

let's bloom together is a collaborative world map. You tap one button, pick a flower, and it gets planted where you are. It shows up as a seed. Twenty four hours later it opens into the flower you chose, and it stays there forever. Everyone sees everyone else's, appearing live as they're planted.

Client

Personal project

Duration

One day, sketch to live

Team

Solo, with an AI coding assistant

My role

Product direction, UX, visual design, UX writing, QA, AI-directed build

Built with

Claude, concept to a live database
one HTML file, 89 KB, no framework

let's bloom together, a collaborative world map of flowers planted by strangers

This one you can actually go use. It takes about five seconds, and whatever you plant will still be there tomorrow.

Go plant one ↗

Plant a seed today, come back tomorrow

The entire product is one button and one wait.

No account, no login, no form. You tap plant, pick from ten flowers, and it lands at your location as a seedling. It becomes a shoot, and a day later it opens.

That delay is the product. Instant blooms would have made it a toy you look at once. Making people wait a day gives them a reason to come back, and it turns a tap into something closer to an actual small act of care. The tagline says the whole thing out loud: plant a seed today, come back tomorrow and see your flower.

Everything else in the build exists to protect that one moment.

The three growth stages: seed, shoot, and open flower

The question that saved it

Before I uploaded anything, I asked one question: where are the flowers stored, and if we update this later, do we keep them?

The prototype was saving them into the preview environment's key value store. That API does not exist on a normal web server. If I had uploaded it as it was, the site would have loaded, looked perfect, accepted a flower, and silently saved nothing. Every visitor would have seen an empty map, forever. No error, no crash, no warning. I would have found out weeks later when the garden was still empty.

That question is the difference between a demo and a product, and the only reason it worked is that I asked it before launch instead of after.

The fix, done before I promoted the site anywhere: a Postgres table with row level security, so the front end can never touch the data no matter what changes later. One table, every column constrained, because anonymous strangers are allowed to write to it.

The map alongside the database table the flowers are stored in

Then I tested it the way a stranger would hit it, not the way an admin does:

Row level security, tested from the outside
What I triedWhat happened
Plant a valid flowerAllowed
Insert a <script> tag as the emojiBlocked
Set the latitude to 999Blocked
Backdate a flower three yearsBlocked
Edit someone else's flower0 rows changed
Delete the whole garden0 rows deleted

Ask where the data lives before you launch. It's the cheapest question in the project and the most expensive one to skip.

The only reason there's a garden at all.

A map that stays out of the way

The palette came from a painting I liked, not from a theme. Navy ocean, olive land, ochre for the one button that matters. The first pass had generic dark mode colors and it looked like every other data map on the internet.

Then I asked for the coastlines to be muted down. On a map about flowers, the flowers should be the loudest thing on screen. Country borders are context, not content.

Under that, there's no map library at all. The country shapes are Natural Earth boundaries simplified down to 27 KB of path data and embedded straight into the page, drawn on a canvas with a custom projection. No tile server, no API key, no dependency that can go down or start charging.

The world map at full zoom, flowers scattered across several countries

Clustering handles the crowding. At world zoom, a city is one flower with a count badge. Tap it and the map flies in and splits the group apart. Tapping the Jacksonville cluster of 31 drops the biggest visible group to 7 and spreads it across 13 separate points, 8 of them a single flower standing alone.

A city cluster with a count badge, before tapping
Before

Zoomed out, a whole metro area collapses into one flower and a count. Readable, but you can't see anyone's individual flower.

The same cluster after tapping, split into individual flowers
After

One tap flies the map in and splits the group apart. Those 31 become 13 separate points, 8 of them a single flower, largest remaining group down to 7.

Being wrong three times about the glow

The seedlings were hard to see, so a glow got added behind them. I said it was too much. It got dimmer. Still too much. Dimmer again. Still too much.

On the fourth round I asked to see it with no glow at all.

Zero glow was correct. The glow had been propping up a problem that no longer existed. The real cause was a rendering bug where seedlings were painting in the navy of the ocean gradient on some machines, and once that was fixed, the decoration was pure noise.

Seedlings with a glow behind them
Before

Glow on. The seedlings pull the eye harder than the open flowers do.

The same seedlings with no glow
After

Glow removed entirely. The flowers carry the screen again, which was the point of the whole map.

Brightness above the background
BeforeAfter
A flower at the moment it opens+66+66
A seedling+74+32

The seedlings had been shouting louder than the flowers. The hierarchy was backwards, and the glow was the thing making it backwards.

I keep this one in because the lesson is useful: when a fix needs three rounds of dialing back, it's usually not the right fix. Take it out entirely and see what the problem actually was.

Knowing where you are, without knowing who you are

The site names the place each flower was planted. That took three tries.

The accurate version is a 1.39 MB file. Putting it in the page would have blocked the map from appearing for several seconds on a phone, which is fatal for a site whose whole appeal is the map showing up.

So it loads separately, after the map is already on screen. The page went from 422 KB back down to 89 KB, which means it now loads faster than it did before the feature was added, and place names fill in a second later. I tested it against a deliberately slow server and against the file being missing entirely. In both cases the map draws normally and the sentence just leaves the place name out instead of printing something broken.

A tooltip naming the town a flower was planted in
Three passes at naming a place
Cities in the fileWhat a flower in rural Pennsylvania got called
v1199“Washington”, which was actually D.C., 175 km away
v29,062“United States”
v349,025“Hollidaysburg, Pennsylvania, United States”

Two more things worth a line. Country names aren't shipped at all: the file stores two letter codes and the browser expands them, so the names come out correct in any language for zero bytes. And scanning 49,000 cities for every flower would have been unusable, so cities are bucketed into a grid. A lookup takes 2.6 microseconds.

The privacy side is the part I care about most. Coordinates get rounded to about 1.1 km before they ever leave the browser, and no IP address, device ID, or anything identifying is stored at all. That turned out to be genuinely binding: when I went looking for which flowers were mine, I couldn't tell. The design protects me from myself, which is exactly the right answer.

Measured, not eyeballed

Almost every fix here was verified by measuring something instead of squinting at it.

The seedling color bug was found by sampling the exact pixel color out of a screenshot, #273555, and matching it to one specific value in the code.

The share card had a version of the same problem: it was drawn on an offscreen canvas that doesn't support color emoji on every machine. Measured, it came back with 0% green pixels, one flat tone. The fix was to delete that second renderer entirely and photograph the live map instead, borrowing the real canvas for one frame and handing it back safely so a failure mid capture can't leave someone's map broken.

Then Instagram cropped the card's text in half. A square image scaled into a 9:16 story keeps only the middle 56.25% of its width. Now every line of card text auto fits a safe band, verified by actually cropping the output to 9:16 and measuring the margin on each line.

The generated share card shown inside a 9:16 story frame

A few more that came out of measuring rather than looking:

  • A phantom flower every 25 seconds. The live poller was re-adding one, because Postgres stores timestamps in microseconds and JavaScript can only express milliseconds, so the newest flower always looked newer than the last check. Fixed by tracking row IDs instead of trusting time, and verified across 14 polls against a server deliberately running 7 seconds fast.
  • Tooltips vanished instantly on touch, because lifting a finger fires a leave event right after the tap. Touch now gets 10px of wobble room and a 36px target.
  • A dead zone nobody finds without testing at odd sizes. The ticker collided with the plant button between roughly 720px and 1100px. Rebuilt as a grid and checked at nine widths from 375 to 1800.
  • A city called Al-`Ula, Saudi Arabia has a backtick in its name that would have broken the entire page. It only came into range when I asked for the smaller city threshold. A check in the build script caught it before it ever shipped.

Where I couldn't measure something, I left the claim out. The test browser freezes its clock, so several timing numbers simply couldn't be verified, and they're not in here.

How it got built in a day

This was built with an AI coding assistant, and it wrote most of the code. I want to be straight about that, because the interesting part isn't who typed.

What was mine was every decision:

  • Asking where the data lived before launch, which is the only reason there's a garden at all
  • The palette, taken from a painting, replacing a generic dark theme
  • Muting the coastlines so the flowers would carry the screen
  • Choosing “plant” over “sow”, because it's the plainer word for a global audience
  • Pushing back on the glow three times, and being right all three times
  • Catching the navy seedlings, the cropped story text, and the distracting neighbors on the share card
  • Keeping the 24 hour growth stages instead of blooming instantly
  • Asking for more accurate place names, and then accepting the tradeoff that came with them
  • Asking whether Instagram makes a shared photo clickable, which is how I found out the printed URL was the only way back and needed to be bigger

The build was fast because the questions were good. That's the actual skill, and it's the same one I've been using on client work for years.

The tools changed. Knowing what to ask didn't.

Key takeaways

Four things I'd carry into any build

01 · Before you launch

Ask where the data lives.

Not after. It's the cheapest question in the project and the most expensive one to skip.

02 · When a fix keeps needing fixes

Three rounds of toning down means it's the wrong fix.

Remove it entirely and find out what the real problem was.

03 · On tradeoffs

Accuracy has a weight.

The right call is usually not “cut the feature” or “ship it slow”. It's changing when the weight arrives.

04 · On knowing you're right

Measure it.

Sample the pixel, crop the image, skew the clock. Looking at it isn't testing it.

Where it landed

59flowers, day one
10countries
89 KBone HTML file

The site went live the same night it was sketched. The first flower was a daisy planted in Puerto Vallarta at 23:57 UTC. By the next afternoon there were 59 flowers across 10 countries, all organic, no promotion beyond a couple of shares. One of them is 1,500 km out in the South Atlantic, which the site cheerfully labels “the open sea.”

It's one HTML file, 89 KB, 33 KB gzipped, no framework and no build step, running on a real database that anonymous strangers write to safely.

The next thing to build is rate limiting. Nothing stops a script inserting a thousand rows right now. That's fine at this size, and it won't be later.

Go plant one.

It takes five seconds, it's anonymous, and it opens tomorrow. The rest of my case studies are password-protected to respect client confidentiality.

www.daisey.com/bloom