Back to work
Product & UI Lead · Senior Design Project · Computer Vision · React

Designing trust into a barcode-free checkout

Self Checkout+ is a computer vision grocery checkout system that identifies items without barcodes. I led the product direction and built the entire customer-facing experience, designing an interface that makes shoppers trust a system working invisibly behind the scenes.

Project Self Checkout+
My Role Product & UI Lead
Context Senior Design Capstone
Frontend React · Framer Motion
Backend FastAPI · SQLite
Hardware Raspberry Pi · ATmega328p
UX Design React Computer Vision Product Ownership Framer Motion Hardware + Software
01

The Problem

Traditional self-checkout fails at the same moment every time: when there's no barcode. Loose produce, bulk goods, specialty items, the customer either guesses a code from a printed menu or waves down an attendant. That's the failure point we set out to eliminate with computer vision instead of a scanner.

But removing the barcode doesn't remove the two people who have to trust the result. Shoppers need to believe a system working invisibly behind the scenes got their total right before they'll pay it. Store staff need a fast way to step in the moment it doesn't, without holding up the line. Design for the first group and ignore the second, and one CV miss turns into a stuck checkout lane. The CV pipeline itself, YOLOv8 detection matched against a CLIP-trained embedding database, was strong from early on. The open problem was everything around it: an interface that earns trust for a system nobody can see working.

6 distinct customer screens in the checkout flow
2 user segments: shoppers and store staff
0 barcodes scanned in the entire checkout flow
4 person team across hardware, CV, and software
02

My Role

As Product & UI Lead, I owned two things that rarely travel together: every pixel of the customer experience, and every major decision about what the product should actually do.

Product Ownership
Defining what the system is
  • Led product decisions with the team and advisor, from feature scope to architecture tradeoffs
  • Raised and resolved questions like: should we auto-add items or ask first? should we show a live video feed?
  • Contributed to the systems thinking and architecture for the computer vision model, not just the customer's side of it
  • Cut features that added complexity without adding trust or speed
Design & Engineering
Building the experience layer
  • Designed and built the complete customer checkout flow in React, all six screens
  • Built the admin dashboard for store operators: inventory, overrides, session monitoring
  • Integrated the Raspberry Pi cameras myself, wiring the hardware capture into the CV module
  • Owned the SQLite schema and the integration between the React frontend and FastAPI backend
  • Implemented Framer Motion animations and resolved animation-blocking issues during API calls
The stack

Customer UI and admin dashboard in React with Framer Motion. Backend in FastAPI with SQLite. CV pipeline using YOLOv8 + CLIP + FAISS + OCR running on a Raspberry Pi. Hardware signaling over an ATmega328p MCU. My domain was mainly the software layer above the CV model, plus the camera integration and input on the CV architecture.

03

The Customer Experience

Six screens, one goal: make shoppers trust a system working entirely out of sight. Every design decision was about control, giving customers a visible way to verify, correct, and confirm before money changed hands.

04

Key Design Decisions

The most interesting design work wasn't the visual styling, it was the product thinking that shaped the flow. Each one below started from a pain point a shopper or a store operator would actually hit, and the tradeoff I made to fix it.

01
Build in a confirmation step, don't auto-add items
Pain point: a system that silently adds items, even accurately, feels like it's deciding for you. When the CV pipeline identifies an item, the shopper sees "Did we get this right?" before it's added to the cart. That trades a little speed for trust: it gives the customer a moment of control and an easy way to fix a wrong call without flagging down an attendant. It costs one extra tap per item, and for a brand-new checkout format where trust isn't a given yet, that was worth it.
UX decision
02
Cut the live video feed entirely
Pain point: watching yourself get scanned is unsettling, not reassuring. An early version showed a live camera feed in the scanning zone. I cut it. Live video is visually noisy, raises real privacy questions, and doesn't help the customer finish any faster, the task is checkout, not watching the camera process food. What replaced it was a clean scanning zone with a simple status indicator, enough to show the system is working, nothing more.
Product decision
03
Let the backend own the cart total
Pain point: even a few cents of drift between what the screen shows and what you actually get charged reads as a bug, and in a system nobody can see working, one visible bug undoes a lot of trust. Early builds kept the cart total in React and synced it with the backend now and then, which caused subtle mismatches. I moved the cart entirely to the backend and had React simply render whatever the API returned. One source of truth, no drift, no reconciliation bugs.
Engineering decision
04
Keep animations off the data-fetching thread
Pain point: a screen that stutters or freezes mid-checkout feels broken, even when the backend is working fine. Framer Motion transitions would stutter whenever an API call blocked the main thread. The fix was separating the animation from the data fetch: the UI plays the transition on its own, and the component updates when the fetch resolves, not when it starts. Every screen transition felt instant even while the backend was still working.
Engineering decision
05

The Integration Challenge

The hardest problems weren't in any single layer. They were at the seams between layers. The CV pipeline produced detection events. The FastAPI backend processed them into cart items. The React frontend rendered the cart. When all three systems were moving at once, things broke in ways no single component's tests could catch.

As the person responsible for the integration layer, I owned the SQLite schema that made the CV pipeline's output usable by the frontend. I also designed the API contract between FastAPI and React: which fields the frontend could trust, which had to be calculated server-side, and how errors from the CV layer should surface to the customer.

The key insight

The hardest integration problems weren't really about code. They came down to being clear about which layer owned what. Once each layer had one job, most of the bugs had nowhere to hide.

CV pipeline → SQLite → FastAPI → React
Each layer had a defined contract. The CV pipeline wrote detection events, SQLite stored item metadata, FastAPI computed totals, and React rendered state. Keeping those responsibilities separate is what made the system easy to debug.
06

The Admin Interface

The customer-facing flow was one half of the product. The other half was the operator dashboard, the interface that lets store staff manage inventory, approve manual overrides, and monitor active sessions. I designed and built this separately from the customer UI, giving it a simpler, more utilitarian look that reads as an internal tool rather than a customer product.

Mockup: the admin inventory dashboard — a sidebar with store operations, a product database table showing item, category, price, CV detection confidence, and status, with summary stats for total items, active items, average confidence, and pending reviews.

Mockup: the override queue — three pending items the CV model could not identify, each with the lane, session, reason code, price, and Approve/Reject actions for store staff.

The override queue was one of the most important features I designed, not because it gets used often, but because it's the plan for when things go wrong. When the CV pipeline can't identify an item, store staff need a fast way to add it manually without interrupting the customer's checkout. Every override is logged with a reason code, which also gave us data on exactly where the CV model needed to improve.

"The system could identify a banana with 99% confidence. My job was designing the 1%, the moment it doesn't, and what happens next."

07

The Results

We wrote seven testable specs before building anything, and validated every one. The numbers I cared about most were the ones a shopper actually feels, that the total always matched, that the screen never froze, and that an uncertain item still resolved inside the same six-second window.

100% Top-1 accuracy on the 16-SKU test set, no misclassifications (target was 99 in 100)
<6s Full checkout, from item placement to emailed receipt, across 50 trials, none over 8s
12 hrs Continuous operation with zero crashes or disconnections
<200ms Admin operations, with no freezes during a live customer checkout

Two of those tie straight back to the layer I owned. The cart total matched the backend on all 50 transactions, because I'd made the backend the one place the total lived instead of syncing two running counts. And the interface stayed responsive mid-scan because the animation and the data-fetching ran on separate paths. Speed and accuracy were the baseline. What I really wanted to prove was that the trust held up under real conditions, including the moments the model wasn't sure.

North Star metric

If this ran in a real store, the main thing I'd watch is override rate, how often a shopper's cart needs a manual fix. Accuracy tells you the model is good; override rate tells you whether shoppers trust it enough to not double-check every item themselves.

08

What I Learned

01
Accuracy wasn't enough to earn trust
The CV model was genuinely accurate. But accuracy doesn't equal trust. Trust comes from the interface: the confirmation step, the item preview, the clear error path, the ability to delete something. People trust a system when it gives them visible control and an easy way to fix it.
02
Product decisions live in architecture meetings
Some of the biggest product choices happened in technical discussions: whether to cache detection events or query live, how the backend should handle concurrent sessions, what the API contract between FastAPI and React should look like. Being in those conversations, and asking "what does this mean for the customer?", mattered more than any later feature decision.
03
Less on screen made the process smoother for the customer
Removing the live video feed wasn't about making the UI simpler. It was about making the customer's task clearer. Every element on screen should earn its place by helping the user finish. The camera feed was technically impressive but distracting, and impressive doesn't help someone check out faster.
04
Integration is a design problem
The hardest bugs weren't in any single component. They were at the seams: the animation and API call competing, the cart UI and backend total diverging. Designing clear ownership boundaries between layers would have caught those problems before they became debugging sessions. I'd now treat the API contract as something to design carefully up front, not patch in later.

"Anyone can build a system that works when everything goes right. I spent my time on the moments it doesn't, because that's where a shopper decides whether to trust it."

Back to all work