Six projects that show the most range: a safety-critical health app, a client HR system, RAG, row-level security, a Laravel PWA and a reconstructed game.

BodyFit Filter landing page: the headline 'See what you can do with your body and your equipment' beside a 3D figure seated on a chair mid-exercise

BodyFit Filter: Safety-First Home-Exercise PWA

Laravel 12 · Livewire · Tailwind · Three.js · SQLite · PWA · Sentry

  • Built a server-side eligibility engine that filters a 95-exercise library to a person's equipment and stated limitations. It leads with exclusion reasons, so every hidden exercise says why, and a property test pins it. The workout generator re-checks every pick through the same engine.
  • Built an audited safety-rule workflow: state changes are logged, editing a rule resets it to draft, and a publish guard is the only route to users. Caution rules were researched from NHS-trust patient leaflets, with every quotation machine-checked against its source text.
  • Made the exercise demos as 3D clips on a procedural Three.js rig (IK-baked keyframes) and shipped them in an offline PWA with a workout player, a dynamic service worker, and a page cache cleared on session change.
  • Hardened for launch: nonce CSP, auth hardening, zero-PII Sentry, a nightly SQLite backup without exec(), and 295 PHP and 14 JS tests. It is informational, with a visible disclaimer, not medical advice.
HR and recruitment system overview page with four summary counts and two lists of recent applications, each with a coloured status pill (demo data)

HR & Recruitment System

Django 5.2 · Django REST Framework · Vue 3 · TypeScript · Pinia · Tailwind 4 · PostgreSQL · Docker Compose · Nginx

  • Built a recruitment and people system for a healthcare cooperative federation: a Django REST API (nine apps) and a Vue 3 + TypeScript single-page app with URL-synced list filters, one fetch wrapper that handles CSRF and normalizes errors, and a documented design system.
  • Modeled the hiring flow end to end: jobs with per-job screening questions, a public application form with content-sniffed CV upload and optional ClamAV scanning, an eight-status pipeline with history, interviews with feedback, and offers with an approval step.
  • Extended it past recruitment into employee records and self-service: a reusable approval engine whose flows are stored as data, attendance clock-in with a live selfie and a server-side office geofence decision, token login for a future native app, and manpower requisitions with per-position HR Manager approval.
  • Built security in from the start: TOTP two-step sign-in written against RFC 6238, role- and object-level permissions with negative tests on every endpoint, HR-admin-only compensation fields, an append-only audit log, and consent and retention records.
  • Packaged for production with Docker Compose, Nginx, Gunicorn and Let's Encrypt, plus nightly backups with a tested restore procedure. The suite stands at 652 backend, 77 frontend and 14 end-to-end tests.

Built for a client: internal system, no public link. Screenshot shows demo data only

DOCUMENT·RAG public chat: a document Q&A assistant with four example questions and the line 'every claim links to the passage behind it'

DOCUMENT·RAG

React · Vite · Supabase (Postgres + pgvector, Auth, Storage) · Deno Edge Functions · Gemini · Tailwind v4

  • Built a reusable single-tenant RAG app: a React and Vite front end on a Supabase backend (Postgres with pgvector, Auth, private Storage, two Deno Edge Functions), with in-function embeddings, a swappable LLM provider, client-side PDF text extraction, salted per-IP rate limiting, and Row Level Security on every table (the vector table has no client access). Each client gets its own isolated deployment.
  • Deployed it to production: a dedicated Postgres project, the migration set, Row Level Security checked on every table against the live database, rate-limiter and hash secrets, edge functions, a seeded admin account, and the static bundle on a CDN host. Then smoke-tested ingest, cited answers and the audit log on the live stack.
  • Fixed two deployment failures a green test suite couldn't catch: a retired model identifier that returned 404 only for newly issued API keys, and a single-page-app fallback rule the host ignored, which sent every hard refresh of a client-side route to the host's 404 page.
  • Linked every inline citation to its source passage: hover a claim and the source expands, hover a source and its claims highlight. The front end uses Tailwind v4 with copied component primitives.
View live →
Prompt Library landing page showing the dark quire-themed hero and how-it-works section

Prompt Library

