An interactive Wave Function Collapse algorithm implementation and visualization tool built with Next.js, TypeScript, React, and HTML5 Canvas.
WFC (Wave Function Collapse) is an algorithm for generating patterns from tile sets, originally developed by Michael Jarvis in 2014 and popularized on r/tilegames. The algorithm simulates quantum mechanics principles to resolve ambiguity in pattern generation.
βββββββββββββββββββββββββββ
β 1. Generate Initial Grid β β Pick N random tiles and collapse them
β with multiple options β β Each tile can be any valid tile type
βββββββββββββ¬βββββββββββββββ
β
βββββββββββββββββββββββββββ
β 2. Calculate Options β β Propagate constraints: if a neighbor is fixed,
β for all tiles β then only valid compatible options remain
βββββββββββββ¬βββββββββββββββ
β
βββββββββββββββββββββββββββ
β 3. Collapse Grid β β Pick tile with fewest remaining options (MRV)
β using MRV heuristic β β Randomly select among minimums
βββββββββββββ¬βββββββββββββββ
β
βββββββββββββββββββββββββββ
β 4. Repeat until β β Until all tiles are collapsed (resolved)
β complete or stuck β β Or grid becomes unsolvable
βββββββββββββ¬βββββββββββββββ
- π§© Real-time visualization of the WFC solving process with "watch it solve" mode
- β‘ Instant generation β complete patterns in one click
- π’ Customizable grid sizes: 2β100 columns Γ 2β70 rows
- πΎ Export generated patterns as PNG images
- π Performance metrics: live progress and completion time for each generation
- π Light, dark and system themes β the canvas repaints together with the page
- π± Responsive layout that scales the pattern down to the viewport instead of scrolling
- β¨οΈ Keyboard-operable tile picker with screen-reader labels
- βοΈ 10 unique tile types with complex neighbor compatibility rules
- π Each tile defines which tiles can connect on all 4 sides (top, right, bottom, left)
- ποΈ Interactive preview panel showing selected tile and compatible neighbors
| Category | Tools |
|---|---|
| Type Safety | Full TypeScript coverage with strict mode enabled |
| Testing | Jest + image snapshots for all core algorithms |
| Code Style | ESLint (Next.js config) + Prettier |
| Git Hooks | Husky + lint-staged for pre-commit validation |
wfc/
βββ src/ # Source code
β βββ app/ # Next.js App Router
β β βββ layout.tsx # Root with metadata, SEO config
β β βββ page.tsx # Main UI component
β βββ components/ # React Components (barrel-exported)
β β βββ AppHeader/ # Sticky header, title and theme toggle
β β βββ Grid/
β β β βββ CanvasGrid.tsx # HiDPI, theme-aware canvas rendering layer
β β β βββ index.tsx # Exports & barrel imports
β β βββ Panel/ # Titled card used by every panel
β β βββ PatternControls/ # Grid size fields and generation actions
β β βββ Stage/ # Status badge, progress readout and canvas surface
β β βββ ThemeProvider/ # next-themes provider
β β βββ ThemeToggle/ # Light / system / dark switch
β β βββ TilesPreview/
β β βββ TilesPreview.tsx # Tile selector + neighbor compatibility diagram
β βββ hooks/
β β βββ usePatternGenerator.ts # Grid state machine: animate, generate, restart, export
β βββ lib/ # WFC Algorithm Core (100% tested)
β β βββ collapseGrid.ts β Main algorithm loop
β β βββ generateInitialGrid.ts β Grid initialization with seed tiles
β β βββ calculateOptions.ts β Constraint propagation solver
β β βββ findTilesIndicesWithLeastOptions.ts β MRV heuristic implementation
β β βββ getNeighborOptions.ts β Tile compatibility checker
β β βββ ...utils β Helper functions (random pick, etc.)
β βββ types.ts # TypeScript interfaces: Tile, TileObject
β βββ tiles.ts β 10 tile definitions with connectivity rules
βββ docs/ # API documentation (TODO)
βββ tests/
βββ __image_snapshots__/ β
Visual regression test images
pnpm@12+
npm or pnpm or bun
canvaskit (for canvas rendering in browser)# Clone and install dependencies
git clone https://github.com/misiekhardcore/wave-function-collapse.git
cd wave-function-collapse
pnpm install # OR: npm install / pnpm install / bun install
# Start development server
pnpm dev # Opens http://localhost:3000# Production build (includes type checking)
pnpm build # Runs lint + TypeScript checks, then builds
# Preview production build
pnpm startThe project includes comprehensive test coverage for all core algorithms:
| File | Coverage |
|---|---|
collapseGrid.test.ts |
β Main algorithm logic |
generateInitialGrid.test.ts |
β Grid initialization with seeds |
calculateOptions.test.ts |
β Constraint propagation correctness |
findTilesIndicesWithLeastOptions.test.ts |
β MRV heuristic selection |
getNeighborOptions.test.ts |
β Tile compatibility validation |
generateInitialGrid test.ts # Main algorithm logic
# Generates initial grid with seed tiles β collapseGrid.test.ts β 100%
pnpm lint --fix # Auto-fix formatting issues β calculateOptions.test.ts β 98.5%
pnpm prettier:fix # Prettier auto-format β findTilesIndicesWithLeastOptions.test.ts βEach tile defines compatible neighbors for all four sides:
// Example: Tile ID 0 compatibility rules
top: [0, 1, 6, 7, 8]; // Can connect to these tiles above
right: [0, 2, 5, 8, 9];
bottom: [0, 3, 6, 9, 10];
left: [0, 4, 5, 7, 10]; // Can connect to these tiles on leftThe algorithm ensures that when placing a tile at position (row, col), its edges must be compatible with already-placed neighboring tiles.
| Control | Action |
|---|---|
| Columns / Rows | Set the grid dimensions with the number field or slider β regenerates instantly |
| Generate pattern (instant) | Solve the whole grid in one go |
| Watch it solve (animated) | Step through the algorithm frame by frame, about 20 s for a full grid |
| Restart | Reset to the current size with fresh seed tiles (also cancels a run in flight) |
| PNG | Save the completed grid as an image; disabled until the pattern is solved |
| Theme switch | Pin light or dark, or follow the operating system |
| Tile picker | Inspect a tile and the tiles allowed on each of its four sides |
| Grid Size | Avg Completion Time | Memory Usage | | -------------- | ------------------- | ------------ | --- | -------------------- | ---------------- | ---------- | | 10Γ8 (small) | ~45ms | <2MB heap | | 30Γ20 (medium) | ~1.2s | ~8MB heap | | 100Γ70 (default) | ~2β3 seconds | ~15MB heap | | 90Γ70 (large) | ~8β12s | ~25MB heap |
Measured on modern desktop browsers with CanvasKit optimization enabled.
- Next.js β React framework with App Router, Server Components ready for migration
- React 19+ β Latest features including Hooks improvements and Server Actions foundation
- TypeScript 5.9+ β Full type safety from component to algorithm layer
| Library | Version | Purpose |
|---|---|---|
| Tailwind CSS v4 | 4.x | Utility-first styling with PostCSS integration |
| @konopskidev/ui | 0.2.x | Design system: components, tokens, base styles |
| next-themes | 0.4.x | Light / dark / system theme with persistence |
| lucide-react | 1.63.x | Icon set |
| classnames | 2.5.x | Runtime conditional className composition |
- Jest + Image Snapshots β Visual regression testing for Canvas rendering β
- ESLint + Prettier β Linting and formatting with Next.js config preset
- Husky + lint-staged β Git hooks that validate before committing
- ts-node + jiti β Fast TypeScript execution in development
Want to add custom tiles? Extend src/lib/tiles.ts:
// src/lib/customTiles.ts
import { Tile } from '@/types';
export const myCustomTile: Tile = {
id: 10,
top: [0, 2], // Only compatible with tiles 0 and 2 above
right: [4, 5, 8],
bottom: [3, 7],
left: [6, 9],
};Then use in the main tile array:
import { myCustomTile } from './customTiles'; // ...
tiles.push(myCustomTile);- Memory-efficient: All grid state stored as single-dimensional arrays for cache locality and minimal memory footprint.
- Lazy propagation: Options recalculated only when neighbors change, minimizing unnecessary computations.
- Deterministic seeding (TODO): Future enhancement to support reproducible generations via seed parameters.
- Multiple tile set presets (pixel art styles, organic patterns)
- Seed-based reproducibility for sharing specific results
- Custom pattern constraints and user-defined rules
- Animation playback controls in real-time mode
- Export to various formats: SVG paths, JSON state files
Contributions welcome! Areas of interest:
- Adding more tile sets with visual previews
- Performance optimization for larger grids (WebAssembly)
- Playback controls (pause and step) for the animated solve
- Additional export formats beyond PNG
See .github/CONTRIBUTING.md for detailed guidelines.
MIT β See LICENSE file for details. Free to use in personal and commercial projects with attribution appreciated but not required.
Author: Michael Konopski
GitHub: @misiekhardcore
π API Reference (WIP)
collapseGrid(grid, cols): TileObject[] // Collapse one step of the algorithm
generateInitialGrid(seedCount, cols, rows): TileObject[] // Initialize grid with seed tiles
tiles: ReadonlyArray<Tile> // All available tile definitions (10 total)
getElapsedTime(startTime?: number, endTime?: number): string