SHADE.OSshaderahman.dev/projects

Built things

Projects

Fewer entries, written properly. Each one says what it does and what was hard.

  • EcoStep

    Home energy upgrades are worth thousands in rebates that almost nobody claims, because working out which ones you qualify for means reading your own appliance labels and utility bills. EcoStep reads both from photos and returns a phased upgrade roadmap. The roadmap includes an EcoScore out of 100 for how clean the home is currently. It also shows the rebates you actually qualify for, with payback calculations based on your own utility rate rather than a national average.

    A model was retired mid-build
    The production Gemini model the vision pipelines ran on was retired while the project was still being built. I replaced it with a 4-model automatic fallback chain and a 9-category error taxonomy across both pipelines (appliance-label OCR and utility-bill parsing), which took single-model outages off the table entirely.
    A rebate is not a number, it is paperwork
    A generic '$350 rebate' tells you nothing about whether you can actually claim it. Each one carries the eligibility rules it depends on, the documents it needs, how long the filing takes, its deadline, and the official source it came from. That gap between a headline figure and a claimable one is the whole reason the money goes unclaimed.
    Renters get a different answer
    Most electrification advice quietly assumes you own the building. The roadmap splits upgrades into what a tenant can act on themselves and what is landlord-controlled, so a renter gets a list of things they can do rather than a list of things they are not allowed to.
    Five engines, six routes
    Rebate matching, affordability simulation, outage-resilience planning, group-buying, and proactive coaching, across 6 REST API routes. Benchmarked at 9,600+ evaluations per second, averaging 0.104ms per call, with 8 of 8 unit tests passing. Rebate matching and affordability drive the app; the other three are built and tested behind the API but not yet surfaced in the UI.
    What was broken
    Three classes of app-wide UI bug: a dark-mode toggle that was silently ignored, 17 invalid Tailwind color tokens, and 17 controls that failed contrast. I fixed all three, added a motion layer across 6 components, and shipped with zero TypeScript errors across 13 routes.
    • Next.js
    • React
    • TypeScript
    • Tailwind CSS
    • Supabase
    • Gemini API
    • Vercel

    Built at BloomKnights 2026. The reliability work was not planned. It was a response to the Gemini model disappearing underneath us partway through, and it turned out to be the most useful thing in the project. Anything depending on one model has a single point of failure that a hackathon timeline will happily expose.

    Built with Abeni Rodriguez, Emma Stefanini

    Source on GitHubWriteup on Devpost

  • Bolo

    Plenty of people read English perfectly well, but are slow at writing it, so an email reply they could think through in a minute costs them half an hour. Bolo lets them work in the language they think in, whether it's through text or speech, and returns a drafted English reply. It is a composition problem, not a comprehension one, which is why it drafts rather than translates.

    A screenshot is context, not just text
    You can hand it a screenshot of the thread you are answering. The OCR is not there to save typing, it is there so the model can see what is actually being asked: the sender's wording, what was already said above it, and the problem the reply has to solve. A draft written against that thread beats one written against a blank prompt, because the hard part of a reply is usually the context, not the sentences.
    The measured result
    Drafting time dropped from 30 minutes to 4 across 50+ generated emails, an 87% reduction.
    Code-switching without losing intent
    People writing in a second language mix English words, phrases, and whole sentences into their native text, and those are usually the parts they are most sure about. Prompt flows that passed 35 generation tests preserve them rather than round-tripping them back out through translation, which is the failure mode that makes a tool feel like it is correcting you.
    What an adversarial QA pass found
    21 verified bugs, fixed, with 126 tests passing afterwards. The fixes were prompt-injection detection, Unicode-aware OCR cleanup, runtime API validation, stale async guards, and an empty-response fallback.
    • Next.js
    • React
    • TypeScript
    • Tailwind CSS
    • Tesseract.js
    • Gemini API
    • OpenAI API
    • Vercel

    The OCR path was the fiddly one. Screenshots of emails arrive at every possible resolution and Tesseract will confidently return Unicode garbage for a low-quality crop, so a cleanup pass sits between it and the model.

    Try BoloSource on GitHub

  • Cooked!

    Recipe sites want you to already have the ingredients. Cooked! goes the other way: type in what is actually in your fridge and it ranks recipes by match percentage, showing what you are short of and how much more of it you need, with the instructions and a video for each one. The point is cooking from what you have in your pantry instead of shopping for a recipe you picked first.

    Ranking, not just matching
    A tiered ingredient-matching algorithm with coverage-based scoring ranks 12+ recipes per query, rather than dumping back everything that happens to contain one ingredient you own. A recipe you can make nine tenths of is a different answer from one you can make a third of, and a flat match cannot tell you which is which.
    Cutting redundant calls
    In-memory caching and API error handling reduced redundant external API calls by 73.6% across overlapping query benchmarks.
    People type messily
    Nobody enters ingredients consistently. '2 cups', '2c', and 'two cups' all show up. Real-time validation, unit normalization, and unit suggestions resolve that before anything reaches the matcher.
    The recipe data is messy too
    TheMealDB is the source, and it is incomplete and inconsistent: missing fields, ambiguous units, the same ingredient written several ways. The matcher has to degrade around all of that and still return a ranking that means something, which is most of the reason the scoring is tiered rather than one string comparison.
    • Python
    • Flask
    • Next.js
    • TypeScript
    • REST APIs
    • TheMealDB API
    • Gemini API

    Built at HackUSF 2026 with Natalie Reese. I worked mainly on the backend: the Flask service, the matching and scoring, the caching, and the input normalization. I also did the wiring between the frontend and backend, and the UI/UX. Natalie built the frontend UI and handled integration and deployment.

    Built with Natalie Reese

    Source on GitHubWriteup on Devpost

  • annabelle.sfx

    A birthday present for my best friend. A mobile-first soundboard styled after a Stream Deck with sixteen tactile keys in a 4×4 grid, one sound each, no build step and no dependencies.

    No framework, deliberately
    Vanilla HTML, CSS, and JavaScript. Nothing to install, nothing to build. It registers a service worker and installs as a PWA, so it keeps working with no connection at all, which matters for something meant to be opened on a phone at a party.
    iOS is the hard part
    Safari suspends the Web Audio context until a real user gesture unlocks it, then suspends it again when the page is backgrounded. app.js carries a dedicated recovery path for that, and the repo ships a separate diagnostics page for testing on the phone itself rather than in a desktop simulator.
    Swappable without touching code
    The sixteen keys are built from a manifest.json that maps files to slots, with an optional colour shade per key. Changing the whole sound set means editing one JSON file and reloading.
    • JavaScript
    • HTML
    • CSS
    • Web Audio API
    • PWA

    Not a serious project, and I am keeping it here anyway. It is the only thing on this page built for exactly one person.

    Try annabelle.sfxSource on GitHub

  • Network Connectivity Analyzer

    Given a network and a list of connections that fail one after another, how connected does the network stay at each step? This answers that question in O(log n) average time per operation.

    Running the problem backwards
    Union-Find only merges. It has no way to split a set, so sequential edge removal is the one thing it cannot do directly. Reverse-deletion sidesteps that: replay the removals backwards as insertions, rebuilding the graph bottom-up and tracking size-weighted component merges at each step to recover the connectivity score at every stage.
    Where the log n comes from
    Union-Find with both path compression and union by rank. Either alone is worse; together they give the O(log n) average.
    • Java
    • Algorithms

    Coursework, and the first time an algorithm felt like a genuine trick rather than a procedure to memorise. The insight that you can run a destruction problem backwards as a construction problem is not obvious until someone shows you.

    Source in my coursework repo

  • Gaming CPU Project

    A team build of a RISC-V CPU system-on-chip that runs video games on an FPGA, done through IEEE at UCF. Hardware is the half of computer engineering that a software portfolio usually leaves out.

    The design
    A 5-stage pipeline with VM/0 fetch, built as a team through the university's IEEE chapter.
    My part
    Coding, debugging, synthesis, and implementing hardware modules: the ordinary work of getting RTL to behave on real hardware rather than only in simulation.
    • Verilog
    • FPGA
    • RISC-V
    • RTL Design

    Ran from September to December 2025. Not an active project any more, but it is the reason the hardware side of the degree stopped being abstract.

    IEEE at UCF

  • Card Game Simulator

    A card game simulator in C that reads a deck from a plain text file, deals it out, and tracks every player's hand. Written while learning what linked lists are actually for.

    Where the work was
    Sorted insertion, pile reversal, and transfers between the draw, player, and discard piles, all on hand-rolled linked lists, with dynamic memory allocation and file I/O underneath.
    • C