Next.js 16 (App Router) · Supabase (Postgres/Auth/RLS) · Vitest

  • Built a multi-user prompt manager with three visibility levels: private, a revocable share link, and a public gallery at /explore that needs no account. Postgres Row Level Security enforces access, with no application-layer checks.
  • Found a live privilege-escalation bug in a post-launch security audit: a forged share link could give permanent read access to any user's private prompt from its UUID. Fixed with ACL, RLS and function-level changes plus a trigger that revokes access when a link is revoked, and checked against the production database before and after.
  • Deployed to Vercel and Supabase Cloud with CI (lint, typecheck, tests, dependency audit) on every push, which caught a route-typegen ordering bug that only appeared on a fresh checkout.
View live →
Selah landing page in a warm illuminated-Psalter visual system: a wide serif wordmark between two gilt rules, the day's Scripture verse under a hanging gold pilcrow

Selah: Daily Prayer PWA

Laravel 12 · PHP 8.2 · Blade · Tailwind v4 · Alpine.js · MySQL · Pest

  • Shipped a Laravel 12 app to shared hosting at no added infrastructure cost, the first framework app on the account: app outside the webroot via a symlink, compiled assets committed (the host has no Node), a repository deploy key for pull-based releases, and a server-side release script. It includes server-rendered auth with email verification on MySQL and an admin CRUD area.
  • Designed a leap-safe 365-day content cycle with cached per-day selection and wrote the full 365-entry devotional set, one public-domain Scripture verse per day, all imported from a Bible API without a failure.
  • Fixed a production email-verification 403 caused by an untrusted TLS-terminating proxy: signed URLs were generated over HTTPS but validated as HTTP. The same bug had silently broken password-reset links.
  • Ran a pre-launch audit: CSP and HSTS in middleware and mirrored in .htaccess to beat a weak host-injected policy, a per-agent AI-crawler allow/deny list, route rate limiting, a 404 for inactive records, noindex on the logged-in shell, and a generated Open Graph image.
  • Designed an "illuminated Psalter" visual system with six palettes, each a day and night pair, from one runtime CSS custom-property token set and a pre-paint script that prevents a flash. The choice is saved server-side and in a deliberately unencrypted cookie read before first paint.
Ambar: War of the Elementals landing page: a black-and-gold hero reading 'War of the Elementals' above Found your realm and Return to Ambar buttons, with five elemental-race cards below

Ambar: War of the Elementals

Laravel 12 · PHP 8.2 · Blade · Tailwind · Livewire 4 · SQLite · Pest · three.js

  • Reconstructed a lost 2000s tick-based browser game as documented game archaeology: every mechanic traces to a recovered source (Wayback guide pages, forum threads, leaderboard snapshots) or is tagged as a design-fresh gap-fill with its confidence level.
  • Closed an IDOR across four attack surfaces (combat, spellcasting, thieving, portal management): a client-editable target ID was trusted with no server-side reachability check, so a crafted request could hit a foreign, protected or dead realm. One shared authorization guard now sits at every untrusted entry point.
  • Found a production bug a static audit couldn't: the security-header policy was correct in the app but truncated by the host's edge layer. Curling the live response caught it, and the policy is now mirrored in the web-server config.
  • Fixed a balance bug from a player report: new-account protection was a flat duration regardless of game-mode length, so a 7-day mode gave every player a risk-free run. The window now derives from the mode's duration.

More projects

Client systems, tools, experiments and games.

Personal portfolio site homepage, dark hero section

Personal Portfolio Site

Vite 6 · Three.js · GSAP + Lenis

  • Built two framework-free Vite static sites, each with a custom Three.js hero scene and a shared GSAP and Lenis animation pattern.
  • Traced a silent CI/CD deploy failure on both pipelines to a CDN-fronted hostname and fixed it.
  • Gave each site its own path-filtered GitHub Actions pipeline, so a change to one site never rebuilds the other.
View live →
Shoshin Systems agency site homepage

Shoshin Systems: Agency Site

Vite 6 · Three.js · GSAP + Lenis

  • The second site in the same path-filtered monorepo, on its own domain and sharing no runtime dependencies with the portfolio.
  • Wrote a runbook that keeps every secret out of source control while documenting the deployment setup well enough to rebuild it.
  • Added a privacy-policy page through the existing pipeline with no pipeline changes.
View live →
Health care system cash receipts list with a dark sidebar of module folders and a table of receipts with status pills (demo data)

CHMF Healthcare & Accounting System

