Projects

A running overview of the things I am building — what they do, where they stand, and what is next.

ArtPipeline

Live

Shared pixel-art generation — a chat command or a coding agent in, a reviewed, game-ready sprite out.

Pixel-art asset generation for my games, shared with a collaborator. A Discord slash command queues a request on an always-on VPS, so it survives my desktop being off; when the PC comes online its worker renders the queue locally on the GPU — no hosted generation service. Each sprite comes back to the channel with Keep / Discard / Another buttons, and only the ones we keep are committed to a shared asset repo.

The interesting half isn’t the model. Local diffusion produces pixel-art-styled images, not true pixel art, so the post-processing is load-bearing: reconstruct the real pixel grid, remove the background (following gradients and peeling off frames the model draws unasked), and snap every colour to a fixed per-game palette in CIELAB, because an RGB match swaps hues on dark shades. Because the pipeline chose the render size, it knows the grid’s cell size and passes it to the reconstruction step instead of letting it search: 9.4 seconds down to 0.75, and exact rather than inferred. And each game renders every category at the same render-pixels-per-sprite-pixel, so a 64px chair and a 256px building are drawn at the same level of detail.

The Claude Code agents building my games use it too, through a skill rather than by driving the model directly. A small command-line tool gives them the same pipeline the Discord queue uses: create a game’s style if it doesn’t exist yet (categories, sprite sizes, palette), draft a batch of candidates to a scratch folder, look at each one, and keep the good ones. Keeping commits the sprite, with the prompt and seed that reproduce it, into the same shared repo, under a lock that the Discord worker and other agents also take. The JezzBall agent set up its own game this way and made its ball and tile art. Its feedback shaped the tool: pale subjects came out hollow because a “plain” background is white and leaked through gaps in the outline (a solid dark green background fixed that, from 3 hollow in 6 to none), and tile categories now skip background removal and come with a tiled preview for checking the seams.

It runs end to end today: a few seconds from claim to sprite, with results that survive Discord outages, bot restarts and the worker dying mid-job.

Next Letting the game agents use it for real and seeing where it falls short. Character animation is the likely next pressure — keeping a character consistent across jump and attack frames — and a per-game LoRA trained on the sprites we've kept would lock each look in rather than steering it by prompt alone.

  • Python
  • PyTorch
  • ComfyUI
  • discord.py
  • FastAPI
  • SQLite
  • Docker
  • Caddy
  • DigitalOcean
  • Azure DevOps

Gambling Tracker

Live

An offline-first phone app for tracking a casino trip.

Keeps a running bankroll across a casino trip and logs sessions and spending in a few taps — live and tournament poker, slots, table games, and non-gambling spend — each tagged to the casino where it happened. A bankroll graph and trip-review screens let you see how a trip went, broken down room by room.

Designed for one-handed use on an iPhone and built to keep working without a signal: trip data lives on the device, so logging continues even when the casino floor has no reception. Casinos come from a built-in catalog you can extend with your own custom rooms and logos, and each trip draws from the set you pick at the start.

Because everything lives in on-device storage, there’s a manual backup path: export your whole history to a single JSON file and import it back later, choosing to merge it in or replace what’s there — enough to move data between devices or keep a safe copy.

JezzBall

In progress

A remake of the DOS-era JezzBall, with a few twists, headed for the browser.

Balls bounce around a rectangle at constant speed. You click to start a wall growing in both directions across their path, and any region you seal off without a ball trapped in it is erased from play. Clear enough of the board and the level is done; the next one adds another ball. Get clipped by a ball while a wall is still growing and the wall is lost along with a life.

Past the original, the ending of a level is where the game gets interesting. Clearing well beyond the target pays a bonus that ticks up a percent at a time, and from the third level a translucent bonus zone appears on the board: every ball inside it when the level ends adds 25% to that level’s points. You can wall a ball into a corner of the zone, or time your last wall as one passes through — and herding several balls in at once is exactly what makes a level risky. Cleared area sweeps in as a wave from the wall, a meter beside the board rises to match, and the results are revealed one bonus at a time.

The whole thing hangs on one decision: the authoritative game state is a grid of cells, not Unity’s physics engine. Clearing area is then a flood fill outward from each ball — whatever no ball can reach gets filled in. That runs in microseconds and only on wall completion, which is what makes the extensions I actually want to try later (curved walls, diagonals, non-rectangular boards) rasterization work rather than a rewrite. Balls move kinematically with swept collision against that grid rather than on a Rigidbody2D, because the original’s bounces are crisp and constant-speed and physics would feel mushy next to them. The scoring and level rules sit in plain C# with no Unity dependency, apart from the code that draws and reads input.

