Jon Milley Creations

Introducing QuiltForge: Where Software Meets Stitches

As I mentioned in my last post, I’ve taken up quilting for the first time. Like most beginners, I quickly discovered that quilting is far more than cutting fabric and sewing it back together. There is colour theory, geometry, fabric estimation, cutting precision, and a surprising amount of planning before a single stitch is made. As someone who has spent years writing software, my instinct was immediate: there has to be a better way to plan all of this digitally.

So I built one.

QuiltForge is a web-based quilt design application I have been building in my spare time. It is free to use, runs entirely in your browser, and is designed to help quilters (especially those just starting out like me) plan their projects from start to finish before touching a single piece of fabric.


What QuiltForge Can Do So Far

Fabric Library

You can upload photos of your fabrics and organise them in a personal library. Add tags, give them names, and QuiltForge will automatically extract a dominant colour from each one. Your fabrics then appear as swatches throughout the rest of the app, so your designs always reflect what you actually have on hand.

Block Designer

This is where the fun begins. QuiltForge ships with a growing library of classic quilt block templates including Half-Square Triangles, Quarter-Square Triangles, Flying Geese, Pinwheels, Ohio Stars, Log Cabins, Churn Dash, Sawtooth Stars, and more. Select a template, assign your fabrics to each shape, and watch it come to life with your actual fabric textures.

There is also a free-form drawing mode for when you want to design something entirely from scratch.

Quilt Layout Designer

Once you have designed your blocks, you can arrange them into a full quilt layout. Set your grid dimensions, drag blocks into cells, and mix and match them across the quilt. Each cell can be individually rotated or flipped. You can also add sashing, multiple borders, and binding, each with their own fabric assignments.

The layout gives you a live preview of your finished quilt with accurate proportions and your real fabric textures applied.

Fabric Estimator

One of the trickiest parts of starting a quilt is figuring out how much fabric to buy. QuiltForge calculates yardage requirements for every fabric in your quilt automatically, including seam allowances, a 15% waste factor, and the standard bolt width. It rounds up to the nearest eighth of a yard so you always have enough.

Cutting Calculator

A standalone tool that takes a finished piece size and tells you exactly what to cut, with seam allowances included. It covers the most common units: squares, half-square triangles, quarter-square triangles, flying geese, and more.

When you are ready to get to the sewing room, QuiltForge can generate a print-ready summary of your project including a quilt layout diagram, block diagrams with piece labels, a full cutting list organised by fabric, and a fabric requirements table. You can also export the quilt layout as a PNG image.


A Work in Progress

I want to be upfront: QuiltForge is very much a work in progress. I am still early in both my quilting journey and the development of this tool. Some features are rough around the edges, a few are incomplete, and the design will keep evolving as I learn more about what quilters actually need.

I am building this alongside my own learning. Every time I start a new project and run into a planning problem, it becomes a feature on my list.


Free to Use, Always

QuiltForge is and will remain free to use. There is no account required, no subscription, and all your data stays in your browser. My goal is simply to make quilt planning easier and more enjoyable, and I hope it can be useful to others who are just starting out the way I am.


I Would Love Your Feedback

If you give QuiltForge a try, I would genuinely love to hear what you think. What works well? What is confusing? What feature would make your planning life easier? Whether you are a brand new quilter or someone with decades of experience, your perspective would help shape where this goes next.

You can find the app at quiltforge.jonmilley.com and send feedback or feature requests my way at https://x.com/jonmilley.

Happy quilting!

Starting My Quilting Journey at the Anna Templeton Centre

Starting My Quilting Journey at the Anna Templeton Centre

There’s something about picking up a craft that connects you to the people who came before you. For me, quilting is that craft, and today I took my first real steps into it.

I enrolled in Quilting: Next Level at the Anna Templeton Centre for Craft, Art & Design in St. John’s. The centre is tucked into a beautiful historic building on Duckworth Street, a not-for-profit that’s been championing craft in Newfoundland for years. I’d been there before. I took an earlier course with the same instructor where we made a tote bag and a zippered pouch, and the warm, encouraging atmosphere is a big part of why I came back.


Day One: Strips, Cuts, and the 9-Patch Block

Today’s class was all about foundations. We started by cutting our fabric into strips, then sewing those strips together into a strip set. Strip piecing is wonderfully efficient. Rather than cutting and sewing individual squares one at a time, you assemble long strips first and then crosscut them into ready-made segments. It feels almost like a cheat code once you see it in action.

From those segments we’re building toward a 9-patch block, nine squares of fabric arranged in a three-by-three grid, alternating between two fabrics. It’s one of the most classic blocks in quilting, and for good reason: it teaches you to keep seams straight, press accurately, and nest your seams so the intersections lie flat. Get comfortable with the 9-patch and you have a foundation you can build almost anything from.

One thing I appreciated: we learned both ways to make a 9-patch. The strip set method is the efficient, production-friendly approach, great when you’re making many blocks. But we also cut and sewed individual squares by hand, which is slower and more deliberate. The instructor designed it this way intentionally, so we’d understand not just how to do it, but why each method exists and when to reach for which one.

I left class with my strips cut, sewn, and ready for the next step. It’s a small thing, but holding those pieces and seeing them actually align was satisfying in a way I didn’t quite expect.

My 9-patch squares laid out on the cutting mat

Here’s what the instructor’s finished sample quilt looks like, and where we’re headed:

Instructor's sample quilt showing the finished 9-patch pattern


Why This Matters to Me

I’m not taking this course just to learn a new hobby. There are two projects waiting for me on the other side of this learning curve, and both of them carry a weight that’s hard to put into words.

My Mother’s Quilt

My late mother nearly finished a quilt. It’s a classic block pattern with a border, and she got so close. It just needs to be quilted. For years it’s sat waiting, almost done, a project she never got to complete.

I want to finish it for her. Learning to quilt properly, understanding tension and batting and how to move a quilt through a machine or by hand, means I’ll be able to do it right. It deserves to be done right.

My Grandmother’s Embroidery

My grandmother was a skilled hand embroiderer, and before she passed she created a collection of pieces depicting Newfoundland scenes: lighthouses, fishing boats, puffins, moose, the landscapes and wildlife of this place we call home. They are beautiful, and they deserve to be seen.

My plan is to incorporate them directly as quilt blocks, setting her embroideries into a larger quilt design. Before they go in, I’ll interface them, a layer of fusible backing that stabilizes the fabric and protects all that careful hand stitching from the stress of being worked into a quilt. It’s a way of honouring the work she put into each piece while giving them a new life as something you can wrap around yourself on a cold Newfoundland evening.

One of my grandmother's hand embroidered Newfoundland scenes


Looking Forward

I’m excited about where this goes. Not just for the technique, though I do want to feel genuinely confident taking on my own projects, but for what’s possible once I have the skills. A quilt made from my grandmother’s embroidered puffins and lighthouses. My mother’s quilt, finally finished.

There’s a long tradition of quilting in Newfoundland, of women sitting together and turning scraps of fabric into something lasting and beautiful and warm. I feel like I’m just starting to understand what that means.

More updates as the course progresses.

Rewriting a 2004 PHP Site with Claude Code

Rewriting a 2004 PHP Site with Claude Code

LotusEmpire.com has existed in roughly the same form since 2004. It’s the community hub for a text-based online game (a MUD), Lotus MUD, and over the years it accumulated the kind of technical debt you’d expect from PHP written by a college student two decades ago: MD5 passwords stored in plaintext-equivalent form, SQL queries built by string concatenation, hardcoded server paths, and a WYSIWYG editor (HTMLArea) that hadn’t worked in any modern browser for years.

The site was never broken enough to demand a rewrite. But it was never good, either. I finally decided to do something about it, and I used Claude Code to drive the entire process, from architecture to implementation.

Here’s what the original looked like:

Screenshot of the original LotusEmpire site


What the Site Does

LotusEmpire is a community portal for a MUD, a text-based multiplayer game that predates the modern MMORPG. The site serves several purposes:

  • Public content: lore, guides, area descriptions with a custom dynamic description engine
  • Wholist: a live-updating list of who’s online in the game (polled from the MUD server via TCP)
  • Area Explorer: an interactive browser for in-game areas, powered by that same dynamic description parser
  • Builder tools: a semi-private section for the volunteer builders who create in-game areas, including a to-do list and internal messaging
  • Admin panel: full content management: create/edit sections, manage images, manage builder accounts

The “dynamic description engine” deserves a mention. The MUD uses a custom markup language in area description files that lets descriptions change based on time of day, weather, flags, and other conditions. I had already built a TypeScript reimplementation of this parser in a separate project (dynparse), which became the foundation for the Area Explorer in the new stack.


