Activity

ryan

Shipped this project!

Hours: 15.0
Cookies: 🍪 254
Multiplier: 16.91 cookies/hr

Added three new features to pic2recipe: you can now favorite recipes and view them in a dedicated tab, share recipes as formatted text through your phone’s share sheet, and mark recipes as “made” which automatically removes the used ingredients from your pantry.
The trickiest bug was the favorite heart not showing as filled when opening a recipe you already saved. Turned out I was passing the wrong object to the component - the recipe from the API didn’t have an ID field, so the favorite check always failed. Fixed it by passing a manually built object that includes the ID.
Proud of how the “I made this” button turned out. It compares your pantry against the recipe’s ingredients, figures out which ones you used, and clears them out. Small feature but it makes the whole app feel like a complete loop: scan ingredients, find a recipe, cook it, and your pantry updates automatically.
Unfortunately, it’s Android only since IOS support requires $100

ryan

What I added this devlog:

Recipe Sharing
Added a share button on the recipe detail page next to the favorite heart. Uses React Native’s built in Share API so no new libraries were needed. Formats the recipe into a readable text block with the title, cook time, difficulty, ingredients list, and numbered steps. Opens the phone’s share sheet so users can send it through text, email, or any other app.
“I Made This” Button
Added a button that shows up at the bottom of the instructions tab. When you press it, it looks at the recipe’s ingredients, checks which ones match what’s in your pantry, and removes those from the pantry. Then it sends you back to the home screen.

To make this work I had to:

  • Pass the user’s pantry ingredients from the results page through RecipeCard into the recipe detail page as a route parameter
  • Add a removeIngByTitle function to the database operations since the existing remove function only worked with IDs
  • Update InstructionOption to accept and pass down the ingredient lists
  • Create a new MadeRecipeButton component that handles the filtering and removal logic
Attachment
Attachment
0
ryan

Favorites Feature

What I Built:
Added a favorites tab where users can save recipes they like. There’s a heart icon on every recipe card and on the recipe detail page. Tapping it saves the recipe locally. The favorites tab shows all saved recipes with a count at the top.

How It Works
Favorites are stored in WatermelonDB, same as the pantry ingredients. I added a new favorites table to the schema with columns for recipe ID, title, ingredients, directions, cook time, difficulty, and equipment. Since WatermelonDB doesn’t support array columns, I store arrays as JSON strings and parse them when reading.
The heart icon is its own component called FavoriteButton. It checks the database on mount to see if the recipe is already saved, and fills the heart if it is. Tapping it adds or removes the recipe.
The favorites tab uses withObservables to reactively display saved recipes. When you favorite or unfavorite something, the list updates right away without needing to refresh.
Problems I Ran Into

Heart not filled on recipe detail page - I was passing the wrong object to FavoriteButton. The recipe from useRecipeDetail doesn’t have an id field, so the favorite check always failed. Fixed by passing the manually built recipe object that includes the ID.
“0 min” instead of “Unknown” - WatermelonDB returns 0 for empty number columns instead of null. The ?? operator doesn’t catch 0 because it only handles null and undefined. Switched to || which treats 0 as falsy.
“Uses your: “ with nothing after it - The favorites list was passing an empty ingredients array to RecipeCard. Fixed by also observing the ingredients table in FavoritesList so it knows what’s in the pantry.
Heart positioning - The heart icon would sometimes go off the card depending on title length. Fixed by using absolute positioning in the top right corner with pr-12 on the title so they don’t overlap.
Tests breaking after merge - The ingUsed.test.ts test started failing because RecipeCard now imports FavoriteButton, which imports the database, which tries to start SQLiteAdapter. SQLiteAdapter is a native module that doesn’t exist in Jest. Fixed by mocking the database module in the test file.
Schema migration - Had to bump the schema version from 2 to 3 and write a migration file so existing installs don’t crash. Also had to clear app data on my phone during development because the old database didn’t have the favorites table.

Attachment
0
ryan

VIDEO DEMO: https://youtube.com/shorts/t7tEtORol2o?feature=share

  • Fixed the ingredient scanner not working in the apk (had to add a secret in eas)
  • Made the find recipes button smaller so it’s easier to delete ingredients (used to be covered)
  • added a label on the scan tab for better UX
  • Added ability to scan receipts too in the scan tab
  • Integrated Sentry (error reporting software)
    No ios support unfortunately because it costs $100
Attachment
Attachment
Attachment
0
ryan

Shipped this project!

Hours: 73.1
Cookies: 🍪 1283
Multiplier: 17.55 cookies/hr

