100 chess variants with AI, embed support, and a headless SDK for server-side consumers.
Drop a board into any page with a single iframe. The embed is fully responsive and touch-ready.
<iframe
src="https://engine.moddable.games/play/?embed=1&variant=standard"
style="width:100%;max-width:560px;aspect-ratio:1/1;border:none"
title="Play Chess"
></iframe>
| Param | Values | Default |
|---|---|---|
embed | 1 | Required for embed mode |
variant | Any variant key | standard |
opponent | ai | human | ai in embed |
difficulty | beginner | easy | medium | hard | expert | medium |
color | white | black | white |
pieces | Piece set ID | mce-fairy-complete |
flipped | 1 | Board orientation |
Control the embed without reloading the iframe. Send messages to iframe.contentWindow:
// Change variant
iframe.contentWindow.postMessage({ type: 'chess:setVariant', variant: 'atomic' }, '*')
// Change difficulty (also activates AI)
iframe.contentWindow.postMessage({ type: 'chess:setDifficulty', difficulty: 'hard' }, '*')
// Switch between human/AI
iframe.contentWindow.postMessage({ type: 'chess:setOpponent', opponent: 'ai' }, '*')
// Game controls
iframe.contentWindow.postMessage({ type: 'chess:newGame' }, '*')
iframe.contentWindow.postMessage({ type: 'chess:undo' }, '*')
iframe.contentWindow.postMessage({ type: 'chess:flip' }, '*')
// Change piece set
iframe.contentWindow.postMessage({ type: 'chess:setPieces', set: 'mce-chess' }, '*')
// Change board theme
iframe.contentWindow.postMessage({ type: 'chess:setTheme', theme: 'cosmic' }, '*')
// Themes: classic, cosmic, wood, marble, neon, minimal, transparent
Listen on the parent window for game events:
window.addEventListener('message', (e) => {
switch (e.data.type) {
case 'chess:ready':
// { variant, fen }
break
case 'chess:move':
// { from, to, fen, variant }
break
case 'chess:status':
// { text: 'white'|'black'|'draw', gameOver: true, variant }
break
}
})
Headless SDK for server-side tools, APIs, and direct browser consumption.
import {
listVariants,
createGame,
getLegalMoves,
analyzePosition,
getGameStatus,
renderSvg,
createAI,
} from 'moddable-engine/play/sdk'
Returns all registered variants with metadata.
const all = listVariants() // 100 variants
// [{ key, label, group, board, description, rule }]
Returns all legal moves in algebraic notation.
const moves = getLegalMoves('standard') // starting position: 20 moves
// [{ from: 'e2', to: 'e4', capture: false, promotion: null, flag: null }]
AI evaluation with best move.
const result = analyzePosition('standard', null, 4)
// { bestMove: 'e2e4', evaluation: 0.3 }
Renders a board position as an SVG string. No DOM required.
const { svg, rows, cols } = renderSvg('capablanca')
// svg = '<svg xmlns="..." ...' (14KB, self-contained)
const custom = renderSvg('standard', 'rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq - 0 1')
const status = getGameStatus('standard', 'some-fen')
// { status: 'active', gameOver: false, turn: 'white', inCheck: false }
// or: { status: 'checkmate', gameOver: true }
const game = createGame('chess', 'atomic')
const moves = game.getLegalMoves()
game.applyMove(moves[0])
100 variants registered and playable. Use the key as the variant URL parameter or SDK argument.
| Key | Board | Distinguishing rule |
|---|---|---|
standard | 8×8 | Standard FIDE rules |
absorptionChess | 8×8 | Capturing piece gains the victim's movement abilities |
almost-chess | 8×8 | White's Queen replaced by a Chancellor (R+N) |
amazon-chess | 8×8 | Queens replaced by Amazons (Q+N) |
andernachChess | 8×8 | Capturing piece changes colour |
antichess | 8×8 | Captures mandatory; first to lose all pieces wins |
asean | 8×8 | Southeast Asian rules: Makruk-family pieces, rank-6 promotion |
atomic | 8×8 | Captures explode all adjacent non-pawn pieces |
benedictChess | 8×8 | No captures; attacked enemies convert to your colour |
berolinaChess | 8×8 | Pawns move diagonal, capture straight (inverted) |
berserkChess | 8×8 | Check grants one bonus move |
birds-chess | 8×10 | 1874 variant with Guard (R+N) and Equerry (B+N) |
breakthrough | 7×7 | Pawns only; first to reach the far rank wins |
brusky | hex | Horizontal 84-hex board, 10 pawns per side |
byzantine-chess | 4×16 | Circular board, Shatranj pieces, stalemate wins |
capablanca | 8×10 | Archbishop (B+N) and Chancellor (R+N) on a wider board |
carrera | 8×10 | 1617 variant with Champion (R+N) and Centaur (B+N) |
chancellor-chess | 9×9 | Chancellor (R+N) added on 9×9 |
chaturanga | 8×8 | Ancient Indian ancestor; weak counsellor, leaping elephant |
checklessChess | 8×8 | Cannot give check unless it is checkmate |
chess960 | 8×8 | Randomised back rank (960 positions) |
chigorin | 8×8 | White's Bishops replaced by Knights |
circular-chess | 4×16 | 64 squares in 4 concentric rings; no castling |
codrus | 8×8 | Win by getting your own King captured |
courier | 8×12 | Medieval 1202 variant on a 12-wide board |
crazyhouse | 8×8 | Captured pieces switch sides and can be dropped |
cylinder-chess | 8×8 | a-file wraps to h-file |
darkChess | 8×8 | Total fog; only see your own pieces' squares |
de-vasa | hex | 81-hex rhombus board |
diana | 6×6 | No queens or knights; bishops and rooks only |
diceChess | 8×8 | Die roll constrains which piece type must move |
displacementChess | 8×8 | Swap positions with adjacent friendly pieces |
duckChess | 8×8 | Place a blocking duck on any empty square after each move |
einsteinChess | 8×8 | Non-capturing moves demote; captures promote |
empire | 8×8 | Asymmetric: FIDE Kingdom vs Empire dynasty |
endgame-chess | 8×8 | Kings and pawns only from move one |
extinction | 8×8 | Lose any piece type entirely and you lose |
fiveCheck | 8×8 | Five checks wins |
fogOfWar | 8×8 | See only squares your pieces can move to |
giveaway | 8×8 | Forced captures; stalemate is a loss |
glinski | hex | 91-cell hexagonal board; three bishops per side |
grand | 10×10 | Marshal and Cardinal; pawns start on rank 3 |
gridChess | 8×8 | Moves must cross a 2×2 grid line |
half-chess | 4×8 | 4-rank board; armies start adjacent |
hexapawn | 3×3 | Six pawns; reach far rank wins |
hoppel-poppel | 8×8 | Knights capture like bishops; bishops capture like knights |
horde | 8×8 | White: full army vs Black: 36 pawns |
immunizationChess | 8×8 | Captures grant adjacent enemies temporary immunity |
janus | 8×10 | Two Januses (B+N) on a 10-wide board |
khans-chess | 8×8 | Asymmetric: FIDE vs Mongol Horde with divergent movers |
kingOfTheHill | 8×8 | King reaching d4/d5/e4/e5 wins instantly |
knightmate | 8×8 | Knight is royal; Kings move as knights |
leganChess | 8×8 | Berolina pawns; King and Queen swap squares |
los-alamos | 6×6 | First computer chess (1956); no Bishops |
madrasiChess | 8×8 | Same-type opposing pieces paralyse each other |
maharaja | 8×8 | One Amazon (Q+N) vs a full army |
makpong | 8×8 | King cannot move out of check; must block or capture |
makruk | 8×8 | Thai chess; Khon moves 5 directions, rank-6 promotion |
marseillais | 8×8 | Two moves per turn (except White's first) |
mccooey | hex | 91-hex board; diagonal pawn captures, stalemate draws |
medusaChess | 8×8 | Queen petrifies attacked enemies for 2 turns |
mini-hexchess | hex | 37-hex board; no Queen piece |
minichess | 5×5 | Gardner's 5×5 with all piece types |
monsterChess | 8×8 | White moves twice but starts with only K, R, pawns |
nightrider | 8×8 | Knights replaced by Nightriders (chained knight-leaps) |
no-castling | 8×8 | Standard chess without castling |
noRetreat | 8×8 | Pieces cannot move toward their own starting rank |
oblong-chess | 16×4 | Historical 4×16 board; Shatranj pieces |
omnicide | 8×8 | Lose all pieces to win; captures NOT forced |
orda-chess | 8×8 | Asymmetric: standard vs Horde with divergent movers |
orda-mirror | 8×8 | Both players use the Horde army |
ouk-chaktrang | 8×8 | Cambodian chess; King may leap as Knight on first move |
patrolChess | 8×8 | Can only capture when defended by a friendly piece |
pawns-only | 8×8 | Kings and pawns only; first to promote wins |
peasants-revolt | 8×8 | White: K+8P vs Black: K+2N+8P |
petty | 5×6 | All piece types on 5×6; single copies |
poisonChess | 8×8 | Capture squares become poisoned for 3 turns |
progressive | 8×8 | Moves escalate: 1, 2, 3, 4... Check ends turn early |
progressive-italian | 8×8 | Progressive; no check until final move of turn |
racingKings | 8×8 | No checks; race your King to rank 8 |
recruitmentChess | 8×8 | Captured piece defects to vacated square |
rifle | 8×8 | Capturing piece stays on its square |
shafran | hex | 70-hex board with 9 files; castling permitted |
shatar | 8×8 | Mongolian chess; mate must include shak; bare king draws |
shatranj | 8×8 | Medieval Islamic chess; bare king and stalemate win |
shatranj-kamil | 10×10 | 10×10 Arabic variant with War Machines |
singleCheck | 8×8 | One check wins |
sittuyin | 8×8 | Burmese chess; placement opening phase |
stalemate-wins | 8×8 | Stalemate is a win instead of a draw |
suicideChess | 8×8 | Forced captures; stalemate is a draw |
teleportChess | 8×8 | 3 teleports per game; move any piece to any empty square |
threeCheck | 8×8 | Three checks wins |
toroidal-byzantine | 4×16 | Circular board with radial wraparound |
toroidal-chess | 8×8 | All four edges wrap (torus topology) |
torpedo | 8×8 | Pawns can double-step from any rank |
turkish-great-chess-iii | 12×12 | Ottoman 12×12 with multiple rook-type pieces |
turkish-great-chess-iv | 14×14 | 14×14 with mixed Indian/Persian piece names |
upside-down | 8×8 | Pieces start on the opponent's back rank |
weakChess | 8×8 | Weakest piece type must move first |
wildebeest | 10×11 | Camels and Wildebeests (N+Camel) on 11×10 |
| Level | Depth | Notes |
|---|---|---|
| beginner | 1 | Random with basic blunder avoidance |
| easy | 2 | Simple evaluation, no deep tactics |
| medium | 3 | Full evaluation, opening book, captures extended |
| hard | 4 | Transposition table, quiescence search |
| expert | 5+ | Full search with iterative deepening |
1,557 puzzles available as static JSON at /api/puzzles/index.json.
// Fetch puzzle data
const res = await fetch('https://engine.moddable.games/api/puzzles/index.json')
const data = await res.json()
// data.standard = [{ id, fen, solution, rating, themes }]
// data.variants = [{ id, variant, fen, solution, rating }]
// data.meta = { count: 1557, standard: 606, variants: 951 }