Python · Django · PostgreSQL · QODBC

  • Architected an in-house healthcare and accounting system for a medical cooperative federation, with a service layer that keeps business logic out of controllers and models.
  • Implemented double-entry accounting with atomic transactions, debit/credit validation, and a Draft to Approved state machine with idempotency keys.
  • Connected a legacy 32-bit QuickBooks Desktop to Python through the QODBC driver and built a Chart-of-Accounts sync.

Internal enterprise system, no public link. Screenshot shows demo data only

Admin panel orders table with scope tabs for today, unpaid and paid, a filter sidebar, and rows showing branch, customer, total and payment amounts (demo data)

Republika Steel: Inventory & Admin Panel

Ruby on Rails 7 · ActiveAdmin · Devise · PostgreSQL · RSpec · Render

  • Built an order, inventory and payment tracker for a multi-branch hardware business: customer orders with line items, incoming supplier stock, and cheque, cash and online payments, with sales and inventory reports by branch, month and year.
  • Gave each staff role (admin, order, payment, print and others) its own admin view through menu, scope, column and form-field rules.
  • Tuned the report pages by removing N+1 queries and Ruby-side aggregation from the yearly and monthly sales reports, then added view-rendering tests for every admin resource and RuboCop in CI.
  • Deployed to Render with a health-check endpoint, then added Sentry error reporting and an uptime check, and fixed daily production crashes by matching Puma's worker count to the 512 MB instance.

Private client system, no public link. Screenshot shows demo data only

SmartResume AI Builder dashboard

SmartResume: Resume Builder

FastAPI · Next.js 16 · React 19 · TypeScript

  • Built a resume builder around a personal knowledge base (profile, experience, education, skills, projects, certifications). It scrapes job URLs, scores fit, and writes a tailored resume and cover letter.
  • Replaced a shared-secret login with per-user accounts: password hashing, database-backed sessions, and encrypted per-user API-key storage across every table and router.
  • Fixed a concurrency bug where AI provider credentials came from a process-wide environment variable shared by simultaneous users.
View live →
SkillStack certification tracker dashboard

SkillStack Certification Tracker

Next.js 16 · React 19 · TypeScript · Recharts

  • Built a certification-tracking dashboard with no backend: browser localStorage is the only storage, so there is no server to host or pay for.
  • Wrote a weighted ROI score that ranks certifications on four independent axes and drives sorting, filtering and a Top 5 recommendation widget.
  • Added a byte-accurate storage guard so browser quota failures can't happen silently.
View live →
Claude Usage Widget: a title card with a gauge motif; a desktop system-tray tool with no web UI

Claude Usage Widget

Python 3 · PySide6 (Qt6) · keyring · pytest

  • Re-architected a Windows-only desktop app into a cross-platform Qt client, replacing OS-specific credential storage and autostart with APIs that work on Windows, macOS and Linux.
  • Ran a review-gated process that found and fixed a dozen-plus concurrency defects, including fatal thread-destruction crashes and a spoofable cookie-domain check.
  • Wrote a 76-test suite for a GUI app that had none, covering threading and state changes headlessly with no display.

Built, cross-platform release candidate

Content Guard Chrome extension blocking domains

Content Guard

Chrome Extension · Manifest V3 · declarativeNetRequest

  • Built a Manifest V3 extension that blocks content at the network level with declarativeNetRequest, ahead of Chrome's MV2 sunset.
  • Built a rule-sync system that scales user-managed blocklists from 2 entries to 500 enforced domains.
  • Audited the store listing and default settings against the platform's disclosure policy before submission and fixed a metadata transparency gap.
View on Chrome Web Store →
Shoshin Tools dark-mode homepage showing developer tool cards

Shoshin Tools

Eleventy (11ty) · Vanilla JS · pdf-lib · fflate

  • Shipped 70 free utilities and calculators on one Eleventy static site with no framework: a JSON formatter, JWT decoder, five PDF tools, an in-browser OCR tool, a 55-country regional-pricing calculator, and mortgage, tax and investing calculators. Everything runs in the browser, with no accounts or uploads.
  • Added light/dark theming across all 70 pages: a CSS custom-property token layer, a CSP-compliant external script that prevents a flash of the wrong theme, and a unit-tested theme-resolution module.
  • Found and fixed a DOM-based XSS (unescaped error messages through innerHTML) in 9 tools, then closed title, meta-description and structured-data gaps across the site.
View live →
Usage Meter for Claude.ai Chrome Web Store listing page

Usage Meter for Claude.ai

Chrome Extension · Manifest V3 · Playwright

  • Built a Chrome extension that shows 7-day claude.ai session and weekly usage caps in the toolbar, with no API key or sign-up.
  • Tested it in a real Chrome instance with a Playwright script that mocks claude.ai's endpoints and asserts the actual badge text and color, so the check runs through the real extension code.
  • Prepared it for the Chrome Web Store: a trademark-safe rebrand, a dedicated privacy-policy page and generated icons.
View on Chrome Web Store →
Design Systems gallery homepage showing a grid of fictional-business design system cards

Design Systems

Eleventy (11ty) · GSAP/ScrollTrigger · Three.js

  • Built a gallery of 58 fictional-business design systems. Each has its own brand, a color system with computed WCAG contrast, an inline-SVG logo drawn from scratch, and a scroll-driven interaction tied to that brand's real-world process.
  • Adopted a third-party design-audit tool after the gallery was flagged as generic, and ran the existing systems through it instead of checking only new ones.
  • Fixed two recurring scroll-animation bugs (one that depended on viewport height, one where SVG and JS transforms collided) and backported both fixes to earlier systems.
View live →
Remotive MCP Server: a title card with a client-to-server connector motif; an MCP server with no web UI

Remotive MCP Server

Python · MCP Python SDK · httpx · Pydantic

  • Built and published an MCP server that exposes Remotive's public job board as two tools, keyword/category search and job-detail lookup, for Claude Desktop or Claude Code with no API key.
  • Found that Remotive's CDN cached by URL path and ignored query-string filters, confirmed it from the response cache headers, and worked around it by filtering the full listing client-side.
  • Verified it with a real stdio MCP session and a 10-question evaluation set with hand-checked answers against the live API.
View on GitHub →
Cloudflare Uptime Monitor status page on a dark background: a table of 30 monitored sites, every row marked UP with 24 hour, 7 day and 30 day uptime percentages

Cloudflare Uptime Monitor

Cloudflare Workers · Hono · D1 · Cron Triggers · Resend

  • Built a self-hosted uptime monitor on Cloudflare's edge: a Worker on a 5-minute Cron Trigger checks sites and writes to D1, and the same Worker serves a server-rendered status page, with no separate frontend build.
  • Reviewed after every task and again across the whole branch. That caught two bugs no single task review could see: a missing error state when D1 fails, and health checks that didn't bypass Cloudflare's edge cache and could have reported a down site as up.
  • Deployed end to end (D1, migrations, Cron Trigger) with 49 tests against local D1 emulation instead of mocks, and a TypeScript typecheck on every change.
  • Now watches all 30 of my live sites. It found a gap in itself: when a domain's routing broke, Hostinger served its "parked domain" page with an HTTP 200, so five of my sites looked 100% healthy while the public saw nothing.
  • Fixed it with a page-content check and email alerts through Resend: a site must fail two checks in a row, one digest email goes out per run, and a recovery notice follows a confirmed outage. Batched the D1 writes so 30 sites fit the free-plan limits, and tested the alert end to end with a deliberately unresolvable site.
View live →
PokéRAG AI Gradio app: an ask box beside an answer panel and a retrieved-context panel, so you can see exactly what the model was shown

PokéRAG AI

Qdrant · LangChain · RAGAS · MCP · LangGraph · CrewAI · Gradio

  • Built a RAG-to-agents pipeline in 8 separately verified stages: Qdrant vector search, LangChain RAG, RAGAS evaluation, MCP tool calling, LangGraph workflows, CrewAI multi-agent orchestration, and deployment.
  • A 20-question RAGAS run exposed a real weakness: structured facts were never embedded, and that question category scored worst. Adding a Postgres store closed the gap, re-checked against the same harness.
  • Shipped a LangGraph workflow with a human-in-the-loop interrupt (pause, wait for approval, resume from saved state) and a 6-agent CrewAI crew whose output a dedicated fact-checking agent checked for hallucinations.
  • Traced a Hugging Face ZeroGPU failure to a large model object crossing a subprocess boundary as a function argument, using the platform's own error trace.
PoopLog sign-in screen in a stencil-dyed-cloth visual system, celadon dye on a rice-cream ground

