Inspiration

Most restaurants I go to still use paper menus. When a price goes up, the old one gets crossed out and the new one is written next to it. Some places have QR code menus, which are easy for customers to use. But setting one up means creating an account and retyping the whole menu, and that's why so many places stay on paper.

I wanted something a café or bistro manager could try in under a minute: take a photo of the menu, get a QR code for the tables. No typing, no account.

What it does

The manager enters the restaurant name and uploads one to five photos of a printed menu or a chalkboard. A few seconds later they get:

  • the dishes and prices read from the photos, which they can fix, add to, or hide when a dish runs out;
  • a QR code to print for the tables, pointing to a public page like /chez-marcel;
  • a six-character code like K7Q-29P to come back and edit the menu the next day.

Customers scan the QR code and see the available dishes and prices on their phone.

How we built it

I planned the project with Claude Code before writing any code (scope, product requirements, technical spec, build checklist), then built it step by step and reviewed each change in VS Code.

  • Frontend: React 19, Vite, TypeScript, Tailwind CSS 4 and shadcn/ui with a custom theme.
  • Backend: FastAPI on AWS Lambda through Mangum, behind API Gateway. CloudFront serves the React app from a private S3 bucket and routes /api/* to the Lambda.
  • Menu reading: a single Claude Haiku 5.5 call reads all the photos at once and returns dishes and prices as structured output validated with Pydantic. The prompt handles French price formats (12,5, 12€50) and merges dishes that appear on several photos.
  • Data: one MongoDB Atlas collection. Photos are never stored.

Before upload, the browser shrinks each photo to 1600 px and re-encodes it as JPEG. A Lambda request is limited to about 6 MB, and API Gateway base64-encodes the body, which adds a third to its size:

$$\text{request size} \approx \frac{4}{3} \sum_{i=1}^{n} s_i \le 6\ \text{MB}$$

where $s_i$ is the size of photo $i$. Compressing keeps five phone photos well under the limit, makes the extraction faster, and removes EXIF metadata such as GPS position.

The edit code replaces the account. Its alphabet leaves out 0/O and 1/I/L so it can't be misread from a handwritten note. That leaves 31 characters, so $31^6 \approx 8.9 \times 10^8$ possible codes.

Challenges we ran into

I'm a full-stack developer, more at ease on the back end. My main challenge was the interface: the screens I got from the agent often looked like every other AI-generated app, and I didn't know if the problem came from my instructions or from the agent.

During the build, I found a big part of the answer. shadcn init installs the Geist font, a neutral gray theme and a dark mode by default. Left as they are, these defaults are what make an app look generic. Nobody chose them, so nothing about them is specific to the product.

Accomplishments that we're proud of

  • On the live site, a real chalkboard menu with 22 dishes was read in about 4.5 seconds.
  • A manager goes from a photo to a printable QR code without typing a single dish or creating an account.
  • If nothing can be read from the photos, the restaurant, its code and its QR code are still created, and the manager can type the menu by hand.
  • The interface has its own look instead of the default AI-app style.
  • The app is deployed on AWS and costs close to nothing to run for a demo.

What we learned

Giving the agent a design direction before the first screen changed the result:

  • Write the theme first. I created theme.css before any screen: a blue-green "Pétrole" palette with a touch of apricot used only for the "Menu ready" moment, Fraunces for headings and DM Sans for body text.
  • Show a reference, not a description. I gave the agent the Uxcel landing page from Mobbin and said what to keep (a small logo, a headline, a short subtext, two buttons) and what to change (the buttons switch tabs instead of opening new pages).
  • Say what you don't want. No emoji, no gradients, no chalkboard styling.

What's next for Paper to QR

  • Updating the menu with a new photo when the menu of the day changes, instead of editing dishes one by one.
  • Letting restaurants customize their customer page with their own colors and logo.
  • Adding categories, descriptions and allergens to dishes.

Built With

Share this project:

Updates

posted an update —

How to test it : Open https://d1gdupqowt3o8k.cloudfront.net, ideally on a computer with your phone at hand.

Option 1: start from the demo menu (fastest)

In the Existing menu tab (open by default), paste the code 3TG-QSW and click Validate. The Menu screen of the demo restaurant, Chez Marcel, opens with its dishes and prices. Edit the menu: click the pencil to change a dish's name or price, then the check to save; add a dish with Add dish; delete one with the x; turn one inactive with the toggle. See the customer side: scan the QR code with your phone, or click the link under the QR code. The page shows the restaurant name and the active dishes only, so a dish you turned inactive is gone and your edits are there. The demo menu is shared by everyone who uses this code, so you may see changes made by other judges.

Option 2: create your own menu from a photo

Open the New restaurant tab, enter any restaurant name and add one or more photos of a menu. If you don't have one at hand, use the sample photos in https://github.com/ayshagassama1/paper-to-qr/blob/main/sample-menu-photos

Log in or sign up for Devpost to join the conversation.

Submission history