Try that. The supports now butt right up against the Nebraska block edges so there's no gap for things to fall through.
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.
Good. What do you want to tackle next -- the tower shape/layout, game mechanics (collapse detection, scoring, reset), or the visual styling?
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.
That's not a real font file. Let me find the right URL.
Done. Want me to commit what we have so far?
Done. Files are now at /Users/andrew/code/nesbitt.io/xkcd-2347/. Want me to switch my working directory there and commit?
Committed as f6b3ac7 on master. I'll work from /Users/andrew/code/nesbitt.io/xkcd-2347/ going forward. What's next?
Done. Removed the score display, overlay, and rebuild button from the HTML.
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.
Got it, skipping the font. I'll use a fallback and we can add the font later. Let me build everything now.
Changes:
Refresh a few times to see the variety.
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.
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?
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.
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:
Changes:
That's looking much more like the comic. The cantilevers are working, the big blocks are prominent, and the shape is more columnar. Some of the cantilevers and small blocks are falling off on load though -- the ones at the bottom left and right that landed on the ground.
The cantilever blocks are sticking out too far (only ~65% supported). Let me make them more like 75-80% supported so they balance better. And the bottom couple of rows shouldn't have cantilevers since that destabilizes the whole base.
Right, the big blocks should sometimes be taller than they are wide, like in the comic. Let me adjust.
Big blocks now: 50-85% of row width (narrower) and 60-120px tall (taller). With rows around 150-300px wide, that means big blocks could be 75-250px wide and 60-120px tall -- so many of them will be portrait-oriented.
The blocks are too heavy so gravity squishes them together during the initial physics frames. I'll reduce the density.
Quarter of the previous density. Should compress much less on load.
Now the big one -- the game logic.
Bumped friction from 0.9 to 1.0 and static friction from 2.0 to 3.0. Blocks should grip each other better now.
Ha, good. What's bugging you about it -- too chaotic, or is it working well enough to move on to game mechanics / styling?
The cantilevers and gaps are destabilizing things too much. Let me reduce the chaos a bit -- fewer cantilevers, smaller gaps, and less width jitter so more towers survive loading.
A few things are causing instability:
Yes -- Matter.js sleeping is exactly that. When all bodies have gone to sleep, nothing is moving. You can check it in the game loop:
Want me to add that as a settled/stable detection? Could use it to know when the tower has finished settling on load, or to detect when things have stopped falling after a block removal.
That's a great idea. Let me add settled detection and a random project name generator.
Let me test it in a browser to see how it looks.
Now add the settled detection in the game loop and draw the name:
Now draw the project name below the tower when settled:
Once the tower settles (all blocks sleeping), a random project name like "libcrypt-utils" or "node-parse.js" or "hyper-mux-fast" appears below the tower. Each reload generates a new tower and a new name.
Moved to top of canvas at 20px font.
Shows the name after either all blocks sleep or 5 seconds, whichever comes first.
Each block gets a random light grey between rgb(230,230,230) and rgb(255,255,255).
The simplest approach: keep the canvas at 800x700 internally but scale the container to fit the viewport with CSS. For touch on mobile, Matter.js mouse constraint already handles touch events. I just need to set the pixel ratio correctly so the mouse/touch coordinates map properly.
Now fix the mouse coordinate scaling in the JS. When the canvas CSS size differs from its internal resolution, Matter.js mouse coordinates get offset. I need to account for that.
Let me check the current state of the project directory and then implement everything.
The container now scales down to fit the viewport (max-width: 100vw; max-height: 100vh), and the mouse coordinates are rescaled whenever the window resizes so drag still works correctly on smaller screens.
35% chance the name becomes something like gitlab.com/jeff42/libcrypt-utils or sourceforge.net/bus-factor-1/node-parse.js.
Good to hear. Want to keep going with it? Some things that could come next:
What interests you?
Committed as c8d48f2. What's next?
Ha, nice. The idea being that JS projects are the most precarious. Let me add that.
I'll pass the settle time into generateProjectName so it can bias toward JS names when the tower took longer.
Now pass the settle time in:
Now the JS chance scales with settle time -- a tower that settles in 500ms has ~12% JS chance, one that takes 2 seconds has ~50%, and one that times out at 3 seconds has ~68%. The JS names use things like @left-pad/, babel-, webpack-plugin, .mjs and always end in a JS-flavored suffix.