pic2recipe: point your camera at ingredients, get recipe recommendations from a database of 2.2M recipes.
Built the full stack from scratch: React Native frontend with WatermelonDB, FastAPI backend with pgvector for vector similarity search, and Gemini Flash for ingredient detection. The most challenging part was trying to get YOLO object detection working on-device: spent days debugging TFLite output tensor formats before realizing the classification head breaks during quantization. Pivoting to a cloud vision API took 30 minutes and worked better than anything I trained. Proud of the embedding pipeline: figured out how to generate and load 768-dim vectors for 2.2M recipes using numpy memmap after the naive approach kept crashing from OOM errors. Learned a lot about when to push through a problem vs when to pivot.

ryan

Finalized app before shipping:

  • added an image icon (not just the default app)
  • changed app name
  • added readme
Attachment
0
ryan

ALMOST READY TO SHIP!!

In this devlog, i’ve:

  • added frontend tests with the Jest testing framework, and integrated a continuous integration pipeline using Github Actions to automate testing on push/pull request
  • added some more small ui changes like margins/padding, only showing the “Add to pantry” button when Gemini API actually identifies ingredients, and showing the user an error screen when they press the “Find Recipes” button without having any ingredients.
  • turned the app into an APK using EAS and right now i’m doing some last minute testing before shipping.

ONE MAJOR SETBACK:

  • My demo will only work on android unfortunately because it’ll cost $100 to launch the app on ios (i’m too poor)
  • also i have an android so i can’t test on ios even if i could pay the $100

NEXT:

  • SHIPP
Attachment
Attachment
0
ryan

I implemented a continuous integration pipeline using github actions to automate the backend tests. This ensures that new features that i add won’t break old features.

again this doesn’t change anything in the actual project so the image is just the test cases passing on github.

Attachment
0
ryan

I did a lot of learning with docker and containerized the backend + ngrok tunnel i use to send requests from my phone to the server. I also created tests for the backend, testing the service (getting reccomendations), repository (querying the db based on query embedding values), and routers (making sure that valid ingredients would work and invalid ones wouldn’t). I also created a separate mock test db in docker to do this testing. I learned a ton about pytest and how tests should be done.

Since this stuff doesn’t actually show on my ui, i didn’t really know what to screenshot but there are my running containers and tests working.

Attachment
Attachment
0
ryan

Trained YOLO11n on two different food datasets on Kaggle. Both models could locate objects on-device but couldn’t classify them correctly — always predicted the last class index regardless of input
Debugged for days. Tried every combination of TFLite export settings (float16/float32, with/without NMS, different input normalizations). None worked. YOLO’s classification head breaks during TFLite conversion. Found a GitHub issue confirming this is a known problem with no fix

Gave up on on-device ML. Switched to Gemini 2.5 Flash API -> send photo, get back ingredient labels + bounding box coordinates. Took 30 minutes to implement vs days of YOLO debugging (are we serious)

Attachment
0
ryan

I fixed the bounding box logic. But the model I trained is pretty bad and innacurate (see attached photo, it detected an apple as a lemon) so i’m currently training another model in hopes that that one is more accurate.

I also still have to:

  • make it so once the user presses the “add to pantry” button, it’ll actually add to the db
  • fix some race condition errors with the model loading then running inference with the capture button

But after fixing those and some polishing, i’m basically done the MVP for this project!

Attachment
0
ryan

I’ve made a ton of small UI fixes and changes for a better user experience. for example, adding scrolling to the ingredients list, loading screens for everything. i’ve also started on implementing the ml model to detect ingredients. I’ve added the camera, trained an ml model, did a ton of debugging, and now have code that runs inference on it to detect ingredients (this works), but right now the bounding box logic i’m trying to implement isn’t working so i’m debugging that right now.

Attachment
Attachment
0
ryan

The results screen now uses the ingredients inside the ingredients db instead of being hardcoded. And i also fixed a bug with the options on an individual recipe screen not working

Attachment
Attachment
0
ryan

added watermelondb, which is a database which uses sqlite but has lazy loading. I first had to include it in my config files, then i created the Ingredient model, table schema, and functions to add and remove ingredients from the db. And finally, i added a “+” button on the main page to allow users to add and remove ingredients from the db.

Attachment
Attachment
0
ryan

added equipment, time, and difficulty to the actual ui. Also wrote a little script to find what ingredients the recipe uses that the user has. Also fixed some ts errors.

Attachment
Attachment
0
ryan

I added cook time, equipment and difficulty to the database for a better user experience using regexs to scan the directions and ingredients for key words. I first created the test_parsers.py file to test whether my parsers would work, then in enrich_recipes.py, I used the parsers to load new columns in the db. Then I modified the sqlalchemy schemas and pydantic models in the backend to account for the new columns

Attachment
Attachment
0
ryan

made it so the frontend will actually display the recipe info based on the ingredients given (those are hard coded for now) on the results page. And on the individual recipes page, it now displays the recipes info like directions, ingredients.

Attachment
Attachment
0
ryan

Shipped this project!

