Tazzina

Reducing the waste and guesswork of dialing in coffee.

Download on the App Store

Role
Creator: design, engineering and operations
Team
Solo
When
Since January 2026

My favorite interactions

I like making products that feel like real physical objects. Tazzina is where I leaned into that the hardest.

Context

Tazzina is the first app I've made in years. I designed and built all of it on my own: the SwiftUI app and backend, bag scanning, suggestions, analytics, the website and an admin console for my admin operations. It's live on the App Store and free for now.

I started it after getting into espresso. Dialing in means adjusting grind size, dose, yield, time and sometimes temperature until a coffee tastes right. Change the wrong one and the next shot gets worse. It usually took me two to five shots per bag. At around 18g a shot, that's 36g to 90g of coffee, or 15% to 35% of a 250g bag wasted before the first good cup.

shots to dial in a new bag
2–5
of coffee wasted per bag
36–90g
of a 250g bag gone before a good cup
15–35%

At first I wrote the grind size on masking tape and stuck it to the bag. That was fine for one bag. Once I had several beans in rotation I couldn't remember the other variables. The Notes app was cumbersome to use and made it harder to keep a log of attempts to learn from.

Masking tape on coffee bag with grind size written on it

Why another coffee app

Tazzina is for people who want the best cup they can get from a bean and want to repeat it. Especially people like me who keep buying new beans and don't want to waste time and coffee getting each one right.

Notes can hold the numbers but knows nothing about the coffee they belong to. Dedicated coffee apps like Beanconqueror go further but they're still spreadsheets with a UI. They don't know anything about a bean or a grinder until you type it in. You enter the roaster, the bean, its details and every setting. The app just stores it.

Tazzina starts from the bean because I wanted a journal that understands coffee. Every bean is a public entry in a library of over 4,000 beans, like wines on Vivino. You add one to your shelf by scanning the bag or searching for it. Your brew attaches to that bean and stays with it. You can also share it with a friend who buys the same bag.

When the bag runs out you archive the bean. When you buy it again the brew comes back. The first time that happened I had forgotten I'd ever bought the bean. Tazzina brought back grind 6, 36g yield and 93°C.

Knowing the bean also lets Tazzina track freshness from the roast date, suggest how to brew it based on its characteristics and remind you to grind finer as it ages.

Tazzina home screen listing current beans as cards with roast date, freshness, and saved brew Tazzina collections screen with stacked bean cards using coffee-bag artwork Tazzina camera screen scanning a coffee bag to identify the bean

Technical decisions

I started with a web app but the workflow kept pointing to native iOS. Scanning a bag, checking a widget or glancing at a watch are small interactions. They're also the ones you need when you're standing next to the machine.

I chose Convex over iCloud. iCloud is good at syncing one person's data but suggestions need everyone's brews in one place. I also wanted to keep Android and web open without rebuilding the data model.

Guided dial-in

The guided flow changes one variable at a time so beginners don't overcorrect. It asks how the brew ran and how it tasted using photos instead of coffee jargon. Sour is a lemon and bitter is chocolate. Every brew that comes out of it is also cleaner data for suggestions.

I tried a tasting compass first but the classic espresso compass has always felt almost unintelligible to me. It assumes people have the vocabulary to describe what's wrong with a cup. I wanted Tazzina to help people judge a brew without making them pretend to be professional tasters. The manual sliders use the same plain language: less sour, less bitter, stronger, weaker.

Tazzina guided dial-in step asking how the brew flowed, with photos of a pour-over running slow, steady, and fast Tazzina taste step showing sour, perfect, and bitter as a lemon, an orange, and chocolate Tazzina brew screen with plain-language sliders for grind, water, and temperature

The long-term strategy

Every saved brew can give the next person a better first cup. If enough people save brews for the same bean Tazzina can say "start here" instead of leaving everyone to waste the same few shots.

Grind size makes this hard. "Grind 6" means nothing unless you know the grinder. A Breville Barista Express goes from 1 to 16, a Niche from 0 to 100, others from 0.0 to 10.0 or 1A to 10F. So every brew is tied to a grinder and suggestions only draw from brews on the same one.

Cross-grinder translation goes a step further. Once enough brews connect the same beans to different grinders Tazzina can work out how a setting on one grinder maps to another. It can then suggest a starting grind for a bean nobody has brewed on your grinder yet.