The look is now pixel art, generated through my ArtPipeline by the coding agent working on the game: the ball, a bevelled tile that patterns every cleared area, the steel side panels and the HUD’s hearts and target symbol. The game has a fixed native resolution of 960x540 with every sprite drawn at 4 pixels per grid cell, and Unity’s Pixel Perfect Camera scales it to the window. That was a fix, not a starting point: stretched by a fraction, the tile’s one-pixel edges landed on one or two screen pixels in a repeating beat. Now it scales by whole multiples and smooths only the remainder, so the browser embed and a 1080p screen get exact pixels. Walls snap onto the tile grid, so a landed wall is always a clean row of blocks.

The HUD lives in panels either side of the board: level, score, lives as hearts, and on the right a cleared meter with the target marked. It sweeps up after each wall, and whatever you clear past the target shimmers as a rainbow, because that part is bonus.

It’s playable start to finish in the editor. The text is still a plain font, and there’s no browser build yet.

Next Swapping the HUD's plain text for a pixel font, then playing more to see whether the bonus zone is the right hook and taking it further in later levels. After that, a saved high score and the first browser build — embedded at the game's native 960x540 — with a touch-friendly way to flip a wall's orientation.

  • Unity
  • C#
  • WebGL

MeepleBoard

Live

A personal board-game play tracker.

Shows my whole board-game collection as a grid of box art, tracking each play as a meeple (wood for one play, silver for ten, gold for fifty). Filtering to a group of players shows only the games everyone already knows, and a player count narrows it further — handy for picking the next thing to play.

A “5th Grade Challenge” page tracks our family’s attempt to play the entire collection during my daughter’s 5th-grade year, as a calendar of box art.

The public view is optimized for use as a wall-mounted display; recording plays happens from my phone on a password-protected admin version of the site.

  • .NET 9
  • EF Core
  • React 19
  • TypeScript
  • Vite
  • SQLite
  • Docker
  • Caddy
  • DigitalOcean
  • Azure DevOps
Visit →

Personal Blunder Trainer

Parked

Turns your own chess mistakes into practice puzzles.

Pulls in your Chess.com games, uses the Stockfish engine to find the moments you went wrong, and turns those exact positions into training puzzles — so you drill the patterns you actually get wrong until they stop recurring. It tracks repeated weaknesses over time, links each puzzle back to the real game it came from, and keeps every player’s history separate.

Built as three cooperating services — a web front end, an API, and a background analysis worker — with careful data modeling so the same blunder across different games is recognized as one recurring lesson.

Next Paused and offline for now. It was built to come back easily, and the next step on return is richer puzzle explanations.

  • Next.js
  • ASP.NET Core
  • PostgreSQL
  • Supabase
  • Stockfish
  • Docker
  • Microsoft Entra ID
  • Azure DevOps

Space Miner

In progress

A browser-based, space-themed area-control strategy game.

Expand your territory across a solar system of planets, shoot down passing meteors for resources, and build up turrets, rovers, launchpads, and drills to out-develop your rivals. Win by seizing control of a shared central planet before anyone else can.

It’s two pieces working together: a Unity game that runs in the browser, and a lightweight real-time server for online matches. The clever part is that every player’s screen reconstructs the same action from a shared random seed, so the server stays small and cheap while play stays identical for everyone.

For now it plays against AI bots and sits behind a password on itch.io while it gets some polish. The online multiplayer server is temporarily offline — an Azure mishap forced me to delete the subscription that hosted it — so standing it back up is the next thing on the list.

Next Standing the online multiplayer server back up — it's bots-only until then.

The Chore Drawer

Live

A playful household chore picker.

Pull open the drawer to draw a random chore that’s currently due, then pin it to the wall to commit to it. Up to three accepted chores hang on brass tacks and persist across visits, so one you took but couldn’t finish is still waiting next time. When you’ve actually done it, drag the slip off its tack and let it float away. Chores recur on a set cadence — daily, weekly, monthly, or quarterly — and resurface once their window lapses.

It’s a personal household tool, themed like an old library card catalog. A shared PIN gate keeps strangers out of the public URL — first visit redirects to an unlock page, and entering the PIN sets a signed 30-day cookie.

Next Build a visual number pad for PIN entry — make unlocking the site a tap-friendly keypad rather than a plain text field.

  • .NET 10
  • Blazor Server
  • EF Core
  • SQLite
  • Docker
  • Caddy
  • DigitalOcean
  • Azure DevOps
Visit →

Vital Vortex

Parked

A personal macro and nutrition tracker.

Plan meals across the day, track water and macros (calories, protein, fat, carbs, sugar, fiber) against personal goals, and review a day-by-day history. Goals are computed from each person’s profile, and a built-in food search pulls nutrition data straight from the USDA database so entries fill themselves in.

Every account’s foods, plan, history, and settings are fully separate. It started life as a Google Sheets-backed script and has since been rebuilt as a proper self-hosted web app.

Next Offline for now, since its primary user has moved on. It's kept so it can come back easily if needed.

  • Flask
  • Gunicorn
  • Python
  • SQLite
  • Docker
  • Caddy
  • DigitalOcean