♟️ The Story Behind ChessLens
💡 Inspiration
ChessLens was inspired by my own experience trying to improve at chess.
I enjoy watching GothamChess because of the way he explains games.
He does not simply say that a move changes the evaluation from +1.2 to
-2.5. He explains what actually went wrong, what the player was
trying to achieve, and why a position suddenly became difficult.
Then I would go back to playing chess and immediately play terribly.
The problem was not the lack of learning material. There is an enormous amount of chess content available online. The problem was that, as a beginner, most of it felt overwhelming and difficult to apply to my own games.
Traditional chess analysis often gives information like:
- An evaluation bar
- Best move suggestions
- Engine lines
- Opening names
- Tactical variations
For an experienced player, this information is extremely useful. For a beginner, seeing:
Mistake
Evaluation: +0.7 → -1.8
Best Move: Nxd4
does not always answer the most important question:
Why was my move bad?
I wanted something closer to having a chess coach sitting beside me and explaining the important moments of my game in simple language.
That idea became ChessLens.
🎯 What We Wanted to Build
Our goal was to make chess analysis easier to understand for beginner and intermediate players.
We did not want to build another interface that simply displayed Stockfish output.
Instead, we wanted to combine the calculation strength of a chess engine with the communication ability of a large language model.
The basic idea was:
Stockfish finds what happened.
↓
Gemini understands the structured engine data.
↓
Gaster explains it to the player.
This became the foundation of ChessLens.
Our AI coach, Gaster, focuses on explaining important moments instead of commenting on every single move.
If a move barely changes the position, the player does not need a paragraph of AI-generated text. However, if a move causes a major evaluation swing, that is a moment worth understanding.
Conceptually, we detect significant changes using the difference between consecutive engine evaluations:
$$ \Delta E = E_{\text{after}} - E_{\text{before}} $$
We also calculate the magnitude of the evaluation swing:
$$ S = |\Delta E| $$
A sufficiently large swing can indicate a critical moment in the game.
This allows ChessLens to focus its coaching on the positions that actually matter.
🏗️ How We Built ChessLens
ChessLens was built using the MERN stack, with Stockfish and Gemini integrated into the analysis pipeline.
Frontend
The frontend was built using:
- React
- Vite
- Tailwind CSS
- React Router
- TanStack Query
- React Chessboard
- Chess.js
The chessboard is fully interactive and supports move navigation, legal move highlighting, live play, check detection, and historical position viewing.
Backend
The backend uses:
- Node.js
- Express.js
- MongoDB
- Mongoose
We separated the backend into routes, controllers, and services so that PGN parsing, engine analysis, evaluation processing, and AI generation remained independent.
The general architecture is:
React Frontend
↓
Express API
↓
Chess Analysis Services
↓
Stockfish
↓
Evaluation Processing
↓
Critical Move Detection
↓
Gemini
↓
Gaster Commentary
🔍 Building the Game Analysis Pipeline
The first major technical challenge was converting a complete chess game into data that could be analyzed move by move.
Users provide a game in PGN format.
We use Chess.js to parse the PGN and reconstruct every board position.
The pipeline begins as:
PGN
↓
Parse Moves
↓
Generate Position History
↓
Store FEN Before and After Each Move
Each move contains information such as:
Ply
Played Move
FEN Before
FEN After
Player Who Moved
We then send each position to Stockfish.
Stockfish returns an evaluation and a principal variation containing its preferred continuation.
For every move, we compare the evaluation before and after the move.
This allows us to calculate how much value the player lost from their perspective.
For White, evaluation loss can be represented as:
$$ L_{\text{white}} = \max(0, E_{\text{before}} - E_{\text{after}}) $$
For Black:
$$ L_{\text{black}} = \max(0, E_{\text{after}} - E_{\text{before}}) $$
This distinction is important because Stockfish evaluations are generally represented from White's perspective.
Using this data, ChessLens identifies mistakes, major evaluation swings, and strong moves.
🤖 Building Gaster
One of the most important decisions we made was not allowing Gemini to freely analyze chess positions.
Large language models can generate explanations that sound extremely convincing while describing tactics that do not actually exist.
For a chess learning platform, this would be a major problem.
Instead, Stockfish acts as the source of chess truth.
Gemini receives structured information such as:
{
"playedMove": "Ke7",
"bestMove": "Kd8",
"evaluationBefore": -0.58,
"evaluationAfter": 0.81,
"evaluationSwing": 1.39,
"eventType": "mistake",
"principalVariation": [
"Kd8",
"Ng4",
"Nh6",
"c3",
"Nc2+"
]
}
Gaster is instructed to explain only the supplied Stockfish analysis.
The responsibilities are intentionally separated:
Stockfish → Calculate chess
Gemini → Explain chess
This helped us create explanations that are both understandable and grounded in engine analysis.
🎮 Building Play From Here
One feature we especially wanted was the ability to experiment with a position.
While studying an opening or reviewing a game, users can select Play From Here.
ChessLens then creates a playable chess position from the current FEN.
The player can continue making legal moves from that exact position.
Every new move is analyzed using a separate live analysis endpoint:
Player Move
↓
FEN Before Move
↓
FEN After Move
↓
Stockfish Analysis
↓
Evaluation Comparison
↓
Critical Event Detection
↓
Optional Gaster Commentary
Importantly, the chessboard does not wait for Gaster.
The user can continue playing while Stockfish and Gemini process previous moves in the background.
If an important event is detected, Gaster's explanation appears when the analysis is complete.
📜 Live Move History
Live play introduced another problem.
Once the player had made several moves, there was no way to return to an earlier position and understand what Gaster had said at that moment.
We added a live position history.
Each played move creates a new history entry containing the board position and move information.
Commentary is cached against the corresponding live history position.
This means the user can navigate backward through their live game and see:
- The board at that moment
- The move that was played
- Gaster's commentary for that move
- The evaluation associated with the position
When viewing an earlier position, the board is temporarily locked. Moving back to the latest position allows the player to continue the game.
🚧 Challenges We Faced
Stockfish Output Is Not Beginner-Friendly
Stockfish provides extremely useful information, but its raw UCI output is designed for software and experienced chess tools.
We had to parse engine information, normalize centipawn evaluations, handle mate scores, extract the best move, and convert principal variations into usable move sequences.
The engine output had to become structured data before Gaster could explain it.
Evaluation Perspective
Stockfish evaluations are generally represented from White's perspective.
This created problems when calculating whether a move was good or bad for Black.
A positive evaluation change may be good for White but bad for Black.
We had to calculate evaluation loss based on the player who made the move instead of simply using the absolute evaluation difference.
AI Hallucination
During early testing, AI-generated explanations could occasionally describe ideas beyond the supplied engine line.
The explanations sounded believable, which made the issue more dangerous.
We changed our prompting strategy so that Gemini receives structured Stockfish data and is explicitly instructed not to invent tactics, variations, or continuations outside the supplied principal variation.
Gemini Availability and Latency
AI requests introduced network latency and occasional service availability issues.
During testing, Gemini sometimes returned temporary 503 UNAVAILABLE
responses because of high model demand.
We designed live play so the board remains interactive while analysis happens asynchronously.
The player sees a small Gaster is analyzing indicator but can continue playing.
Managing Live State
Play From Here became one of the most technically challenging parts of the frontend.
We had to manage:
- The original PGN position history
- The current viewer position
- The live Chess.js game
- Live move history
- Historical live positions
- Cached commentary
- Asynchronous analysis responses
We also encountered React hook-order errors while restructuring the chess viewer.
Keeping hooks at the top level and separating live state management into
the useChessViewer hook helped stabilize the component.
Keeping the Experience Simple
The largest product challenge was deciding what not to show.
Stockfish can provide enormous amounts of information.
Showing every engine line and commenting on every move would recreate the same overwhelming experience that originally inspired ChessLens.
We deliberately focused on:
- Important evaluation changes
- Short explanations
- Visual evaluation feedback
- Interactive exploration
The goal was not to expose every calculation Stockfish makes.
The goal was to explain the moments a beginner should care about.
📚 What We Learned
Building ChessLens taught us that combining AI with a traditional algorithmic system works best when each system has a clearly defined responsibility.
Stockfish is far better at calculating chess positions than a general-purpose language model.
Gemini is far better at converting structured information into natural explanations.
Using either system alone would not create the experience we wanted.
Together, they form a stronger pipeline:
Specialized Engine
+
Structured Data
+
Language Model
=
Understandable Analysis
We also learned a lot about asynchronous frontend state, chess position representation, the UCI protocol, prompt design, and building systems around AI latency rather than assuming AI responses are immediate.
Most importantly, we learned that adding more information does not always make a learning tool better.
For beginners, the right explanation at the right moment is often more useful than showing every possible line.
♟️ Final Thoughts
ChessLens started from a simple frustration: I could watch chess explanations for hours, open a game, play badly, and then stare at an engine evaluation without understanding what I had actually done wrong.
We wanted to build the kind of analysis tool we would personally use while learning chess.
ChessLens combines Stockfish's calculation strength with Gaster's explanations to help players understand their games, explore positions, and learn from the moments that actually changed the game.
See the game. Understand the move.
P.S. We discovered a deployment issue with the native Stockfish engine shortly after submission. As a result, AI-powered analysis may not function correctly on the hosted version, although it works in our local development environment. We apologize for the inconvenience and appreciate your understanding.
Built With
- chess.js
- express.js
- gemini
- mongodb
- node.js
- react
- stockfish
Log in or sign up for Devpost to join the conversation.