No other coffee app does either of these. None of them aggregate grind size per bean and none translate it between grinders. The logic is built but it needs far more brews than a new app has.

Tazzina community suggestion for a Chemex brew on a Sette 270 grinder Tazzina community suggestion for an Orea pour-over brew, anchored to a specific grinder Tazzina community suggestion for an espresso brew, anchored to a specific grinder

Strategy pivot

Suggestions are what set Tazzina apart. But they only work once lots of people have saved brews and a new app doesn't have those people yet. So I made a conscious call to change strategy. Instead of leading with suggestions I made every other part of Tazzina worth using on its own. That includes smaller tools like scaling a brew for a bigger batch. If people save brews for their own reasons the data for suggestions builds up as a side effect.

The biggest investment was the bean library. I grew it to over 4,000 beans with details for each one. That turned it into a way to discover new coffee as well as keep track of your own. You can browse by roaster, roast, origin, process, tasting notes or varietal. Those details also let Tazzina suggest how to brew a bean without anyone else's brews. Grind size is the exception. It still needs real brews on the same grinder.

beans in the library with metadata for each
4,000+
Tazzina search screen with filters for roast, tasting notes, origin, process, and varietal Tazzina roaster page for Bigface listing its beans with origin, tasting notes, and roast level Tazzina screen for scaling a brew to twice the dose and water at the same ratio

What did not work

Recognizing a bean from the bag took a few attempts. First I used OCR to pull out only the roaster and bean name and filtered out words like roast levels and tasting notes. The results were unreliable. An LLM was more accurate but slower and more expensive.

What worked at first was simpler. I saved every word OCR found on the bag as something to match against and asked people to type the roaster and bean name when they added a new bean.

Scanning improved a lot once the library had details for each bean. Most of what's printed on a bag is already in the bean's metadata. That gave scans plenty of other fields to match on and I could stop storing scanned words altogether.

Managing the library

I underestimated how much work would go into verifying beans. Convex is a good backend but its dashboard isn't built for this. To check one submission I might have it open in one tab, the existing bean in another, Google in a third, a color picker somewhere else and a contrast checker after that. Even simple questions like "is this the same roaster?" were awkward when I was comparing names across rows of JSON.

So I built an admin console for myself. It puts the review queue, bean details, merging, web search, color picking and contrast checking in one place and in the order I work through them. Since then the library has grown large enough that most beans people scan already exist. New submissions dropped from about three a day to one a week.

new beans to verify at first
3 a day
new beans to verify now
1 a week

I chose not to spend design time on it. It has one user (me) so it only needs to fit my needs and the way I think about the data. The screens are dense because that's fastest for me. They follow my own mental model instead of one a wider group of users would need.

Tazzina admin console showing the grinder catalog and per-grinder range settings Tazzina admin console showing bean card color selection and contrast checking

Scope control

I decided against puck prep guidance and shot logging. Both were useful ideas but they would have turned Tazzina into an espresso course or a daily tracker. I only care about the brew that worked. That's also the biggest difference from logging apps that record every attempt.

There were other cuts. Official bean photos had copyright issues, temperature was too advanced for the first version, sharing was useful for distribution but premature and other brew methods like V60 and Aeropress could wait.

Three of those came back once the basics held up. Temperature and sharing are in. Tazzina now supports pour-over, Aeropress, moka pot and more alongside espresso without any change to the core loop.

Design

I wanted the beans to be the hero of the app. Coffee bags already have a strong visual language and I let it define the look of the product. Bean cards show your own photo of the bag as a small polaroid. They take their colors from the bag and use subtle 3D motion so they feel like objects you could pick up.

The same cards are on Apple Watch. I reach for it more than I expected when my phone is in another room.

Tazzina Apple Watch app showing a collections list and the current beans with their brews

Impact

Before
2–5 shots
A new bag started from a guess. My best system was grind size written on masking tape and dialing in cost me two to five shots per bag.
After
1–2 shots
Each bean keeps its own brew and rebuying a bag brings it back. The guided flow moves one variable at a time. A new bag now takes me one or two shots and sometimes none when a suggestion exists.

Those numbers are from my own use. Tazzina only has a handful of users so far and marketing starts soon. There's no real usage data yet.

The analytics are set up for the questions that will tell me whether the strategy works: whether people use suggestions, how many attempts a bean takes, where scanning and search fail and how often people come back to check a brew. The first thing I want to learn is whether people save brews for themselves. Every grind suggestion depends on it.