MusicNote

Leave a song. Find a story.

Inspiration

Music holds a place in a way photographs never quite manage. Hear a song you have not heard in years and the whole situation comes back with it: the air that afternoon, who you were with, what you were worried about, the light on the way home. The song is a bookmark pressed into a moment.

What if the places we pass through could remember us?

As our lives become increasingly digitalised, we are losing genuine human connection and the relationship between the digital and physical realms. Social media makes us think twice before sharing because we fear being judged or criticised. Every post carries a name, a follower count, and a quiet calculation about how it will land.

This is why we created MusicNote, an app that lets you leave a song and a short message at a physical location. When another traveller visits that same spot, your postcard surfaces, creating an unexpected connection between strangers across time.

As more people leave postcards in the same location, it builds its own collective soundtrack and evolving emotional history, shaped by everyone who has passed through it. A set of stairs by the harbour is not just stairs once four people have left something there at four different hours of their lives.

What it does

You arrive somewhere and look around. If somebody has left a postcard nearby, one of them surfaces: a short handwritten message on paper, one song, the date, and the suburb it was left in. You can play a preview of the track right there.

Then you decide. Keep it, and it joins your collection. Pass it on, and it stays where it was for whoever comes next.

Leaving your own works the same way in reverse. Write the message, choose the song, and it waits at that spot. You never find out who picks it up.

Two rules hold the whole thing together, and both are deliberate:

  1. Postcards are anonymous. No names, no profiles, no follower counts, and no way to reply. You cannot perform for an audience that has no face.
  2. You have to actually be there. Postcards only surface within 300 metres. There is no feed and no browsing other cities from your couch. The place is the index.

How we built it

MusicNote is a Flutter app with a Postgres backend on Supabase, using PostGIS for the distance queries that decide what is near enough to find.

Music comes from the iTunes Search API, and that was the first real design decision. We started with Spotify, because that is the obvious choice for a music app, and it does not work. Spotify removed preview clips from its public API in late 2024, and playing a full track requires the listener to hold a Premium subscription. That creates an asymmetry which breaks the whole idea: the person leaving a postcard needs a subscription, and so does every stranger who finds it. Somebody walking past should not need an account to hear what was left for them. The iTunes catalogue asks for nothing at all, so everybody can hear everybody.

The bigger architectural decision was to put an interface in front of every external dependency: music, authentication, location, storage, audio, and place names. Each one has a stand-in used during testing, which means the entire app runs with no network, no GPS, and no audio hardware. That is why we could keep redesigning this aggressively without quietly breaking behaviour.

Privacy was an engineering problem, not a policy page

Anonymity is easy to claim and hard to actually build, and two decisions did most of the work.

The author is not part of what the app can see. Rather than trusting the app to hide a name, we made the identity impossible to request. The postcard table is not reachable from the client at all. Everything goes through a small set of server functions, and the shape they return simply has no author field in it. Nothing can leak a name accidentally, because there is no name to leak.

That created a real problem worth solving. Reporting and blocking are required for any app carrying user generated content, and our app does not know who wrote anything. The answer is to point at the postcard instead of the person: the app says "block whoever left this one", and the server resolves the author privately, where it is allowed to know.

The map shows how many, never where. Exact pins would have been easier and prettier, and they would have undone the point. Somebody leaving a postcard near home would be dropping a dot on their house. So the map aggregates into grid cells and draws only a count. The cells resize with the zoom level, so clusters merge as you pull back, and they never shrink below about 280 metres no matter how far you zoom in. That floor is not arbitrary: it is roughly the discovery radius itself, so a cell can never be more precise than the rule that governs discovery.

Place names follow the same logic. We turn coordinates into a name but use only the suburb and the city, never the street or the number. "Circular Quay, Sydney" tells you where you were. A street address tells somebody where you live.

Challenges we ran into

A failure that looked like an empty shelf. For a while, everything a user saved simply did not appear. The data was in the database; we checked. A mismatch in how the server returned rows meant every request failed, and the app caught the failure and quietly showed an empty list. A server error and "you have not saved anything yet" looked identical on screen, so we spent a long time hunting a save bug that did not exist. The real fix was small. The change that mattered was making failure look like failure, so the screen now says it could not load and offers to try again.

Turning a rule off did not turn it off. The app stops you leaving a second postcard within 500 metres of one of your own, so a single person cannot stack a place with their own taste. To test freely we set that distance to zero, which changed nothing. Zero means "within zero metres", and the iOS Simulator reports a fixed location, so every attempt sat at exactly the same coordinates as the previous postcard and satisfied the rule. A negative distance disables it properly, because a distance is never negative.

A crash that was not an exception. Adding place names broke sixteen tests at once. The underlying service does not exist in the test environment and fails in a way that our error handling was not written to catch, because Dart draws a line between recoverable exceptions and lower level errors. It sailed straight through. This was the second time that same distinction caught us out, so it is now handled deliberately with a comment explaining why.

Making digital text look like ink. The postcards are photographs of real paper, and text drawn on top of them looked like a sticker rather than handwriting. The technique that would fix it properly, letting the paper grain show through the strokes, turns out not to be available in the way Flutter composes text. We got most of the way there with a slightly translucent ink and a very soft bleed instead.

A location service that punishes enthusiasm. Turning coordinates into place names once per row in a list worked fine with four postcards and would have failed badly with forty, because the phone throttles those lookups aggressively. We now resolve them once per list and reuse the answer for anything within about a hundred metres, so a whole neighbourhood costs a single lookup.

Accomplishments that we're proud of

Anonymity that is structural rather than promised. You cannot get an author out of this backend because the author is not in the shape it returns. We verified that directly against the database permissions rather than trusting a dashboard to tell us it was fine, which turned out to matter: the platform grants access in a way that our first attempt to revoke it silently missed.

A test suite that runs the whole app with nothing plugged in. No network, no GPS, no audio, about thirty seconds. That safety net is the only reason a project this design heavy could keep changing shape without regressing.

A map that refuses to be precise. Choosing counts over pins made the app less impressive in a screenshot and more honest in the hand. It is the decision we would most want a reviewer to notice.

A visual language that feels handmade. Torn paper, tape, handwriting, and a postmark, rather than cards and shadows. It took an unreasonable number of iterations, and it is the reason the app feels like something you found rather than something you opened.

What we learned

The constraint that felt like a loss usually improved the product. Losing Spotify meant anybody can hear a song without a subscription. Refusing to draw pins meant the map communicates a mood rather than a target. Not knowing who wrote a postcard meant nobody can perform for followers.

We learned to distrust a passing test. One of ours protected a privacy property and passed while proving nothing, because the sample data we fed it did not contain the thing it was meant to strip out. Now, when a test guards something that matters, we break the implementation on purpose first and check that the test notices.

And we learned that silence is the worst failure mode. An error swallowed into an empty list cost us far more time than a crash would have.

What's next for MusicNote

  • Cover photos on postcards, with location data removed before anything leaves the phone.
  • Notifying you when somebody picks up what you left, without revealing who.
  • Seasonal decay, so a place's soundtrack reflects who is passing through now rather than accumulating for ever.
  • Android, followed by a careful rethink of the discovery radius for rural areas, where 300 metres is a very different proposition.

Built With

Share this project:

Updates