Building a Space Invaders Easter Egg With Claude Code.

Building a Space Invaders Easter Egg With Claude Code

This evening I had a silly ide: what if my homepage had a hidden arcade game? Click a button, and the page turns into Space Invaders – where the actual text on the page becomes the enemies you shoot.

I built it with Claude Code in about 30 minutes across three iterations. I used Opus as the model with effort set to high and Plan mode enabled for each iteration. The result is a fully playable game: chiptune music, CRT scanlines, neon glow effects, explosions – the whole 80s arcade aesthetic, in ~560 lines of vanilla JavaScript with zero external dependencies.

Here’s how it went.

Iteration 1: The initial prompt

My prompt was intentionally loose:

On the main landing page I’d like to have a button on the bottom that reads ‘play’. If clicked the page turns into a game with a small spaceship on the bottom that can shoot. If a shot hits a character that character is removed and an explosion happens. Only JS, no external dependencies. Simple and small as possible. 80s arcade look. Very simple 80s arcade style music is fine.

With Plan mode enabled, Claude Code didn’t jump straight into writing code. It first explored my Hugo site structure – understood the theme, templates, asset pipeline, and how the homepage renders. It then wrote a detailed implementation plan covering file placement, game architecture, audio design, and visual effects. Only after I reviewed and approved the plan did it start coding, producing two files:

  1. layouts/index.html – a Hugo template override that loads the game script via the existing footer_js block. Clean integration, no changes to any existing file.
  2. assets/js/space-invaders.js – the entire game in a single self-contained IIFE.

The clever part was how it extracted the “invaders”: on game start, it walks the DOM tree, isolates every visible character using the Range API, captures each character’s exact screen position via getBoundingClientRect(), then draws them on a full-screen canvas overlay. Your homepage text literally becomes the enemy formation.

The game had everything from the first version:

  • Ship movement (arrow keys + touch)
  • Shooting with cooldown
  • AABB collision detection
  • Particle explosions with color cycling
  • CRT scanlines and vignette
  • Chiptune music via Web Audio API oscillators
  • Win/lose screens

hugo --minify passed on the first try.

Iteration 2: Two refinements

Playing it revealed two issues:

The button was too prominent. It was a big centered block above the footer – not exactly “hidden Easter egg” material. I asked Claude to move it inline next to the “Imprint | Privacy Policy” links in the footer. It found the right DOM element (.footer-social > span:last-child) and appended the button there with a pipe separator. Small, subtle, discoverable.

Small screens were unplayable. On mobile, the characters – extracted at their original DOM positions – were too wide for the canvas. They’d immediately hit the edges and drop down rapidly, making the game impossible. The fix was elegant: a normalizeChars() function that calculates the bounding box of all extracted characters and scales them to fit within a target area (60px margins, top 45% of canvas height), preserving aspect ratio. Characters never scale up beyond their original size, only down when needed.

Both fixes were surgical – no unnecessary refactoring, no changes to code that was already working.

Iteration 3: The audio bug

I couldn’t hear any sound. The code looked correct – Web Audio API, oscillators, gain nodes – but nothing played.

Claude identified two root causes:

  1. Missing audioCtx.resume() – Modern browsers can create an AudioContext in suspended state even from user gesture handlers. One line fix.
  2. Pre-scheduling 200 music loops at once – The original code created ~4,800 oscillator nodes upfront. This overwhelmed the browser silently. The fix: incremental scheduling that only prepares 3 loops ahead and tops up via setTimeout.

After that, chiptune music and sound effects worked perfectly.

What worked well

Context awareness. Claude Code explored my Hugo site structure before writing a single line. It understood the theme is a git submodule (don’t touch), found the footer_js block in baseof.html, and used it cleanly. No hacks, no workarounds.

The creative bits. The character extraction via Range.getBoundingClientRect() was genuinely clever. I didn’t suggest that approach – I just said “characters on the page become enemies” and it figured out the DOM-to-canvas bridge.

Iteration speed. Each refinement cycle was fast. Describe the problem, get a targeted fix, verify with hugo --minify. No yak shaving.

Constraint adherence. I said “no external dependencies” and “as small as possible.” The result is a single 564-line file with zero imports. Music is synthesized at runtime. Styles are injected inline. It could be smaller, but not by much.

What didn’t work

Audio on first try and Small screen handling. Both were fixed in one iteration each, which is the more important point: the feedback loop was tight enough that “didn’t work the first time” wasn’t a significant cost.

The setup that made this work

A quick note on the Claude Code configuration: I used Opus as the model (the most capable one) with effort set to high – meaning it takes more time to think through problems before acting. Combined with Plan mode, this created a workflow where every iteration followed the same pattern: explore the codebase, write a plan, get my approval, then execute.

Plan mode was particularly valuable here because the task was creative and open-ended. Without it, an AI might dive into code immediately and produce something that doesn’t integrate well with the existing site. With Plan mode, I could review the approach – where files go, how the script loads, how characters get extracted – before any code was written. The plan for the initial version was detailed enough that the first implementation was nearly complete.

For the refinements, Plan mode kept things focused. Instead of rewriting large sections, each plan identified exactly which lines to change and why. That’s how you get surgical fixes instead of unnecessary refactors.

The takeaway

This was a fun, low-stakes experiment. But the pattern applies broadly: describe what you want at a high level, use Plan mode to review the approach before committing, then iterate on the result. The key is that iteration is cheap – both in time and in context. Claude Code remembered the full conversation, so each refinement built on the last without re-explaining anything.

The game is live on my homepage if you want to try it. Look for “INSERT COIN” in the footer.

More

  • This blog post was also generated by Claude Code from the conversation history of building the game. Meta enough for you?

Related posts