Chess game in JavaScript
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
Building a chess game in JavaScript is a good test of architecture because the project is half rule engine and half UI. The easiest way to make it unmaintainable is to mix board state, move validation, and DOM updates into one giant function.
Start with a Board Model, Not the DOM
The board should live in plain JavaScript data so it can be tested, cloned, and manipulated without any rendering code. A two-dimensional array is a reasonable starting point.
The first character marks side and the second marks piece type. This encoding is compact and works well for an initial engine. You can move to richer objects later if needed.
Write Small Rule Helpers First
Before generating legal moves, create tiny utilities for bounds checks, piece lookup, and side comparison. Those helpers keep move generation readable.
These may look trivial, but they prevent index and ownership checks from being duplicated across every piece implementation.
Generate Moves Per Piece Type
Avoid one huge isValidMove function. Give each piece its own move generator and dispatch based on the piece code. A pawn implementation shows the pattern clearly.
Once this pattern works for one piece, you can add rook rays, bishop diagonals, knight jumps, king steps, and queen combination moves in the same style.
Keep Move Application Pure
A move function should update board state without touching the UI. Returning a new board rather than mutating the old one makes simulation easier.
Pure board transitions are useful for undo, move previews, and check detection because you can simulate many candidate moves without risking hidden shared state.
Add Rules in Layers
A sensible implementation order is:
- board state and rendering
- piece movement rules
- turn enforcement
- capture handling
- check detection
- castling, en passant, and promotion
- checkmate and stalemate
The key transition is check detection. Until the engine rejects moves that leave your own king attacked, it is only generating pseudo-legal moves, not real chess moves.
Test the Engine Without Clicking the UI
Rule bugs are easier to detect in unit tests than through manual browser interaction.
With this approach, you can test blocked movement, pinned pieces, castling rights, and special captures directly. The UI then becomes a rendering and input layer on top of a separately trusted engine.
Common Pitfalls
- Mixing DOM updates and rule validation in the same function.
- Mutating the live board while simulating hypothetical moves.
- Stopping at pseudo-legal piece movement and forgetting king safety.
- Implementing special rules before basic movement and turn order are stable.
- Starting AI work before the rules engine is trustworthy.
Summary
- Keep the board model in plain JavaScript data, not in the DOM.
- Build small helpers before writing full move generators.
- Give each piece its own move logic instead of one giant validator.
- Apply moves in a pure, testable way.
- Treat the UI as a view over state and verify the engine with tests first.

