Go to file
Brian Fertig 004fc8d033 Added Read Me file 2026-04-12 09:00:01 -06:00
assets feat: add 10 new puzzles with automated image processing pipeline 2026-04-06 20:01:25 -06:00
js feat(puzzle): add stats completion sound loop and refine glow/timing logic 2026-04-06 19:54:46 -06:00
server feat: add player name support for rooms and networked gameplay 2026-04-04 20:58:41 -06:00
CLAUDE.md first commit 2026-04-04 18:51:09 -06:00
README.md Added Read Me file 2026-04-12 09:00:01 -06:00
index.html feat: implement responsive canvas scaling with Phaser.Scale.FIT and DOM overlay sync 2026-04-05 23:17:14 -06:00
instructions.md Updated to include multiplayer 2026-04-04 19:51:35 -06:00
software.md first commit 2026-04-04 18:51:09 -06:00
start_web.sh Updated to include multiplayer 2026-04-04 19:51:35 -06:00

README.md

iPuzzle

A browser-based multiplayer jigsaw puzzle game built with Phaser 3 and vanilla ES6 JavaScript. Pick an image, choose a difficulty, and solve the puzzle solo or with friends in real time — no account required.


Features

  • 55+ puzzle images — a curated library of illustrated and photographic artwork, from fantasy landscapes to wildlife
  • 7 difficulty levels — 20, 40, 60, 100, 140, 180, or 250 pieces
  • Procedurally generated pieces — interlocking tab/blank connectors generated from a grid so every edge fits its neighbor
  • Real-time multiplayer — create a room and share the 4-character code (or a direct URL) for others to join instantly
  • Player color labels — each player's held piece shows their name tag in a unique color
  • Snap detection with glow — matching edges glow as you bring a piece close; release to lock
  • Group movement — snapped pieces move as a unit
  • Pan and zoom — middle-click drag or right-click drag to pan; scroll wheel to zoom
  • Box selection — click-drag on empty space to grab multiple pieces at once
  • Background themes — Dark Wood or Green Felt table surface
  • Puzzle timer — tracks time from first interaction to completion
  • Completion stats — snap rate chart, corner/edge/quadrant milestones, per-player snap counts
  • Background music — 8 in-game tracks + main menu music with mute toggle
  • Session persistence — progress is saved to localStorage; resume an unfinished puzzle from the main menu
  • Fullscreen support
  • Responsive canvas — scales to fit any window size via Phaser.Scale.FIT

Running the Game

