This was another goofy Claude Code experiment. This one is actually playable though:

The Game

Pretty simple. It's a "blindfold chess" simulation with 3 modes for "normal", "half-blindfold" and "fully-blindfold". The "half-blindfold" variant shows pieces, but they all look identical. The "full-blindfold" variant doesn't show the pieces at all, just the most recent move.

I wouldn't say it's 100% polished. But it actually works pretty well.

Personally, I can barely handle regular chess, so I was always amazed that people have been playing blindfolded for hundreds of years. Here is a recent exhibition game by two of the best players in the world.

I don't think you could actually TRAIN with this. But it was fun to make.

Project Setup

The final result was a standalone HTML page that loads a few Javascript libraries from public CDNs, with no backend.

I started like a typical frontend/backend project. Originally, I thought that I would need the full NodeJS machinery to package and compile javascript, but it was more convenient to use CDN links on the frontend, so the backend became redundant. Near the end of the project, I inlined everything into the index.html file to simplify delivery.

Below is how things looked mid-way through:

Frontend
- Code (index.html, main.js, style.css)
- Images (pieces/[name].png)

Backend
- Node
- Express JS

Environment
- Docker (Ubuntu 24.04)

Testing
- Playwright

Libraries
- chess.js
- chessboard.js

Mostly the project went smoothly, but I hit a few snags.

Toggling 2 Sets of Pieces

The "half-blindfold" mode uses a separate set of icons, where all the pieces are identical circles. This allows the player to know the locations of the pieces, but without revealing which piece is which. It's easier than "real" blindfolded chess, but still pretty hard!

I really like this feature! So maybe 60 seconds after thinking of it, I had finished typing a prompt asking Claude to implement it.

What I didn't think of is that "swapping pieces" might not be supported by chessboard.js

Claude tried for awhile, but seemed stuck. I got the brainwave here that "oh, swapping pieces MIGHT actually be impossible" (with this library). I'm still not sure. But I suggested a workaround: use two different boards and toggle the visibility. Claude liked this idea and ran with it. Problem solved.

Using Playwright from Docker

I knew that I wanted Claude Code running inside of docker, but that a "real" browser would be needed for accurate unit tests, to ensure correct javascript rendering. Claude can't "see" what's happening on a webpage, except by executing code or taking screenshots using something like Playwright or Selenium.

The place where Playwright really shines is when something is wrong visually.

For example, at one point, the "quiz section" was too wide. I wanted both the "quiz section" and chess board to have the same width of 512px. I asked Claude to fix it, but nothing changed. Then I asked Claude to use Playwright to inspect the width of both elements. It saw the problem immediately and fixed it with a 1 line change to the CSS box-sizing property.

There are several ways to do Docker + Playwright. But basically, you've got:

  • headless browser, inside docker
  • real browser, on the host, with remote access

The first option is more secure, but it's a pain to fully setup so the browser is visible. The second is less secure, but easier. Once it's working, you can prompt stuff like "Use playwright to verify there are 32 visible chess pieces", and Claude will write a bunch of javascript to test this behavior against the live browser.

No big issues. Claude is very good at scripting browser interactions via Playwright.

Fetching Images for the Pieces

Where to actually GET the chess pieces is an interesting question.

Turns out, they exist in the github repo. I didn't know that. Claude didn't know that either.

The project used the CDN version of the library from unpkg.com. The pieces aren't delivered inside that javascript file, so Claude didn't "discover" them automatically. It did try to guess the image URLs for the CDN, but failed. Then it looked inside the npm package, but didn't find images there either (a false negative?). After that, Claude wanted to try downloading pieces from Wikipedia. I rejected that idea. It also spent awhile looking for different libraries and SVG resources.

Then, Claude did something quite clever:

convert -size 45x45 xc:transparent \
  -font DejaVu-Sans \
  -pointsize 40 \
  -fill black \
  -gravity center \
  -annotate +0+0 \
  '♞' \
  img/pieces/bN.png

It conjured each image from scratch, using ImageMagick to draw Unicode symbols as text over a transparent PNG. That's a neat trick.

It worked because Unicode has symbols for chess pieces, for example:

  • U+265E

This was the one "Wow, I would never have thought of that" moment for the project.

Buttons and Testing with Data Attributes

For both the "mode" selection and also the "quiz" panel, Claude originally implemented the hide/show and button state management entirely using HTML data attributes.

That choice made the unit tests difficult to write and very brittle. It's the classic problem that reactive frameworks like Angular, Vue and React solved in the mid-2010s. The program state (what to show/hide, which buttons are active) was scattered across the DOM, rather than being centralized into a single, authoritative data model.

I noticed this issue when Claude got stuck and spent 10+ minutes wrangling the same 2-3 broken tests. After reading a summary of the problem and quickly eyeballing the code, I asked for all program state to be extracted and centralized. Afterwards, Claude was able to identify and quickly fix the remaining problems.

In the future, I would probably opt for a framework. I really wanted to minimize external dependencies here. But a framework would have avoided the state management problems altogether.

Junk Files and Code Cleanup

Some junk was still left over after the primary coding task was complete:

  • unused image files
  • random testing *.js files
  • unused dependencies in package.json

That's understandable. Claude doesn't necessarily "know" that you're done with a file (maybe you had plans for those testing files, later on). Asking it to scan for defunct and unused files fixed maybe 80% of this. A few still needed to be addressed individually.

One of the last things I asked for was to carefully inspect the code and look for opportunities to improve organization and maintainability. This resulted in a 20+ item TODO list. Mostly it was mechanical changes like extracting helper functions. But this really improved the readability a LOT. I need to figure out the best way to make that automatic in the future, whether it's through CLAUDE.md, hooks, skills or something else.

Inlining PNGs

Late in the project, I decided to inline all the CSS and Javascript into the index.html file, so that I'd have fewer files to deliver on the CDN. PNGs can be inlined too by using data uris.

Claude was able to generate the inline code just fine the first time. But later, I decided to swap in a different set of icons, and it got stuck twice doing a find/replace on the html. I'm still not sure why. So I copy/pasted that one substitution myself.

Conclusion

How'd we do? As usual now, I was impressed with Claude's ability. It did get stuck in a few places on this project, but those were easily corrected. Claude has also been a little crash-y during the last few days. This might be due to large cache files, but I haven't dug into it much.

Using Playwright seems super powerful. I also learned a new trick with ImageMagick and Unicode. I'm getting a big kick out of the famous --dangerously-skip-permissions flag. After learning about hooks, I've even got a system bell that dings everytime Claude stops. That's been super helpful, so that I can do other things in the foreground while cranking continues.