PoopLog

Vite · React 19 · TypeScript · Supabase (Postgres + RLS) · vite-plugin-pwa

  • Shipped an installable offline PWA as a static bundle on shared hosting with no Node runtime, backed by managed Postgres, with production auth set up end to end (custom SMTP, redirect URLs, separate local and production environments).
  • Built the whole authorization model in Postgres Row Level Security plus one SECURITY DEFINER function with anonymous execute revoked: owner-only records, a revocable read-only share, and an invite, claim and owner-approve handshake. No permission checks in the application layer.
  • Tested every access boundary against the live production database with self-cleaning scripts, including a cross-tenant write that correctly returned a 403.
  • Ran a pre-launch audit covering security, web readiness and search hygiene: moved from a single CSP directive to a locked-down policy, added a React error boundary and real 404s, and bounded text columns to close a storage-exhaustion risk on the shared tier.
  • Designed a visual system on katazome (Japanese stencil-resist dyeing) across every screen: one authored motif, a single dye color owning whole regions, and a signature interaction that respects reduced motion.
View live →
BridgeView townhouse listing site hero showing the price, comparable-value discount, and units remaining

BridgeView

Eleventy · GSAP & ScrollTrigger · Web3Forms

  • Built a marketing and lead-capture site for a ₱55M preselling townhouse, from spec through an 11-task implementation with a reviewer per task and a final whole-branch review.
  • Rebuilt the visual system after a design rejection into a bespoke blueprint-style "Cyanotype Plot" language, then added GSAP parallax, count-up figures and scroll reveals.
  • Ran three self-written audit skills (security, SEO/GEO/AEO, web readiness) on the live build and shipped the fixes: a build-time CSP script hash matched to the rendered JSON-LD, a spam honeypot, an AI-crawler allowlist, and FAQPage schema.
  • Traced a misleading CORS error from a third-party API to its real cause, an email-delivery gap on the server side, by testing from localhost, the live domain and a direct API call.
View live →
Design Systems (React) gallery homepage showing a filterable grid of fictional-business design system cards

Design Systems (React)

Next.js 15 (static export) · React 19 · Tailwind v4 · shadcn/ui + Base UI · TypeScript

  • Built a second design-system gallery on Next.js 15 (static export), shadcn/ui and three effect libraries, with a strict four-tier component policy: every brand renders from one shared primitive layer re-themed through scoped CSS custom properties, and effect libraries are confined to one file, checked in the exported build.
  • Built it to sit beside the hand-authored HTML gallery, showing the same design output from two different implementations.
  • Ran the build as a 12-task plan and closed it with a whole-branch code review, browser visual QA on desktop and mobile, and one consolidated fix pass. That caught a dead theming hook, a prefers-reduced-motion timing bug, a single-source-of-truth violation and an invisible focus indicator before merge.
  • Fixed a Tailwind v4 @theme inline gotcha where a CSS custom-property override compiled to a dead value, and a Base UI button-versus-anchor semantics warning.
View live →
Salapi landing page: a clean blue-and-white hero reading 'Know where your money went' above a single Log in button

Salapi: Personal Finance PWA

Laravel 11 · PHP 8.2 · Blade · Tailwind · Alpine.js · Livewire 3 · SQLite · Pest

  • Built a 30-theme runtime theming system on CSS custom properties: one token set drives every component, and themes switch without a page reload.
  • Changed the app from a single-user tool to public self-registration, putting every Account, Category, Transaction and Budget behind per-user Policy authorization instead of a single ownership check.
  • Found and fixed an IDOR: QuickAdd and BudgetManager accepted a client-supplied category ID validated only with exists:categories,id, so a forged ID leaked another user's private category name onto the attacker's dashboard. Closed with policy authorization and mass-assignment hardening (user_id removed from $fillable, relationship-based creates).
  • Deployed to Hostinger shared hosting on SQLite instead of MySQL, on purpose: MySQL there is only provisioned through hPanel, with no CLI or API. It uses the same git and SSH deploy as YOKAI.
YOKAI: Night Parade landing page: a moonlit shrine and torii gate scene behind a pixel-font 'YOKAI NIGHT PARADE' wordmark over a 'Begin the Parade' button

YOKAI: Night Parade