Hours: 35.95
Cookies: 🍪 782
Multiplier: 21.76 cookies/hr

i built a website that detects if an image is altered using machine learning and opencv. the hardest part about this project was the machine learning because of how it requires complex math like linear algebra and other stuff to understand how it works. i’m proud of the backend because I learned how to use tools like fastapi and pytest

ryan

so i didn’t find out about Flavortown until I was already somewhat progressed in this project, hence the 26h devlog. I did create a log of my work though so here it is all at once:

March 14, 2026

  • Initialized repo with project scaffolding: .gitignore, LICENSE (MIT), README, placeholder CI workflow
  • Added initial Expo frontend (app.json) and backend requirements.txt
  • Included a TFLite model binary for early camera/ML experimentation
  • Cleaned up tracking — removed package.json and package-lock.json from version control

March 20, 2026

  • Built the data pipeline for loading recipe embeddings into PostgreSQL
  • Created init.sql to set up the recipes table with pgvector (vector(768)) column
  • Wrote load_recipes.py — reads RecipeNLG CSV + a numpy memmap of pre-computed embeddings, batch-inserts into DB (512 rows/batch)
  • Added test_query.py to verify cosine similarity search against the DB using sentence-transformers/all-mpnet-base-v2
  • Removed the TFLite binary and old plan.txt from repo

March 26, 2026

  • Scaffolded the full backend package structure: api/, repositories/, services/, schemas/, tests/
  • Defined SQLAlchemy ORM model (Recipe) mapping to the recipes table with pgvector Vector(768)
  • Created Pydantic schemas: IngredientsRequest (input) and RecipeResponse (output with similarity score)
  • Set up FastAPI app in main.py with CORS middleware and lifespan-based model loading (SentenceTransformer)
  • Added config.py using pydantic-settings to pull DB and model config from .env
  • Stubbed out the POST /recipe route, repo, and service layer (empty files)

March 27, 2026

  • Implemented the core querying logic end-to-end
  • dependencies.py — yield-based SQLAlchemy session factory for FastAPI’s Depends()
  • recipe_repo.py — pgvector cosine distance query, returns top-k results with similarity scores
  • recommendation.py — service layer that encodes ingredient text into a vector and passes it to the repo

March 28, 2026

  • Wired everything together to complete the backend API
  • Connected the POST /recipe router to the service layer with dependency injection (DB session + ML model)
  • Extracted ml_model dict into a separate ml.py module to resolve circular import between main.py and the router
  • Added error handling (HTTPException 404) on the recipe endpoint

April 3, 2026

  • Built the frontend UI shell in React Native with Expo Router and NativeWind (Tailwind v3)
  • Set up file-based routing: (tabs) layout with Kitchen Hub, Scan, and Results screens + recipe/[id].tsx
  • Created reusable components: RecipeCard (results list item) and IngredientItem
  • Configured Babel, Metro, Tailwind, TypeScript, ESLint, and NativeWind
  • Placeholder recipe/[id].tsx page (pending API integration)

April 4, 2026

  • Added GET /recipe/{id} backend endpoint for fetching a single recipe by ID
  • New repo function get_from_id() and Pydantic schema IdResponse (title, ingredients, directions)
  • Built out the recipe/[id].tsx detail screen with collapsible sections: EquipmentOption, IngredientsOption, InstructionOption

April 5, 2026

  • Refactored frontend — extracted shared Recipe interface into src/types/index.ts
  • Created src/services/api.ts with axios HTTP client pointing to ngrok tunnel
  • Two API functions: getRecipe(ingredients[])POST /recipe and getById(id)GET /recipe/{id}
Attachment
Attachment
Attachment
Attachment
0
ryan

added the faq and how-it-works files. i forgot to do this before haha

Attachment
Attachment
1

Comments

ryan
ryan about 1 month ago

btw i fixed the bug with the loading screen not showing up!! thanks for letting me know voters

ryan

So I didn’t hear about Flavortown until I finished this project (except for some small stuff), hence the 35h devlog lmao. but I did keep a log of my progress, so here it is all at once:

March 1, 2026
Initialized repository with MIT license and .gitignore

March 3, 2026
Built out HTML shell (index, FAQ, How It Works pages)
Created initial CSS styling (dark theme, cyan accents, Inter font)
Connected frontend to backend with FastAPI endpoint and fetch API
Added Pydantic response schema (UploadResponse)

March 5, 2026
Mounted /static directory so the browser can access server-side images via URL
Implemented first version of copy-move forgery detection using OpenCV SIFT + FLANN matching

March 6, 2026
Implemented splicing detection model (U-Net with ResNet34 encoder via segmentation-models-pytorch)
Tested end-to-end and confirmed working inference

March 7, 2026
Added animated loading screen with CSS keyframe animations (scan line, pulsing circles, backdrop blur)

