All projects
Mar 2025 — Apr 2025Name that song.

SongSnap

Built with my team at McMaster, SongSnap is a cross-platform mobile app designed to identify the song stuck in your head from whatever you remember: an audio clip, a lyric fragment, or a natural-language description.

Source 95%+identification accuracy

SongSnap came out of a software design course, and the assignment was not really about music. We were asked to build something whose subsystems each demanded a different architecture, then justify why each one was the right fit. Picking the problem was ours. We chose song identification because it splits cleanly: identifying a song, storing what happened, and suggesting what to listen to next are three genuinely different jobs, and forcing one pattern across all three would have been obviously wrong.

The app takes whatever you can remember. A clip of audio, a fragment of lyrics, or a description in plain language such as the genre and roughly what the artist sounds like. Most identification tools want a clean recording playing right now, which is useless when the song is stuck in your head and nowhere else.

Identification is a blackboard. The idea comes from systems where several specialists work on one problem without coordinating, and it fits here because audio, lyrics and description are separate kinds of expertise with no reason to know about each other. A controller routes each input to the analyzer that handles it, each analyzer writes a partial solution onto a shared board along with a confidence score, and the board finalizes by returning whichever candidate scored highest. Conflicts resolve by comparison rather than by any analyzer arguing its case, and adding a fourth source of evidence would not touch the three that already exist.

The reporting subsystem is a repository, because the requirement there is the opposite one: storage should be replaceable and the code that writes reports should not care where they land. A ReportGenerator takes an abstract Report and calls save on a ReportRepository interface. Firestore sits behind that interface today. Moving to Postgres would mean writing one class and changing one line where the dependency is constructed.

Recommendations use the strategy pattern, since recommending by artist is one approach among many. SongRecommender is an abstract base with a single method, and ArtistBasedRecommender implements it. The route chooses a strategy after identification finishes, so a mood based or tempo based recommender could be dropped in without the endpoint knowing the difference.

Underneath all three, the data layer is a set of abstract base classes. Audio, Lyrics and Description are all subclasses of one Data type, and Song is a Data as well, which is what allows the controller to dispatch on type and the blackboard to compare results from analyzers that share nothing else.

The backend is Python and FastAPI, with Pydantic validating every request and Gemini doing the actual model work: a multimodal call for audio clips, a text call for lyrics and descriptions. Uploaded audio is written to a temp file in 1MB chunks and deleted in a finally block, so a failed request does not leave a stray mp3 on the server. The front end is React Native with Expo Router and TypeScript, and the whole thing runs on Heroku behind uvicorn.

What I took from it is that architecture is a set of trade-offs you argue for, not a diagram you draw afterwards. Choosing the blackboard meant accepting that no analyzer can help another. Choosing the repository meant an extra interface for a database we never actually swapped. Both were the right call for what those parts had to do, and being able to say why in a design review is a different skill from making the code run.

How SongSnap thinks

Four subsystems, four designs

The assignment was to build something whose parts each needed a different approach, then defend the choices. Pick a subsystem to see the one it got.

System view
Audio clipAudioLyric fragmentLyricsDescriptionDescriptionAudio analyzermultimodal modelLyric analyzertext modelDescription analyzertext modelBlackBoardfacts + partial solutionsAUDIO_RESULTLYRICAL_RESULTDESCRIPTION_RESULTfinalize_solution()highest confidence winsSongname, artist, score

Blackboard

Blackboard architecture

A controller hands each input to the analyzer that understands it. Every analyzer writes what it found onto a shared board as a partial solution with a confidence score, and the board finalizes by returning whichever scored highest.

Why this oneAudio, lyrics and descriptions are separate kinds of expertise with no reason to know about each other, so they contribute to one problem space instead of calling each other.

Multi-modal identification

Audio, lyrics and natural-language descriptions all feed the same identification path.

Blackboard coordination

Specialised analyzers contribute partial solutions to a shared problem space, and the competing predictions are resolved by confidence.

Modular by design

Different responsibilities use different approaches rather than forcing the whole application into one structure.

The app running end to end: a clip goes in, the expert modules argue about it, and the most confident answer comes back.