Starting the Rewrite with Claude Code

I opened the project in Claude Code and ran /init to get it oriented. It analyzed the PHP codebase and produced a CLAUDE.md documenting the architecture, the 3-level content hierarchy (sections, subsections, subsubsections), the role system (admin vs. builder), and the known security problems.

From there I ran /plan to design the new stack. The conversation that followed was genuinely back-and-forth. Claude Code proposed an architecture, I pushed back on certain choices, and we iterated until we had something I was happy with.

The Stack Decision

The final plan settled on:

  • pnpm monorepo with apps/api (Fastify) and apps/web (Vite + React SPA)
  • MariaDB (unchanged) with Drizzle ORM replacing raw SQL
  • JWT access tokens in memory + httpOnly refresh cookie replacing the PHP session/cookie auth
  • TipTap replacing HTMLArea for rich text editing
  • fast-xml-parser for parsing MUD XML feeds (wholist on port 14501, area explorer on port 14500)
  • node-cron for a 5-minute wholist cache refresh

The dynparse package I’d already written got embedded as a workspace package (@lotusempire/dynparse). Both the Fastify server (for SSR-style area rendering) and the React AreaExplorer component (for live preview) consume it.

The Migration Strategy

One of the more interesting planning conversations was about how to migrate without taking the site down. The plan Claude Code proposed used nginx as a traffic router: PHP (Apache on :8080) and the new Node/React app (:3001/:5173) run in parallel, with nginx routing by path prefix. You shift traffic feature-by-feature, and rolling back is just a nginx config change.

Database migration was designed to be additive, no columns dropped, no tables renamed, so PHP could stay live during the rollout. Password migration was handled transparently: on login, if the stored hash is MD5, accept it, silently re-hash to bcrypt, and clear the MD5 field. After cutover, remove the fallback.


Execution and Iteration

The implementation proceeded in phases roughly matching the plan. What made Claude Code genuinely useful here wasn’t just code generation. It was the ability to describe a gap and have it figure out the right fix across multiple files at once.

A few examples of back-and-forth that shaped the final result:

The builder auth bug. The builder management page was calling apiPost('.../delete') but the backend route was DELETE /api/admin/builders/:id with a numeric ID. Claude Code caught this while reviewing the admin panel plan and fixed both the HTTP method and the ID extraction in one pass.

The images route mismatch. Images are registered at /api/images in the Fastify app, not /api/admin/images. When implementing the admin images page, the initial code had the wrong prefix in all three API calls. Flagged during review, fixed immediately.

The admin login redirect. RequireAdmin was redirecting unauthenticated requests to / instead of /admin/login, which didn’t exist yet. Claude Code added the login page, wired up the route, and fixed the redirect in one session.

dynparse integration. When I mentioned that I already had a TypeScript implementation of the dynamic description parser in a separate project, Claude Code updated the plan on the spot: instead of reimplementing it, copy parser.ts and evaluator.ts (zero external dependencies) into a workspace package. This saved a significant amount of work and ensured the parser behavior matched what the MUD actually produces.


The Result

Here’s the rewritten site:

Screenshot of the rewritten LotusEmpire site

The security picture is dramatically improved:

  • Passwords: bcrypt with transparent migration from MD5
  • SQL: Drizzle ORM; no string-concatenated queries
  • Auth: JWT + httpOnly cookie; no PHP session files
  • Input: validated at API boundaries

The editor works again. The Area Explorer runs the same parser the MUD uses. The wholist updates on a cron every five minutes without a page refresh.


Reflections on Claude Code as a Planning Partner

The most useful thing Claude Code did in this project wasn’t write code. It was maintain coherent context across a large, multi-session rewrite. A PHP-to-React migration touches authentication, database access, routing, rich text editing, real-time data, and file uploads simultaneously. Keeping all of those decisions consistent across a two-week effort is genuinely hard to do in your head.

The /plan workflow produced a document I could return to, update, and use as a checklist. When something changed (like the dynparse integration), updating the plan kept everything downstream consistent. When I found a bug or a mismatch, describing it in plain English got me a targeted fix across the relevant files rather than a generic suggestion.

It’s not magic. You still have to understand what you’re building. But as a tool for holding complexity and translating decisions into working code, it made a 20-year-old PHP site into something I’m not embarrassed to show people.