Laravel 12 · PHP 8.2 · Blade · Tailwind · Alpine.js · Livewire 3 · SQLite · Pest

  • Built a seeded, deterministic battle simulator and two analysis tools to tune balance from data instead of feel: game:sweep runs seeded nights across a level and shrine-level grid and flags "no wall", "pushover" and "spike" cases, and game:progress runs Monte-Carlo player trajectories under different spend policies to report median, p10 and p90 nights to the boss.
  • Found and closed a critical security hole: two Livewire components credited in-game currency from a client-editable public property, and a test using Livewire::test(...)->set('state.rewards.gold', 999999999)->call('claim') credited the forged amount. Fixed by recomputing rewards inside a database transaction from server-only session state.
  • Ran an HD-2D presentation pass (Octopath and Sea of Stars inspired) on an AI-generated, CV-recovered pixel-art pipeline: style-anchored generation, a grid-based pixel recovery tool, and hand-conformed sprites, replacing a mixed-source asset set with one roster.
  • Ships as an installable PWA with a full day/night idle-RPG loop (shrine upgrades, auto-battle, boss), live with GA4 behind a Google Consent Mode v2 banner.
Rapture Timeline landing page: a dark page with a lit doorway under the oversized word TODAY and the line 'You don't know the day. Don't postpone today.'

Rapture Timeline

Laravel 12 · Blade · Livewire 4 · Tailwind 4 · SQLite

  • Built a sourced reference of predicted rapture and end-times dates (what was predicted, why people believed it, what happened) with an admin editor where every entry has an editorial-review state. Unreviewed entries show as "Pending" instead of being presented as fact.
  • Solved a per-request nonce CSP on a shared host that overwrites the CSP header: the same policy goes out as a header and as a <meta http-equiv> tag the host can't touch, and a test decodes the HTML entities to prove they match.
  • Shipped it to shared hosting in a day: a read-only repo-scoped deploy key, git-over-SSH deploys, a security audit, 45 tests, SEO and structured data, and GA4 behind Google Consent Mode v2.
Bible Quest home screen: a dark navy and gold dashboard with a Continue your quest card for Genesis chapter 1, a level and rank panel and daily quests

Bible Quest

React · TypeScript · Vite · Dexie (IndexedDB) · Tailwind · Playwright · PWA

  • Took over an unfinished Bible-reading app from an interrupted AI session that left no docs or commits, audited it honestly, and turned it into a working offline-first reading app with all 66 books, 1,189 chapters and 31,098 verses bundled.
  • Gamified the reading with levels, daily quests and honors, framed as milestones rather than a measure of faith. Progress stays on the device in IndexedDB, with migration tests guarding the schema.
  • Added 79 unit tests and Playwright end-to-end tests for the offline journey, accessibility (axe), keyboard use, CSP and consent.
  • Shipped with a strict CSP, a privacy page and consent-gated analytics that never receive notes, verses or reading position, then checked headers, deep links and offline use against the live site.

Scripture text: World English Bible, public domain

Bubu & Dudu: Our Little Adventures title screen: a white panda and a brown bear standing on a flower-covered floating island under a moonlit sky

Bubu & Dudu: Our Little Adventures

Three.js · matter-js · Vite · PWA · Vitest · Playwright · Sentry

  • Rebuilt a 2D Phaser game in 3D with Three.js and matter-js: a rope-tied co-op island run, a hub world, and a costume wardrobe with 29 character-pair outfits, all drawn procedurally with no downloaded 3D models.
  • Built a reusable costume kit (hair, hoods, props, patterns, held-item pose clamps) so each outfit is a small data set, and cached built costumes so restarting in an outfit takes about 25 ms.
  • Shipped it as an offline-first PWA, tested against a local server that emulates the host's headers (strict CSP, HSTS, service-worker updates, a fallback page when WebGL is missing), with 728 unit tests and Playwright end-to-end tests.
  • Added costume powers and new levels: a Super Dudu power-up, bounce mushrooms with a hidden room, balloons, rope-swing vines, ice physics, and pressure-pad gates, taking the island run from 3 to 6 levels and the rope-tied co-op mode from 5 to 7.
  • Made it public with GA4 behind Consent Mode v2 (nothing loads until Accept), an About and credits screen with a fan-project disclaimer and takedown contact, and zero-PII Sentry.

Unofficial, non-commercial fan project

