Implement the following plan:
A browser-based 2D physics Jenga game styled after XKCD 2347 ("Dependency"). A tower of blocks representing software dependencies, with one tiny "Nebraska" block near the base holding everything up. You drag blocks out with your mouse and watch the tower (hopefully not) collapse. Hosted on GitHub Pages -- pure static files, no build step.
MouseConstraint for drag interaction, Composites.stack() for building block towers, and sleeping support for stable stacking. Best option for this use case by a wide margin over Planck.js or p2.js.seed per shape for consistent strokes across frames.@font-face.index.html -- canvas, overlays, CDN script tags css/style.css -- layout, @font-face, overlay styling js/game.js -- all game logic fonts/xkcd-script.woff2 -- hand-drawn font
Tower layout: Mirrors the comic's shape. A wide messy mass of blocks at top ("All modern digital infrastructure") narrows toward the bottom, until one tiny block near the base is labeled "A project some random person in Nebraska has been mass maintaining since 2003". Blocks are labeled with real dependency names (OpenSSL, zlib, curl, SQLite, etc.) and end-user systems (Your bank, GPS, Email, etc.).
Interaction: Click and drag blocks to pull them out. Low stiffness on the mouse constraint (0.2) so it feels like tugging a block from a tight stack rather than teleporting it.
Collapse detection: After releasing a dragged block, monitor the tower for a few seconds. If blocks fall significantly from their original positions or leave the canvas, declare collapse.
Messages:
Physics tuning: High friction (0.8), high static friction (1.2), very low restitution (0.05), sleeping enabled. Tower built bottom-up with careful positioning so it starts stable.
Rendering: Custom requestAnimationFrame loop that reads body positions from Matter.js and draws them through Rough.js. Each block uses seed: body.id for consistent hand-drawn strokes. Text labels in xkcd-script font. An annotation arrow pointing to the Nebraska block like the original comic.
index.html, css/style.css with font loading, canvas layout, overlay markupfonts/js/game.js: Matter.js engine, world, ground, mouse constraintindex.html in a browser (or serve with python3 -m http.server)If you need specific details from before exiting plan mode (like exact code snippets, error messages, or content you generated), read the full transcript at: /Users/andrew/.claude/projects/-Users-andrew-code-xkcd-2347/81aac8c4-a31c-4d6d-8379-c4ec870a84fa.jsonl
Let me check the current state of the project directory and then implement everything.
Good, clean directory. Let me download the font and create all the files in parallel.
That's not a real font file. Let me find the right URL.
Got it, skipping the font. I'll use a fallback and we can add the font later. Let me build everything now.
Now the big one -- the game logic.
Let me test it in a browser to see how it looks.
opening index.html works fine
Good. Let me take a look at what we've got and see if the tower physics need adjustment. The main concern is whether the tower stands stable on load and whether the shape actually narrows toward the bottom like the comic.
Let me re-read the game.js to review the tower layout and think about a few issues I spotted while writing it.
There are a few problems I can see:
The tower is upside-down from the comic's shape. The comic has a wide mass at the top narrowing to a tiny block at the bottom, but the way physics works, the wide tiers at the top (tiers 5-7) have no physical support -- they extend way past the narrow base blocks. They'll just fall off the sides immediately.
The title block is static and invisible-ish but physically huge (500px wide) -- it acts as a shelf holding up the top blocks, which is a cheat but might actually be needed.
The resetGame calls init() which calls requestAnimationFrame(gameLoop) again, stacking up multiple animation loops.
The success toast uses inline styles which the CLAUDE.md says to avoid.
Let me fix these issues. The biggest architectural decision: the tower needs to actually be physically stable. I'll restructure it so the wide blocks at the top are supported by making each tier only slightly wider than the one below, with blocks overlapping enough to rest on their neighbors. I'll also make the title block a pure visual element (not a physics body) so it doesn't interfere.
Now let me also add the toast CSS class to avoid inline styles, and remove the block labels per your request.
Now remove the block labels from the drawing code.
Done. The game should now load with:
Refresh your index.html and give it a try. The tower might need physics tuning depending on how stable it settles -- let me know what you see.
[Image: source: /var/folders/8m/47w09vss1bqg72tlz9xcxl6h0000gn/T/TemporaryItems/NSIRD_screencaptureui_n6X8JG/Screenshot 2026-02-24 at 21.40.03.png]
The tower collapsed during the physics settling phase. The problem is that the upper tiers extend way beyond their support -- physics doesn't care about our intentions, blocks without something underneath them just fall.
I need to rethink the tower structure. The key insight from the comic is that it's not a traditional pyramid -- it's an inverted one, narrow at bottom, wide at top. To make that physically stable, I'll need to:
Let me also look at the comic again to understand the actual shape better.
Now I can see the actual comic. The structure is very different from what I built:
The whole thing looks like a precarious vertical structure sitting on a stable wide base, not an inverted pyramid. Let me completely redo the tower layout to match this, and randomize block sizes as you requested.
Refresh and see how this looks. The tower should now be:
The blocks are randomly sized using a seeded RNG so the layout is reproducible on reset. Let me know what happens when you load it.
[Image: source: /var/folders/8m/47w09vss1bqg72tlz9xcxl6h0000gn/T/TemporaryItems/NSIRD_screencaptureui_TvNJgj/Screenshot 2026-02-24 at 21.42.27.png]
Still collapsing during settle. The problem is that randomly-sized blocks packed loosely with gaps just can't form a stable column under physics simulation. The approach of "place blocks approximately and hope friction holds them" doesn't work -- Matter.js needs blocks to actually have solid support underneath them.
I need a different strategy: place each block precisely so it rests on the blocks below it, like actually building a brick wall. I'll lay blocks in rows where each row sits exactly on top of the previous one, with blocks offset like brickwork. The randomness comes from varying widths and heights within each row, but the vertical positioning is exact.
Fair point. Let me strip this back to the minimum: just a few blocks that actually stand up. No annotation, no title, no brace. Get the physics right first, then build up from there.
Stripped down to the bare minimum: 6 blocks in 4 rows, ground, mouse drag, rough.js rendering. No collapse detection, no scoring, no overlay, no annotations. Just blocks that should stand and be draggable.
Refresh and let me know if these blocks stay standing. Once we confirm the physics work, I'll build up the tower shape incrementally.
yep better, very uncreative though
[Image: source: /var/folders/8m/47w09vss1bqg72tlz9xcxl6h0000gn/T/TemporaryItems/NSIRD_screencaptureui_rJXxXd/Screenshot 2026-02-24 at 21.44.02.png]
Good, the physics work. Now I'll build it up to match the comic -- a taller tower with varied block sizes, the Nebraska block near the bottom, and the messy vertical column shape. I'll keep each row precisely placed on top of the last so nothing collapses on load.
Looking at the comic carefully: it's a pedestal base (wide flat layers), then a messy column with blocks of wildly different sizes -- some tall and narrow, some short and wide, some tiny. Small blocks stick out the sides. Near the bottom of the column there's one obviously small block (Nebraska). The top has multiple spires of small blocks sticking up at different heights.
Let me hand-craft this tower to match, keeping every block precisely supported.
Good call -- the comic has some really big blocks mixed in. Let me widen some blocks and add a few large ones before you test.
Refresh it. The tower should be taller now with a mix of big chunky blocks (140-240px wide) and small ones, with the Nebraska block still tiny near the base. The whole thing should stand about 360px tall.
the blocks have a lot of spring to them atm
The springiness is from the mouse constraint stiffness being too low (0.2), and maybe the restitution needs to be even lower. Let me tighten that up.
Higher stiffness on the mouse constraint (0.6 from 0.2) so blocks follow the cursor more directly instead of bouncing on an elastic tether. Added damping to kill oscillation. Zero restitution so blocks don't bounce at all when they land.
its climbing, falls over straight away though
[Image: source: /var/folders/8m/47w09vss1bqg72tlz9xcxl6h0000gn/T/TemporaryItems/NSIRD_screencaptureui_O46wKE/Screenshot 2026-02-24 at 21.45.30.png]
The tower's center of mass is off -- blocks are shifted too far right in places, so it topples. I can see from the screenshot it's leaning right. The problem is several rows have blocks placed asymmetrically (the tall narrow block at x=-50 in row 7, blocks at x=60, etc.).
I need to make sure every row's center of mass is roughly centered. Let me also rebalance -- keep blocks centered on x=0 and make sure no row overhangs too much.
I can see the problem -- rows are lopsided. Row 5 has blocks at -45, 10, 60 (center of mass is to the right). Row 7 has a tall block at -50 but wider stuff at 0 and 55. Row 8 is -10 and 60. Row 9 has -35, 15, 55. Almost everything leans right.
Let me recenter every row and also make sure blocks within each row don't have gaps (blocks should be packed edge-to-edge to support the row above).
The Nebraska row is the problem. The two support blocks (58px wide each centered at +-50) leave gaps around the tiny 22px Nebraska block. The blocks above have nothing supporting them where the gap is, so everything above Nebraska falls through and the tower splits apart.
The fix: make the Nebraska row supports actually touch Nebraska's edges, with no gaps. Nebraska goes from -11 to +11. Left support should go from about -80 to -11, right support from +11 to +80.
Try that. The supports now butt right up against the Nebraska block edges so there's no gap for things to fall through.
no, the problem is that pulling some at the bottom out leaves the top ones floating
Ah right -- the problem is the opposite of what I've been fighting. The blocks are too stable because they sleep and don't respond to blocks being removed below them. They just float in place.
I need to wake up nearby blocks when a block gets dragged, so the physics engine recalculates whether they should fall.
The sleep threshold is 40 which is very high -- blocks go to sleep quickly and stay asleep even when their support is removed. I'll wake all blocks whenever a drag starts.
Now add a startdrag event that wakes all blocks so they react to the removal.
Good, we have Sleeping imported. Now I need to add an event listener. Let me find where the mouse constraint is set up.
That should fix it. When you grab any block, every block in the tower wakes up and rechecks whether it has support. Pull a bottom block and the ones above should now fall.
ok thats better
Good. What do you want to tackle next -- the tower shape/layout, game mechanics (collapse detection, scoring, reset), or the visual styling?
the tower needs to be wider, some very wide blocks
Got it, removing the Nebraska block.
And remove the isNebraska references from the rest of the code since we're not using it anymore.
Done. Nebraska block removed -- it's now a normal 24px-wide block same height as its neighbors. The tower is wider now too, with blocks up to 300px wide at the base and 200px in the column. Refresh and see how it looks.
ok, so this kinda works, lets git init
Done. Want me to commit what we have so far?
"hello world" commit
actually, lets make this a folder in ../nesbitt.io
Done. Files are now at /Users/andrew/code/nesbitt.io/xkcd-2347/. Want me to switch my working directory there and commit?
yes
Committed as f6b3ac7 on master. I'll work from /Users/andrew/code/nesbitt.io/xkcd-2347/ going forward. What's next?
lets remove the "blocks removed" bit
Done. Removed the score display, overlay, and rebuild button from the HTML.
ok now lets think about building a randomized alogrithm for creating towers on load, blocks of various sizes, layering up from the bottom with gaps, getting smaller towards the top
Good idea. Let me think through the algorithm:
The key constraint is that every block must sit on top of something from the row below -- no overhangs beyond the previous row's edges or it'll topple.
Let me build this.
Refresh and try it. Each page load gives a new random tower. It starts wide at the bottom (~300px) and tapers to narrow at the top across 16-22 rows, with random block widths that get smaller going up, occasional small gaps between blocks, and varied row heights.
need to work in some gaps, and some multirow height blocks, its too uniform
Changes:
Refresh a few times to see the variety.
we still don't have those really big blocks, like 5x bigger than everything else
15% chance any row becomes a single massive block spanning the full row width and 30-55px tall. These will be the big chunky slabs you see in the comic. They also register as tall blocks so the next row or two won't overlap them.
[Image: source: /var/folders/8m/47w09vss1bqg72tlz9xcxl6h0000gn/T/TemporaryItems/NSIRD_screencaptureui_4GWA9g/Screenshot 2026-02-24 at 21.57.43.png]
That's the comic zoomed in. Those big blocks are really big -- they're like 3-4 rows tall and nearly the full width. And notice how the smaller blocks sit on top of and around them. The tower has maybe 2-3 of these massive blocks with lots of small messy blocks filling the gaps and stacking on top.
Let me bump up the big block frequency and make them even bigger.
Big blocks are now 50-90px tall (was 30-55) and 70-100% of the row width. 20% chance per row. They'll tower over the smaller 12-28px blocks around them.
That's looking much better. The big blocks are showing up and there's good variety. I can see a couple things to improve:
Want me to adjust the taper curve to be less pyramidal, or is there something else you want to change first?
[Image: source: /var/folders/8m/47w09vss1bqg72tlz9xcxl6h0000gn/T/TemporaryItems/NSIRD_screencaptureui_0T1222/Screenshot 2026-02-24 at 21.58.16.png]
That one's got a big block that's way too wide relative to the rows below it -- overhanging off the right side. The big blocks shouldn't be wider than the row width at that level.
Also the taper is still very pyramidal. Let me fix both -- clamp big blocks properly and change the taper to be more columnar (stays wide longer, then narrows faster near the top).
Now clamp the big blocks so they can't exceed the row width:
Two fixes: the taper is now columnar (stays wide for the bottom 60%, then drops off steeply), and big blocks are clamped to 65-95% of the current row width with their offset limited so they can't overhang.
yes less pryamid, want a bit of random, especially with some long thin ones, that can potentially have things hanging off like a cantaliver
I'll add random width jitter per row and a chance for long thin cantilever blocks that stick out past the row edges.
Now add cantilever blocks. I'll put them after each row's normal blocks are placed: