joemattie
stuff
← index
build2026-07-052 min readupdated 2026-07-09

Multiplayer Puzzler

Joe Mattie
software engineer · mad scientist

My wife Caity loves puzzles. So does our cat, Mae “Muskrat” Mattie, whose idea of enjoying one is carrying a piece off and tossing it around the house. A physical jigsaw on the coffee table doesn’t last long.

So Caity puzzles mostly on her phone or iPad, where Mae can’t relocate the pieces. I like puzzles too, so I built Multiplayer Puzzler: a jigsaw we can both work on at the same time.

What it is

A real-time collaborative jigsaw game. Pick an image and a piece count, get a shareable URL, and anyone with the link drops into the same board. You see each other’s cursors live, pieces snap in when they’re close enough, and snapped pieces merge into groups that move as one unit. Mouse on desktop, touch gestures on phone or tablet, so Caity on her iPad and me on a laptop can chip away at the same puzzle, no pieces under the couch.

Multiplayer Puzzler’s creation screen with six source images, piece counts from 200 to 900, and the classic jigsaw tile style

How it works

A React and PixiJS client talks to an Express and Socket.IO server, with shared Zod schemas keeping both sides honest about what a message looks like. Postgres stores the puzzles underneath.

Two parts were more interesting than I expected:

The pieces are real geometry, not a grid crop. The server generates a “stencil”: for each piece it decides where the edges tab out and where they curve inward, then emits bezier curves for every edge, each tab matched by a blank on its neighbor. The client fetches the stencil plus the source image and uses the curves to mask each piece into its own sprite. So they interlock like a real jigsaw instead of square tiles.

Multiplayer means someone has to own each piece. The hard part isn’t showing cursors, it’s stopping two people from grabbing the same piece at once. The server runs an ownership system: piece:grab locks a piece and its whole snapped group, piece:drop validates you hold the lock before it checks for snaps and merges groups. Without it, two players dragging the same cluster fight over its position and desync the board.

The rest is the long tail of making a canvas game feel good: zoom, pan, and rotate the board; a tray that holds unplaced pieces and scales them by piece count; rotating individual pieces; rotation-aware snapping so a piece clicks in even when turned; and a debug mode (tilde key) that highlights matching edges on hover, mostly for me while chasing snapping bugs.

Where it stands

As of July 9, 2026, it works and is a genuinely pleasant way to do a puzzle with someone remotely. It is not deployed publicly because the server and Postgres setup turn a household tool into an infrastructure project I have not cared enough to operate. It runs locally through Docker instead. Curated starter images come from Unsplash via a seed script. Mae remains unable to interfere.

typescriptmultiplayergamespixijs