About Me
I'm a Computing graduate with a strong focus on creating reliable, user-friendly software. My interests lie in crafting clean, maintainable code and designing experiences that feel natural to use.
I enjoy working on projects that challenge me to learn something new, whether it's exploring emerging technologies or refining the details that make an app truly intuitive. Collaboration is where I thrive most, and sharing ideas and building solutions together keeps me inspired.
My main tech stack includes:
Projects
Selected product-led builds focused on usability, clarity, and engineering quality.
Fridge to Feast
Ingredient-first recipe discovery platform with match scoring and dietary filtering.
Crochets by On-Yee
Full-stack ecommerce rebuild with custom CMS, checkout flow, and Supabase-backed product data.
Other Builds
Other Projects
Campus Habit Hero
Habit-tracking concept for campus students focused on routine, accountability, and goal visibility.
NoSpamPls Discord Bot
Modular moderation and automation bot with slash commands, workflow controls, and scheduled messaging.
A real-time session tracker designed to improve competitive recreational play.
Why I Built It
I play badminton every Sunday and actively work on improving my performance. Tracking improvement was easy in some ways, either by reviewing recordings or by sensing whether I was winning more consistently.
What was missing was structured data.
We had no reliable way to:
- Track scores across multiple games
- Measure win rates over a session
- Ensure fair rotations
- Avoid repeated pairings
- Guarantee equal game time for all players
Using notes quickly became inconsistent and impractical mid-session, so I built Badminton Buddy.
The Problem
Recreational sports groups often rely on manual coordination. With 4-8 players rotating in doubles, pairings become repetitive, court time can become uneven, win tracking becomes inconsistent, and performance insights are lost after the session.
There was no lightweight tool built specifically for this workflow.
The Solution
Badminton Buddy is a web-based session tracker that:
- Generates dynamic player pairings
- Tracks live game scores
- Calculates win rates and performance averages
- Identifies highest-performing pairs
- Ensures balanced participation
- Persists real-time session data with Supabase
It is optimized for mobile usage so it can be used directly courtside without friction.
Technical Stack
- React with functional components and Hooks
- Supabase with PostgreSQL for persistent, authentication-ready backend services
- State management with derived calculations
- Real-time session data persistence
- Custom pairing generation algorithm
- Dynamic stat computation
- Responsive, mobile-first UI
Engineering Decisions
Backend and data storage: I initially considered local storage but transitioned to Supabase to persist player and session history across devices, prepare for shared sessions, enable scalable relational modeling in PostgreSQL, and separate frontend state from long-term storage.
Using Supabase gave me a cleaner data model for:
- Players
- Sessions
- Games
- Pairings
- Match results
This also created a stronger foundation for historical performance tracking, real-time collaboration, and authenticated user accounts.
Custom pairing logic: simple randomization leads to repeated teams, so I implemented pairing constraints to reduce duplication and improve fairness.
Derived stat computation: calculated stats are computed from canonical session data to reduce state complexity and keep metrics consistent.
Mobile-first design: sessions happen in fast-paced environments, so the UI prioritizes large touch targets, quick score input, minimal clutter, and clear stat visibility.
Trade-offs and Improvements
Current limitations:
- Pairing fairness logic can still be improved using more advanced constraint-based algorithms
- Real-time collaboration is planned but not fully implemented
- UI optimizations for larger session groups (8+ players) can be improved
Future improvements:
- Implement Supabase real-time subscriptions for live multiplayer sessions
- Introduce aggregated performance dashboards across sessions
- Optimize pairing logic using weighted fairness scoring
A smart ingredient-based recipe discovery platform.
Why I Built It
Most recipe platforms assume users start with a dish in mind.
In reality, people often start with ingredients.
I wanted to build a system that flips that interaction model: instead of searching for recipes, users select what they already have and the system generates viable meal options.
It became both a UX problem and a matching logic problem.
The Problem
Users frequently:
- Have incomplete ingredient sets
- Do not know what they can make
- Want healthy or culturally specific meals
- Feel overwhelmed by generic recipe search results
Traditional search is keyword-based. What is needed is ingredient-based matching with filtering.
The Challenge
- Handling partial ingredient matches
- Ranking recipes by relevance
- Managing asynchronous API data
- Designing a UI that avoids clutter despite many filters
- Handling edge cases such as empty results, slow API responses, and invalid ingredient combinations
The Solution
Fridge to Feast is a web-based recipe generation platform that:
- Allows users to select ingredients by category
- Integrates with an external recipe API
- Ranks recipes based on ingredient match percentage
- Displays nutritional data and metadata
- Supports filtering for dietary preferences
- Uses loading states and structured UI to maintain clarity
The system prioritizes recipes with higher ingredient overlap, helping users make practical decisions with what they already have.
Technical Stack
- React with component-driven architecture
- External recipe API integration
- Asynchronous data fetching with error handling
- Dynamic ingredient matching logic
- Conditional rendering for filters and result states
- Structured UI layout for categorized ingredient selection
- Optimized loading states for better perceived performance
Key Engineering Decisions
Ingredient categorization: instead of a flat ingredient input field, I structured ingredients into logical groups such as proteins, vegetables, and spices. This improves usability and reduces input ambiguity.
Match scoring logic: rather than simply filtering recipes that include selected ingredients, I implemented a match-percentage system to rank recipes by relevance, prioritize higher overlap, and avoid strict all-or-nothing filtering.
UI architecture: I separated input state (selected ingredients), filter state (dietary constraints), and result state (API responses). This prevents tightly coupled state updates and improves maintainability.
Trade-offs and Improvements
Current limitations:
- Reliant on third-party API performance and rate limits
- Match logic could be enhanced with weighted ingredient scoring
- No persistent user profiles yet
If scaling this product:
- I would cache API results server-side
- Introduce user accounts with saved preferences
- Implement AI-assisted ingredient substitution suggestions
- Add nutritional scoring dashboards
A full-stack e-commerce rebuild for a small creative business.
Overview
Crochets by On Yee is a small independent crochet brand. The original website felt visually outdated, difficult to navigate, and lacked modern e-commerce standards.
The products were strong. The digital experience was not.
I rebuilt the platform from the ground up as a full-stack e-commerce system focused on usability, scalability, and business practicality.
This was my most technically demanding project to date.
The Initial Problem
The previous site suffered from:
- Inconsistent UI hierarchy
- Limited mobile optimisation
- No structured product management system
- Friction in the checkout flow
- Weak trust signals for online payments
For a small business, this directly affects customer confidence and sales conversion.
Client Collaboration and Requirement Gathering
Before writing code, I worked closely with the business owner to understand:
- Their preferred visual direction
- Pain points with the existing site
- Required e-commerce features
- How they wanted to manage products long-term
- Concerns around payment security and order handling
Rather than redesigning based on assumptions, I translated client needs into technical architecture decisions.
Several layout and styling decisions were refined through feedback cycles to ensure the final product aligned with the brand identity.
The Solution
I rebuilt the website as a modern, production-ready e-commerce platform that includes:
- Structured product pages with clear metadata
- Dynamic cart functionality
- Secure Stripe payment integration
- Supabase-powered database storage
- Custom CMS/admin dashboard for product management
- Fully responsive mobile-first design
The result is a scalable storefront that balances aesthetic appeal with commercial reliability.
Technical Architecture
Frontend
- React / Next.js
- Component-based architecture
- Responsive layout system
- Conditional rendering for checkout states
- Cart state management
Backend and data
- Supabase with PostgreSQL
- Product storage
- Order records
- Structured relational data modelling
- Server-side integration for secure operations
Payments
- Stripe integration
- Secure checkout handling
- Payment confirmation logic
- Order recording after successful transactions
Admin and CMS System
I built a custom admin panel that allows the client to:
- Add new products
- Edit pricing and descriptions
- Manage inventory details
- Update product images
This removes the need for developer intervention for everyday business operations.
Key Engineering Decisions
Supabase instead of static data: rather than hardcoding products, I implemented a relational database structure so the platform could scale and handle real order records.
Custom CMS instead of third-party builder: instead of relying on a website builder or external CMS, I built an internal admin dashboard tailored to the client's needs. This ensured flexibility and tighter integration with the product database.
Secure payment flow with Stripe: payments are processed securely through Stripe, with backend validation before recording orders in Supabase. This ensures transactional reliability and data consistency.
Mobile-first UX: small business traffic is predominantly mobile. Layouts were designed with clear visual hierarchy, large touch targets, simplified checkout flow, and minimal friction between browsing and purchase.
Trade-offs and Future Improvements
Current limitations:
- No automated email notifications system yet
- Inventory quantities could be enhanced with real-time stock reduction logic
- Analytics tracking can be expanded
Future enhancements could include:
- Sales dashboards within the admin panel
- Order status management
- Customer account system
- Shipping rate automation by region
NoSpamPls is a configurable Discord bot designed to streamline moderation and automate routine server tasks.
Discord Moderation and Automation Bot
NoSpamPls combines moderation safeguards, structured suggestion workflows, and scheduled announcements within a modular, feature-toggled architecture.
The system is built with scalability and maintainability in mind, using domain-based folder separation and persistent JSON storage.
Core Purpose
- Provide automated moderation tools to reduce manual workload.
- Enable structured community feedback through an approval workflow.
- Deliver scheduled announcements and reminders.
- Allow configuration-driven branding and behavior.
- Maintain clean architectural separation between features.
Slash Command Features
/suggest
- Allows members to submit structured suggestions.
- Posts a formatted embed to the configured suggestions channel.
- Includes interactive Approve and Reject buttons.
- Restricts moderation actions to users with Manage Messages permission.
- Updates suggestion status dynamically.
- Disables buttons after review.
/schedulemessage
- Creates recurring automated announcements.
- Supports predefined intervals: 1 minute, 10 minutes, 30 minutes, hourly, daily.
- Allows duration-based expiry, such as 2 hours or 3 days.
- Targets a selected channel.
- Sends formatted embedded messages.
/listschedules
- Displays active scheduled messages with unique schedule ID, target channel, interval, and expiry time.
/stopschedule
- Stops and removes an active scheduled message using its ID.
Suggestion System
- Stores data in
suggestions.json. - Tracks status as pending, approved, or rejected.
- Button interactions update both the embed and stored data.
- Prevents duplicate or post-approval interaction.
- Designed for structured, moderator-controlled feedback handling.
Scheduled Message System
- Stores recurring tasks in
schedules.json. - Automatically restores valid schedules on bot restart.
- Automatically removes expired schedules.
- Designed for event reminders, server announcements, and rotating informational messages.
Moderation System
Reply and forward detection
- Detects reply-style messages (
message.reference) when moderation is enabled. - Automatically deletes the message.
- Sends a generic warning DM to the user.
- Falls back to channel mention if DM fails.
Message logging
- Logs deleted messages with content, author, and channel details.
- Logs edited messages with before and after content.
- Designed to improve transparency and accountability within server moderation.
Experience
Team Project
Full-Stack Web App
- Designed and shipped a MERN-based web app with React, Node, Express, and MongoDB.
- Led sprint planning, code reviews, and stand-ups for a 5-person team.
- Built CI/CD and automated testing (Jest), cutting manual testing by ~30%.
Education Journey
A visual timeline of my academic progression - from earliest studies to most recent qualifications.
Glasgow Clyde College (Further Education)
Higher National Diploma - Software Development
Glasgow Caledonian University
BSc (Hons) Computing
Contact
Have an opportunity or want to collaborate? Send me a message.