Bubu & Dudu: Chase menu: the pink 'Chase!' logo with Play, Endless, Daily, Levels, Outfits, Settings and About buttons over a 3D apartment hallway with a white panda and a brown bear

Bubu & Dudu: Chase

Three.js · Vite · PWA · Vitest · Playwright · Sentry

  • Built a three-lane 3D runner with six levels (apartment to beach), a catch-and-hug finale, an endless mode, and a daily challenge seeded from the date so every player gets the same run.
  • Kept game rules free of rendering code so they are unit-tested alone (169 tests), with a Playwright smoke test that fails on any console error or unexpected external request.
  • Wrote the precaching service worker by hand as a Vite plugin so the whole game plays offline, worked around the host's Vary: Origin header, and vendored 28 costume pairs with a wardrobe.
  • Delivers its CSP as a header and as a build-injected meta tag, so it holds on a host that drops headers. GA4 is opt-in behind a consent banner.

Unofficial, non-commercial fan project

Bubu & Dudu: Flappy Flight title screen inside a phone-shaped frame: the pink 'Flappy Flight' logo above a white panda and a brown bear, both with angel wings and halos, and a Let's fly button

Bubu & Dudu: Flappy Flight

Three.js · Vite · PWA · Vitest · Playwright · Sentry

  • Built a one-button sky race through cloud gates with a fixed-timestep physics loop and seeded courses, so a given seed always produces the same run.
  • Gave both characters feathered angel wings, each wing one merged mesh, with a flap stroke driven by the flap input.
  • Shipped with a strict CSP, an offline service worker, GA4 behind Consent Mode v2 and zero-PII Sentry. I audited it with my web-readiness and security checklists first and fixed the inline-style and zoom findings.

Unofficial, non-commercial fan project

Bubu & Dudu: Wobbly Tower title screen: a card with the title, a Play button and Aim, Drop and Stack instructions over a pastel playroom with a white panda and a brown bear on stools

Bubu & Dudu: Wobbly Tower

Three.js · Rapier · Vite · PWA · Vitest · Playwright · Sentry

  • Built a pastel 3D stacking game on Rapier physics: aim, drop, build a combo up to x5, and win by stacking a 38-piece tower.
  • Found that Rapier embeds its WebAssembly as base64 and that compiling it needs 'wasm-unsafe-eval' in the CSP, then added the directive and lazy-loaded the physics so the first load dropped from 5.0 MB to 684 kB.
  • Delivered offline as a PWA with consent-gated GA4 and zero-PII Sentry.

Unofficial, non-commercial fan project

Bubu & Dudu: Hide & Seek title card over a top-down view of a cozy house, with a Play button and the keyboard controls

Bubu & Dudu: Hide & Seek

Three.js · Vite · PWA · Vitest · Playwright · Sentry

  • Built a cozy 3D stealth game: you hide from a seeker across a six-room house with nine hiding spots, a 90-second timer and a distraction toy, with a touch joystick for phones.
  • Wrote the seeker as a six-state AI with A* navigation, line of sight and hearing, and rising difficulty, keeping game logic free of rendering code so it is tested headlessly (33 tests plus a 52-check browser smoke run).
  • Shipped with a strict CSP, offline PWA, consent-gated GA4 and zero-PII Sentry.

Unofficial, non-commercial fan project

Bubu & Dudu: Galaxy Dash 3D title screen: a white panda and a brown bear on a stone path beside a floating island, with Play, Characters and How to play buttons and three level cards

Bubu & Dudu: Galaxy Dash 3D

Three.js · Vite · PWA · Vitest · Playwright · Sentry

  • Built a three-lane 3D auto-runner with three authored levels (island run, bubble-ship flight, finish run), collectible stars, crystals and hearts, and a choice of Bubu or Dudu.
  • Wrote the levels as deterministic data with fairness tests, so every obstacle layout can be checked as passable without playing it, plus a Playwright smoke flow.
  • Shipped it with the same hardening as my other games: a strict CSP delivered as a header and a build-injected meta tag, a hand-written offline service worker, GA4 behind Consent Mode v2, and zero-PII Sentry.
  • Includes an About screen with a fan-project disclaimer and takedown contact, a 404 page, and security.txt.

Unofficial, non-commercial fan project

Bubu & Dudu: Kart Chaos 3D title screen: a white panda driving a light-blue kart on a pastel track, with Race, Racers, Garage, Circuits and How to play buttons