Because Phaser loads assets over XHR, the game must be served over HTTP (not opened as a file:// URL).

Option A — Node.js server (multiplayer)

The Node server handles both static file serving and WebSocket multiplayer.

cd server
npm install
node server.js
# open http://localhost:8080

Set a custom port with the PORT environment variable:

PORT=3000 node server.js

Option B — Simple HTTP server (single-player only)

Any static file server works if you only need single-player:

# Python
python3 -m http.server 8080

# Node (npx)
npx serve .

Then open http://localhost:8080.


Multiplayer

  1. One player starts a New Puzzle — a 4-character room code is generated (e.g. X4KR).
  2. Share the code or the full URL (e.g. http://your-host:8080/?room=X4KR).
  3. Other players click Join Puzzle and enter the room code, or open the share URL directly.
  4. All players see each other's pieces in real time. Piece claiming prevents two players from grabbing the same piece simultaneously.

Rooms persist on the server for 5 minutes after the last player disconnects, allowing brief reconnections.


Controls

Action Input
Pick up / release piece Left-click
Pan the board Middle-click drag or right-click drag
Zoom Scroll wheel
Box-select multiple pieces Left-click drag on empty space
Fullscreen Button on main menu

Project Structure

iPuzzle/
├── index.html                  # Entry point; loads all scripts via <script> tags
├── server/
│   ├── server.js               # Node HTTP + WebSocket server
│   └── package.json
├── js/
│   ├── main.js                 # Phaser game config; registers all scenes
│   ├── scenes/
│   │   ├── MainMenuScene.js    # Animated main menu, join/create room UI
│   │   ├── NewPuzzleScene.js   # Image picker, difficulty selector
│   │   └── PuzzleScene.js      # Core gameplay loop
│   ├── puzzle/
│   │   ├── ConnectorGeometry.js  # Tab/blank connector math
│   │   ├── PuzzleGenerator.js    # Divides image into piece grid
│   │   ├── PieceRenderer.js      # Draws each piece to a canvas texture
│   │   ├── PieceObject.js        # Phaser Image wrapper for a piece
│   │   ├── GroupManager.js       # Tracks which pieces are locked together
│   │   ├── SnapDetector.js       # Proximity checks and glow segment generation
│   │   └── RoomCode.js           # 4-character room code generator
│   ├── state/
│   │   ├── PuzzleState.js        # In-memory canonical puzzle state
│   │   └── StorageManager.js     # localStorage persistence helpers
│   └── net/
│       └── NetworkManager.js     # WebSocket client singleton
└── assets/
    ├── puzzles.json              # Manifest of available puzzle images
    ├── thumbnails.json           # Manifest of thumbnail paths
    ├── images/
    │   ├── puzzles/              # Full-resolution puzzle images + thumbnails
    │   └── ui/                   # Backgrounds, logo, sub-logo
    └── audio/
        ├── music/                # main_menu.mp3, track_0108.mp3, tracks.json
        └── fx/                   # click.mp3, grab.mp3, stats.mp3

Architecture

Scenes

Scene Responsibility
MainMenuScene Animated intro, Continue / New Puzzle / Join Puzzle / Fullscreen buttons, join dialog
NewPuzzleScene Scrollable image gallery with thumbnails, difficulty picker, background picker
PuzzleScene Piece rendering, interaction, snap/glow, pan/zoom, multiplayer relay, completion stats

Puzzle piece generation

  1. PuzzleGenerator divides the chosen image into a cols × rows grid.
  2. ConnectorGeometry assigns tab or blank connectors to each shared edge, ensuring that piece A's tab aligns with piece B's blank.
  3. PieceRenderer draws each piece — with its connectors — to an off-screen canvas, then registers it as a Phaser texture.
  4. PieceObject wraps a Phaser Image and tracks grid position, group membership, and world coordinates.

Snap detection

Each frame while a piece is held, SnapDetector.check() compares every piece in the held group to its grid neighbors. If any neighbor is within snapRadius (35% of piece width), the pieces lock together via GroupManager. A larger glowRadius (140% of piece width) drives the edge-glow preview.

Multiplayer protocol (WebSocket)

Message Direction Description
create_room client → server Host sends initial puzzle state
room_created server → client Confirms room code and player ID
join_room client → server Joiner requests to enter a room
room_joined server → client Returns full puzzle state, player list, and existing claims
player_joined / player_left server → all Presence events
claim client → server Request exclusive hold on a piece group
claim_ok / claim_denied server → client Claim result
move client → server Throttled (50 ms) position delta while dragging
release client → server Drop with updated positions and merged groups
completed server → all Broadcast when all pieces form one group

State persistence

StorageManager serialises the current PuzzleState to localStorage on every significant event (snap, release). On "Continue Puzzle" the state is restored from storage. All state is keyed by room code to support future multi-room play.


Adding New Puzzles

  1. Add a 16:9 PNG or JPG to assets/images/puzzles/.
  2. Create a thumbnail (any size, same basename prefixed with thumb_) in the same folder.
  3. Add an entry to assets/puzzles.json and assets/thumbnails.json.

A helper script for bulk thumbnail generation is provided at assets/images/puzzles/make_thumbnails.sh.


Tech Stack

Concern Technology
Game engine Phaser 3.90 (CDN)
Language Vanilla ES6 (no build step, no npm on the client)
Multiplayer WebSocket via the ws library
Server Node.js (built-in http + ws)
Persistence Browser localStorage