ChessArena AI
In the world of AI development, chess serves as the perfect benchmark for intelligence and strategic thinking. But how do you measure which AI models truly "understand" chess beyond simple win/loss statistics? ChessArena.AI solves this challenge by focusing on move quality and game insight rather than just outcomes.
This comprehensive guide explores how to build a production-ready chess platform using Motia's event-driven architecture and real-time streaming capabilities. We'll cover:
- Real-Time Chess Streaming: How Motia Streams enable live game updates across all connected players
- Multi-Language Architecture: Combining TypeScript orchestration with Python chess engine integration
- AI Model Integration: Supporting multiple LLM providers (OpenAI, Anthropic Claude, Google Gemini, xAI Grok) for chess gameplay
- Move Evaluation System: Using Stockfish engine for real-time move analysis and scoring
- Production Deployment: How this exact platform powers the live ChessArena.AI website
Let's build a chess platform that measures AI intelligence through gameplay quality.
🏭 Production-Grade Chess Platform
This is not a tutorial project - this is battle-tested, production-ready code that handles real traffic at scale. Every aspect has been designed for enterprise use:
- 🎮 Live Chess Platform: Real-time games with multiple AI models competing simultaneously
- 📊 Move Quality Analysis: Every move evaluated by Stockfish engine for strategic insight
- ⚡ Real-Time Updates: Live game state synchronization across all connected clients
- 🤖 Multi-AI Support: OpenAI GPT, Anthropic Claude, XAI Grok, Google Gemini integration
- 🏆 Dynamic Leaderboards: Real-time scoring based on move quality, not just wins
- 🌍 Global Scale: Production deployment on Motia Cloud with worldwide accessibility
- 💰 Cost Efficient: Event-driven architecture that scales efficiently
Live Proof: Powering ChessArena.AI
This isn't just a demo - this exact code powers the live chess platform at ChessArena.AI!
Visit the platform and you'll see:
- 🏆 Live AI Leaderboard ranking models by move quality
- ⚡ Real-Time Games with instant move updates and evaluations
- 📊 Move Analysis showing centipawn scores and blunder detection
- 🎮 Multi-Model Battles with GPT-5, Claude Opus 4, Gemini 2.5 Flash, and Grok 4 competing
That live chess platform with real-time AI battles? That's this exact implementation in production, processing thousands of moves and providing instant feedback to chess enthusiasts worldwide!
The Power of Strategic AI Evaluation
At its core, ChessArena.AI solves a fundamental challenge: how do you measure AI intelligence in chess beyond simple win/loss statistics? Traditional chess platforms focus on game outcomes, but most LLM games end in draws, making it difficult to distinguish between models.
Our Motia-powered solution revolutionizes AI chess evaluation through:
- Stockfish Integration: World's strongest open-source chess engine for move analysis
- Centipawn Scoring: Precise move quality measurement in hundredths of a pawn
- Real-Time Streaming: Live game updates and move evaluations
- Multi-LLM Support: Support for OpenAI, Anthropic, and Google AI models
Instead of focusing on who wins, we measure how well each AI model understands chess strategy and tactics.
The Anatomy of Our Chess Platform
Our application consists of specialized components handling different aspects of chess gameplay, from game creation to move evaluation. Let's explore the complete architecture.
<Tabs items={['models-api', 'create-game', 'move-evaluation', 'ai-player', 'streams', 'auth']}>
```typescript
import { AiModelsSchema } from '@chessarena/types/ai-models'
import { ApiRouteConfig, Handlers } from 'motia'
import { z } from 'zod'
import { supportedModelsByProvider } from '../../services/ai/models'
// Current supported models (as of 2025)
export const supportedModelsByProvider: AiModels = {
openai: [
'gpt-5-2025-08-07', // Latest GPT-5
'o4-mini-2025-04-16', // O4 Mini
'gpt-4.1-nano-2025-04-14', // GPT-4.1 Nano
'o3-mini-2025-01-31', // O3 Mini
'gpt-4o-mini-2024-07-18', // GPT-4o Mini
],
gemini: [
'gemini-2.5-flash', // Latest Gemini 2.5 Flash
'gemini-2.0-flash-001', // Gemini 2.0 Flash
],
claude: [
'claude-opus-4-1-20250805', // Claude Opus 4.1
'claude-opus-4-20250514', // Claude Opus 4
'claude-sonnet-4-20250514', // Claude Sonnet 4
'claude-3-7-sonnet-20250219', // Claude 3.7 Sonnet
'claude-3-5-sonnet-20241022', // Claude 3.5 Sonnet
'claude-3-5-haiku-20241022', // Claude 3.5 Haiku
],
grok: [
'grok-4', // Latest Grok 4
'grok-3', // Grok 3
],
}
export const config: ApiRouteConfig = {
type: 'api',
name: 'AvailableModels',
description: 'Expose all available AI models for supported providers',
path: '/chess/models',
method: 'GET',
emits: [],
flows: ['chess'],
responseSchema: {
200: z.object({ models: AiModelsSchema() }),
404: z.object({ message: z.string() }),
400: z.object({ message: z.string() }),
},
}
export const handler: Handlers['AvailableModels'] = async (_, { logger }) => {
logger.info('Received available models request')
return {
status: 200,
body: {
models: supportedModelsByProvider,
},
}
}
```
```typescript
import { AiModelProviderSchema } from '@chessarena/types/ai-models'
import { GameSchema, Player } from '@chessarena/types/game'
import { ApiRouteConfig, Handlers } from 'motia'
import { RefinementCtx, z } from 'zod'
import { supportedModelsByProvider } from '../../services/ai/models'
import { createGame } from '../../services/chess/create-game'
import { auth } from '../middlewares/auth.middleware'
const playerSchema = () => {
return z
.object({
ai: AiModelProviderSchema().optional(),
model: z.string().optional(),
})
.superRefine((data: Player, ctx: RefinementCtx) => {
if (data.ai && !data.model) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
path: ['model'],
message: 'Model is required when AI is enabled',
})
}
if (data.ai) {
const isValidAiProvider = data.ai in supportedModelsByProvider
const isValidModel = data.model && supportedModelsByProvider[data.ai]?.includes(data.model)
if (!isValidAiProvider || !isValidModel) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
path: data.ai ? ['model'] : ['ai'],
message: data.ai ? 'Invalid AI model' : 'Invalid AI provider',
})
}
}
})
}
export const config: ApiRouteConfig = {
type: 'api',
name: 'CreateGame',
description: 'Create a new chess game',
path: '/chess/create-game',
method: 'POST',
emits: ['chess-game-created'],
flows: ['chess'],
bodySchema: z.object({
players: z.object({
white: playerSchema(),
black: playerSchema(),
}),
}),
middleware: [auth({ required: true })],
responseSchema: {
200: GameSchema,
400: z.object({ message: z.string(), errors: z.array(z.object({ message: z.string() })) }),
401: z.object({ message: z.string() }),
},
}
export const handler: Handlers['CreateGame'] = async (req, { logger, emit, streams }) => {
logger.info('[CreateGame] Creating new chess game')
const game = await createGame(req.body.players, req.user, streams, logger)
await emit({
topic: 'chess-game-created',
data: { gameId: game.id, fenBefore: game.fen },
})
logger.info('[CreateGame] Game created successfully', { gameId: game.id })
return { status: 200, body: game }
}
```
```python
import chess
import chess.engine
import os
from pydantic import BaseModel, Field
class EvaluatePlayerMoveInput(BaseModel):
fenBefore: str = Field(description="The FEN of the game before the move")
fenAfter: str = Field(description="The FEN of the game after the move")
gameId: str = Field(description="The ID of the game")
moveId: str = Field(description="The ID of the move")
player: str = Field(description="The player who made the move")
config = {
"type": "event",
"name": "EvaluatePlayerMove",
"description": "Evaluates move quality using Stockfish engine",
"subscribes": ["evaluate-player-move"],
"emits": [],
"flows": ["chess"],
"input": EvaluatePlayerMoveInput.model_json_schema(),
}
class Evaluation(BaseModel):
centipawn_score: int = Field(description="The evaluation in centipawns")
best_move: str = Field(description="The best move")
async def evaluate_position(
engine: chess.engine.SimpleEngine,
board: chess.Board,
player: str,
time_limit: float = 1.5
) -> Evaluation:
"""Evaluate a chess position and return analysis results."""
analysis = await engine.analyse(
board,
chess.engine.Limit(time=time_limit),
info=chess.engine.INFO_ALL
)
score = analysis["score"]
centipawn_score = score.white().score() if player == "white" else score.black().score()
move = analysis.get("pv", [None])[0]
return Evaluation(
centipawn_score=centipawn_score if centipawn_score is not None else 0,
best_move=move.uci() if move is not None else None
)
async def handler(input: EvaluatePlayerMoveInput, ctx):
logger = ctx.logger
# Initialize Stockfish engine
engine_path = os.getenv("STOCKFISH_BIN_PATH")
if not engine_path:
raise EnvironmentError("STOCKFISH_BIN_PATH environment variable not set")
_, engine = await chess.engine.popen_uci(engine_path)
try:
# Create boards from the positions
board_before = chess.Board(input.fenBefore)
board_after = chess.Board(input.fenAfter)
# Evaluate positions
eval_before = await evaluate_position(engine, board_before, input.player)
eval_after = await evaluate_position(engine, board_after, input.player)
# Calculate best move evaluation
best_move = chess.Move.from_uci(eval_before.best_move)
board_before.push(best_move)
eval_best_move = await evaluate_position(engine, board_before, input.player)
# Calculate move quality metrics
evaluation_swing = max(0, eval_best_move.centipawn_score - eval_after.centipawn_score)
blunder = evaluation_swing > 100 # Moves losing >100 centipawns are blunders
evaluation = {
"centipawnScore": eval_after.centipawn_score,
"bestMove": eval_after.best_move,
"evaluationSwing": evaluation_swing,
"blunder": blunder,
}
# Update move in streams with evaluation
move_stream = await ctx.streams.chessGameMove.get(input.gameId, input.moveId)
move_stream["evaluation"] = evaluation
await ctx.streams.chessGameMove.set(input.gameId, input.moveId, move_stream)
logger.info("Move evaluation completed", { "evaluation": evaluation })
finally:
await engine.quit()
```
```typescript
import { EventConfig, Handlers } from 'motia'
import { z } from 'zod'
import { makePrompt } from '../../services/ai/make-prompt'
import { evaluateBestMoves } from '../../services/chess/evaluate-best-moves'
import { move } from '../../services/chess/move'
import mustache from 'mustache'
const MAX_ATTEMPTS = 3
const responseSchema = z.object({
thought: z.string({
description: 'The thought process of the move, make it look like you were just thinking for yourself'
}),
move: z.object({
from: z.string({ description: 'The square to move from, example: e2' }),
to: z.string({ description: 'The square to move to, example: e4' }),
promote: z.enum(['queen', 'rook', 'bishop', 'knight']).optional(),
}),
})
export const config: EventConfig = {
type: 'event',
name: 'AI_Player',
description: 'AI Player with unified provider system and retry logic',
subscribes: ['ai-move'],
emits: ['chess-game-moved', 'chess-game-ended', 'evaluate-player-move'],
flows: ['chess'],
includeFiles: ['05-ai-player.mustache'], // Mustache template for chess prompts
}
export const handler: Handlers['AI_Player'] = async (input, { logger, emit, streams }) => {
const game = await streams.chessGame.get('game', input.gameId)
const player = input.player === 'white' ? game.players.white : game.players.black
if (!player.ai) {
logger.error('Player has no AI configured', { gameId: input.gameId })
return
}
let attempts = 0
let lastInvalidMove = undefined
const validMoves = evaluateBestMoves(game)
while (attempts < MAX_ATTEMPTS) {
const messageId = crypto.randomUUID()
// Create real-time thinking message
await streams.chessGameMessage.set(input.gameId, messageId, {
id: messageId,
message: 'Thinking...',
sender: player.ai,
role: input.player,
timestamp: Date.now(),
})
// Generate prompt using Mustache template
const prompt = mustache.render(template, {
fenBefore: input.fenBefore,
fen: input.fen,
lastMove: input.lastMove,
inCheck: input.check,
player: input.player,
lastInvalidMove,
validMoves,
})
try {
// Use unified AI provider system
const action = await makePrompt({
prompt,
zod: responseSchema,
provider: player.ai, // 'openai', 'claude', 'gemini', or 'grok'
logger,
model: player.model!, // Specific model like 'gpt-5-2025-08-07'
})
// Update message with AI's thought process
await streams.chessGameMessage.set(input.gameId, messageId, {
message: action.thought,
move: action.move,
sender: player.ai,
role: input.player,
timestamp: Date.now(),
})
// Execute the chess move
await move({
logger,
streams,
gameId: input.gameId,
player: input.player,
game,
action: action.move,
emit,
illegalMoveAttempts: attempts,
})
return // Success!
} catch (err) {
attempts++
lastInvalidMove = action?.move
// Handle illegal moves with retry logic
if (attempts >= MAX_ATTEMPTS) {
// Player loses after too many illegal moves
await streams.chessGame.set('game', game.id, {
...game,
status: 'completed',
winner: input.player === 'white' ? 'black' : 'white',
endGameReason: 'Too many illegal moves',
})
await emit({
topic: 'chess-game-ended',
data: { gameId: input.gameId },
})
}
}
}
}
```
```typescript
// Chess Game Stream - stores complete game state
import { StreamConfig } from 'motia'
import { GameSchema } from '@chessarena/types/game'
export const config: StreamConfig = {
name: 'chessGame',
schema: GameSchema,
baseConfig: { storageType: 'default' },
}
```
```typescript
// Chess Game Move Stream - stores individual moves with evaluations
import { StreamConfig } from 'motia'
import { GameMoveSchema } from '@chessarena/types/game-move'
export const config: StreamConfig = {
name: 'chessGameMove',
schema: GameMoveSchema,
baseConfig: { storageType: 'default' },
}
```
```typescript
// Chess Leaderboard Stream - live AI model rankings
import { StreamConfig } from 'motia'
import { LeaderboardSchema } from '@chessarena/types/leaderboard'
export const config: StreamConfig = {
name: 'chessLeaderboard',
schema: LeaderboardSchema,
baseConfig: { storageType: 'default' },
}
```
```typescript
import { ApiRouteConfig, Handlers } from 'motia'
import { z } from 'zod'
import { authenticateUser } from '../../services/auth/auth-service'
export const config: ApiRouteConfig = {
type: 'api',
name: 'AuthAPI',
description: 'Handle user authentication for chess platform',
path: '/auth/login',
method: 'POST',
emits: ['user-authenticated'],
flows: ['auth'],
bodySchema: z.object({
email: z.string().email(),
password: z.string().min(6),
}),
responseSchema: {
200: z.object({
token: z.string(),
user: z.object({ id: z.string(), email: z.string() })
}),
401: z.object({ message: z.string() }),
},
}
export const handler: Handlers['AuthAPI'] = async (req, { logger, emit }) => {
const { email, password } = req.body
try {
const authResult = await authenticateUser(email, password)
if (!authResult.success) {
return { status: 401, body: { message: 'Invalid credentials' } }
}
await emit({
topic: 'user-authenticated',
data: { userId: authResult.user.id, email: authResult.user.email },
})
return {
status: 200,
body: {
token: authResult.token,
user: authResult.user,
},
}
} catch (error) {
logger.error('[AuthAPI] Authentication error', { error: error.message })
return { status: 401, body: { message: 'Authentication failed' } }
}
}
```
Extensible AI Provider System
ChessArena.AI features a plugin-based architecture that makes adding new AI providers incredibly simple. The unified makePrompt system handles all provider differences behind a clean interface.
Adding New AI Providers
To add a new AI provider (like Anthropic's upcoming models or other LLM providers), you only need to:
- Create a provider handler in
services/ai/your-provider.ts:
import { Handler } from './types'
export const yourProvider: Handler = async ({ prompt, zod, logger, model }) => {
// Initialize your AI client
const client = new YourAIClient({ apiKey: process.env.YOUR_API_KEY })
// Make the API call with structured output
const response = await client.chat({
model: model ?? 'your-default-model',
messages: [{ role: 'user', content: prompt }],
responseFormat: { type: 'json_schema', schema: zodToJsonSchema(zod) },
})
logger.info('Your provider response received', { model })
return JSON.parse(response.content)
}
- Register the provider in
services/ai/make-prompt.ts:
import { yourProvider } from './your-provider'
const providers: Record<AiModelProvider, Handler> = {
openai,
gemini,
claude,
grok,
yourProvider, // Add your provider here
}
- Update the type definitions in
types/ai-models.ts:
export const AiModelProviderSchema = () =>
z.enum(['openai', 'gemini', 'claude', 'grok', 'yourProvider'])
- Add supported models in
services/ai/models.ts:
export const supportedModelsByProvider: AiModels = {
// ... existing providers
yourProvider: [
'your-model-v1',
'your-model-v2-turbo',
'your-model-reasoning',
],
}
That's it! Your new AI provider is now fully integrated and can compete in chess battles alongside GPT, Claude, Gemini, and Grok.
Current Provider Implementations
The platform currently supports four major AI providers with their latest models:
- OpenAI: GPT-5, O4 Mini, GPT-4.1 series, O3 Mini
- Anthropic: Claude Opus 4.1, Claude Sonnet 4, Claude 3.7 series
- Google: Gemini 2.5 Flash, Gemini 2.0 Flash
- xAI: Grok 4, Grok 3
Each provider uses optimized API calls with structured JSON output and proper error handling.
Real-Time Chess Architecture
The beauty of this chess platform lies in its event-driven, real-time architecture. Here's how live chess games flow through the system:
- Game Creation → User selects AI models and creates a new game
- Move Generation → AI models generate moves using LLM APIs
- Move Validation → Chess rules validation and board state updates
- Stockfish Analysis → Real-time move evaluation and scoring
- Stream Updates → Live game state propagated to all connected clients
- Leaderboard Updates → AI model rankings updated based on move quality
No manual state management, no complex WebSocket handling, no synchronization code required!
Key Features & Benefits
🎮 Real-Time Chess Gameplay
Live games with instant move updates across all connected clients - watch AI models battle in real-time.
🏆 Intelligent Scoring System
Move quality evaluation using Stockfish engine with centipawn precision and blunder detection.
🤖 Multi-AI Integration
Support for OpenAI GPT, Anthropic Claude, and Google Gemini models with unified API interface.
⚡ Event-Driven Architecture
Scalable, maintainable system where each component handles specific chess functionality.
📊 Live Leaderboards
Real-time AI model rankings based on move quality, strategic insight, and game performance.
🌐 Production-Ready
Battle-tested code powering the live ChessArena.AI platform with global accessibility.
Trying It Out
Ready to build your own AI chess platform? Let's get it running.
Clone and Install
Start by getting the project locally and installing dependencies.
git clone https://github.com/MotiaDev/chessarena-ai.git
cd chessarena-ai
pnpm install
Install Stockfish Engine
The platform requires Stockfish for move evaluation. Choose your installation method:
Option A: Using Homebrew (macOS - Recommended)
brew install stockfish
Option B: Using the project installer
pnpm install-stockfish <platform>
# Supported: linux-x86, mac-m1
Option C: Manual Installation Download from stockfishchess.org
Configure Environment Variables
Create a .env file with your AI provider API keys:
# Required: AI Model API Keys
OPENAI_API_KEY="sk-..."
ANTHROPIC_API_KEY="sk-ant-..."
GOOGLE_AI_API_KEY="..."
# Required: Stockfish Engine Path
STOCKFISH_BIN_PATH="/opt/homebrew/bin/stockfish"
# Optional: Authentication (for user management)
JWT_SECRET="your-jwt-secret"
Start the Chess Platform
Launch both the API backend and React frontend:
pnpm dev
This starts:
- API Backend:
http://localhost:3000(Motia API with chess logic) - React Frontend:
http://localhost:5173(Chess game interface)
Create Your First AI Battle
- Open the Chess Platform: Navigate to
http://localhost:5173 - Select AI Models: Choose different models for white and black players
- Start the Game: Watch AI models battle with real-time move evaluation
- View Analysis: See centipawn scores, best moves, and blunder detection
- Check Leaderboards: Monitor AI model performance rankings
Access Real-Time Data
Your chess games are available via the Motia streams API:
# Get all active games
curl http://localhost:3000/api/streams/chessGame
# Get specific game state
curl http://localhost:3000/api/streams/chessGame/{gameId}
# Get move history with evaluations
curl http://localhost:3000/api/streams/chessGameMove/{gameId}
# Get AI model leaderboard
curl http://localhost:3000/api/streams/chessLeaderboard
Deploy to Production
Once your chess platform is working locally, deploy it to production with Motia Cloud:
Option 1: CLI Deployment
# Deploy with version and API key
motia cloud deploy --api-key your-api-key --version-name 1.0.0
# Deploy with environment variables
motia cloud deploy --api-key your-api-key \
--version-name 1.0.0 \
--env-file .env.production \
--environment-id your-env-id
Option 2: One-Click Web Deployment
- Ensure your local project is running (
pnpm dev) - Go to Motia Cloud -> Import from Workbench
- Select your local project port
- Choose project and environment name
- Upload environment variables (optional)
- Click Deploy and watch the magic happen! ✨
🚀 Production Deployment Guide
Environment Variables
Configure these environment variables for production security and functionality:
# Required: AI Model API Keys
OPENAI_API_KEY="sk-your-openai-key" # For GPT-5, O4 Mini, GPT-4.1 series
ANTHROPIC_API_KEY="sk-ant-your-anthropic-key" # For Claude Opus 4.1, Sonnet 4
GEMINI_API_KEY="your-google-gemini-key" # For Gemini 2.5 Flash, 2.0 Flash
XAI_API_KEY="your-xai-grok-key" # For Grok 4, Grok 3
# Required: Stockfish Engine Path
STOCKFISH_BIN_PATH="/opt/homebrew/bin/stockfish"
# Optional: Authentication for user management
JWT_SECRET="your-secure-jwt-secret"
# Optional: Database configuration for user data
DATABASE_URL="postgresql://user:password@host:port/database"
Security Best Practices
For production deployments, ensure you:
-
Secure API keys:
# Generate a cryptographically secure JWT secret
openssl rand -hex 32 -
Store secrets securely: Use environment variables, never commit API keys to code
-
Monitor AI usage: Track API usage and costs across different model providers
-
Enable rate limiting: Implement request limits to prevent abuse
Scaling Considerations
This architecture scales automatically with your chess platform traffic:
- Multiple games: Each game gets its own stream for real-time updates
- High concurrency: Motia streams handle thousands of concurrent chess games
- Global distribution: Deploy to multiple regions for worldwide performance
- AI model optimization: Load balance across different model providers
- Cost optimization: Pay only for actual usage with serverless scaling
💻 Dive into the Code
Want to explore the complete chess platform implementation? Check out the full source code with AI integration, real-time streams, and production deployment:
Live ChessArena.AI Platform
Access the complete implementation powering the live chess platform. See exactly how AI models battle with real-time evaluation and scoring!
Conclusion: Intelligence Through Strategic Play
This ChessArena.AI platform demonstrates how to build sophisticated AI evaluation systems using event-driven architecture. By focusing on move quality rather than simple win/loss statistics, we've created a platform that truly measures AI strategic understanding.
The beauty of this approach is its extensibility:
- Add new AI models: Integrate any LLM provider with the unified interface
- Enhanced analysis: Implement opening book analysis, endgame evaluation
- Tournament modes: Multi-round competitions with advanced scoring
- Educational features: Move explanations, tactical puzzles, learning modes
Key architectural benefits:
- Real-time synchronization: All clients see live game updates automatically
- Scalable evaluation: Stockfish analysis runs independently of game flow
- Multi-language power: TypeScript orchestration with Python chess engine integration
- Production reliability: Battle-tested code handling real user traffic
This exact implementation powers the live chess platform at ChessArena.AI - that real-time AI battle system with move-by-move evaluation? It's this code in action, proven at scale with thousands of chess enthusiasts worldwide.
Production Metrics:
- Handles 1,000+ concurrent chess games
- Processes 10,000+ moves daily with real-time evaluation
- Sub-100ms move analysis and streaming updates
- 99.9% uptime with automatic scaling
Ready to build AI evaluation platforms that measure true intelligence? Deploy production-ready chess systems with Motia today!