Bubu & Dudu: Kart Chaos 3D

Three.js · Vite · PWA · Vitest · Playwright · Sentry

  • Took over an unfinished 3D kart racer and grew it to four themed tracks, eight power-ups and three AI rivals, on custom arcade vehicle physics written for the game rather than an engine.
  • Added a lap and checkpoint check so cutting a corner doesn't count, and built the tracks from data with geometry tests.
  • Found problems by measuring: a GPU memory leak, a mirrored-steering bug, and clouds covering the road. Profiled draw calls per category and cut racer cost with distance LOD (about 655 to 554 calls on one track).
  • Ran a web-readiness audit before launch, then shipped with a strict CSP, offline PWA, consent-gated GA4 and zero-PII Sentry, checked in a real browser against the live headers.
  • Wrote the soundtrack as code: 20 original Eurobeat-style songs synthesised live in the browser with no audio files, one picked per race from a shuffle bag so none repeats until all have played, plus a dev-only page to audition them.
  • Reproduced and fixed a bug where falling off the track and respawning counted the same distance twice, so the position display could show first place while last. The regression tests were written first.

Unofficial, non-commercial fan project

Bubu & Dudu: Pawcalypse title screen: the game title over a grassy hill at dusk with two bears, a picnic log card showing best time, best kills, runs and bosses, and a Start the Pawcalypse button

Bubu & Dudu: Pawcalypse

Vue 3 · TypeScript · Three.js · Vite · PWA · Vitest · Sentry

  • Built a cozy 3D survivors-like that runs in the browser: pick a bear, survive five-minute runs, collect upgrades and face a boss. The bears and the world are generated in code, with no downloaded art.
  • Ran web-readiness, security and SEO audits before launch. The security pass found no eval or dynamic code, and the production dependency audit came back clean.
  • Shipped with a strict Content Security Policy that does not allow inline style attributes, consent-gated GA4, zero-PII Sentry and an offline PWA, checked in a real browser against the live headers.
  • Went from first commit to live in about a day by reusing the deploy pipeline from the earlier games, with 37 unit tests at launch and a local production-headers check before every deploy.

Unofficial, non-commercial fan project

Also built

Finished builds that are not deployed or are shelved for now.

TCG Arbitrage Explorer dashboard

TCG Arbitrage Explorer

Next.js 16 · TypeScript · Prisma 7 + PostgreSQL · Playwright · Cheerio

  • Designed a three-tier price-scraping fallback (headless browser, then static HTML parse, then cached snapshot) with heuristic DOM scoring, so the dashboard stays populated for a site with no public API.
  • Modeled weighted arbitrage scoring in Prisma and PostgreSQL, with one aggregated dashboard endpoint returning leaderboards and top movers across hundreds of tracked card pairs.
  • Added a node-cron sync that refreshes only stale records, which caps scrape load.

Built, not deployed. v2 rebuilds the same concept on Prisma, PostgreSQL and a scheduled sync

Only10 trivia video generator configuration screen

Only10 (ReelsCreator)

TypeScript · Node.js · Express · Puppeteer · FFmpeg

  • Built a pipeline that turns structured JSON into rendered 1080x1920 MP4 reels with no outside rendering service.
  • Made video capture frame-accurate by pausing headless Chrome and stepping CSS Web Animations time by hand.
  • Rendered 2,016 videos with it.

Built, not deployed

Premium Video Downloader landing page

Premium Video Downloader

FastAPI · yt-dlp · curl-cffi · React 19 + Vite

  • Built a video retrieval service that turns yt-dlp's raw multi-format output into a filtered API for the client.
  • Unblocked downloads against bot detection by adding curl-cffi browser impersonation.
  • Sent files with RFC 5987-compliant filename headers.

Built, not deployed

PSE Stock Compass landing page

PSE Stock Compass

Next.js 16 · TypeScript · Cheerio · lightweight-charts

  • Built a data pipeline for the Philippine Stock Exchange with a three-tier fallback: disk cache, live scrape, static snapshot.
  • Wrote a health-scoring engine that combines earnings trends, debt-to-equity, valuation multiples and dividend sustainability into one weighted score.
  • Built a Cheerio scraping layer with fuzzy label matching that covers 200+ listed companies with no per-ticker configuration.

Built, shelved for now