March 8, 2026
Built results page skeleton (confidence bar, detected manipulations section, view toggle buttons)
Tuned copy-move detection: reduced sensitivity to lower false positives
Optimized noise detection model post-processing

March 9, 2026
Further tuned SIFT copy-move detection thresholds
Completed results page UI (color-coded scores, per-engine breakdowns, detected/not detected labels)
Built canvas overlay system: red semi-transparent mask for splicing, green bounding boxes for copy-move
Added background cleanup task that deletes uploaded files every 10 minutes
MVP complete

March 10, 2026
Deployed: frontend to Netlify, backend to HuggingFace Spaces (Docker)
Set up CORS middleware for cross-origin requests
Fixed static directory not being found in container
Set up GitHub Actions workflow for auto-sync to HuggingFace
Removed model weights from git tracking (downloaded at runtime instead)
Cleaned up repo structure (removed redundant hf-space folder, converted submodule to normal folder)
Switched to UUID4 for uploaded filenames (was sequential before)
Changed invalid file upload response from 400 to 415 (Unsupported Media Type)
Fixed confidence score ranges and async loading bugs in frontend
Removed redundant code in app.js

March 11, 2026
Added automatic file cleanup via async background task with asynccontextmanager lifespan
Improved splicing detection: added mask ratio cap, morphological open/close, erosion
Introduced dependency injection using FastAPI’s Depends() for the detection service
Created DetectionService class wrapping both engines with ThreadPoolExecutor for parallel execution

March 12, 2026
Set up full pytest suite: conftest.py with fixtures, test_api.py (mock tests), test_detection.py (real inference tests)
Used dependency_overrides to inject mock detection functions for fast API tests
Added pytest.ini with fast/slow markers
Improved false positive reduction: connected component filtering, confidence-scaled blob thresholds, synced mask with confidence output
Set up GitHub Actions CI with two parallel jobs (api-tests, model-tests)
Debugged CI: fixed pytest PATH issue (switched to python -m pytest), fixed model weight download (needed in both jobs since model loads at import time), fixed hardcoded path issues
Removed test images and model weights from git tracking via .gitignore updates

March 13, 2026
Added try/catch error handling in api.js (fetch + response parsing) and app.js (error propagation to UI)

Attachment
0
ryan

Shipped this project!

Hours: 45.64
Cookies: 🍪 710
Multiplier: 15.56 cookies/hr

I build a program that allows you to remap useless keys on your keyboard. The hardest part was definitely the amount of edge cases that I ran into such as if you map a->b and then b->a, it would infinitely spam both keys and basically crash your computer. I’m proud of the heatmap and keypresses wrapped features I added because they look really cool and were highly rewarding to create.

ryan

So I started & finished this project after finding about Flavortown, but I did keep a somewhat detailed log of my progress:

Initial planning:

  • User will be able to remap one key to another key or combination and vice versa
  • Will be a GUI using Java Swing
  • There will be a way for users to see what keys they’ve remapped
  • Mappings will be stored in a hashmap for O(1) lookup
  • Will add a keyboard heatmap cuz it’s cool
  • Will add a wrapped feature (like spotify wrapped) but for your keys

Feb 2+3, 2026:

  • Used JNativeHook to register user keypresses, played around with methods to get used to the library

Feb 4, 2026:

  • created a basic implementation of gui
  • created a custom KeyMap class, which stores keycodes of the key and what it’ll map to
  • played around with trying to block the user’s initial keypress

Feb 6:

  • successfully created the keymapping logic
  • allowed for holding keys too
  • made it so it’ll save all the users mappings inside a text file and it will reload everything into the hashmap that stores everything upon running
  • made some other small changes/features like being able to remove all mappings, hiding the select keybinds options after saving, etc.

Feb 7-9:

  • created a virtual keyboard on gui where you can press two buttons and this will create a mapping between the two.
  • moved gui code away from main.java file to a separate file for organization
  • created custom layouts (100%, 75%, 65%)
  • stored all mappings inside a table so the user can keep track of all of them
  • Also allowed the user to remove a specific mapping by selecting it from the table

Feb 11:

  • fixed a ton of buts
  • Finished MVP
  • switch from saving mappings and custom keys in a text file to storing everything in json using the Gson library.
  • now i can just store everything into one file instead of data from combinations (custom keys) and normal keys being in separate text files

Feb 12:

  • finished heatmap + keypresses wrapped
  • added ability for user to press “shift” and access the keyboard as if shift was being held
  • also allowed users to map shifted keys like @ and !
  • fixed a lot of minor bugs

Feb 13:

  • small changes (minor fixes)
  • basically done

Feb 14-17:

  • fixed bug where it would spam a key infinitely
  • cleaned up and refactored code

Feb 18:

  • added thread safety
0