From a63e2913b346513bbb84958bd4845b40591fb7ad Mon Sep 17 00:00:00 2001
From: Wesley Ray
Date: Sun, 30 Aug 2026 07:59:26 -0400
Subject: [PATCH] cannons: run the demo behind the Play button
The page opened on a dark rectangle. It now opens on a battle already in
progress, with Play over it -- attract mode, as cannons_reloaded 76b32ca
added it.
One iframe, not two: it starts on the demo and the click swaps it for the
game. The demo is the client alone -- no socket, no WebAssembly server --
so the click still buys the payload the old placeholder was there to
defer; the bar was never the client at ~360KB, it was the 3MB server.
Two frame heights, because the demo and the game are not the same shape:
attract mode shows GameAreaPanel alone at 760x400 and the game shows the
whole applet at 770x540. Sized to the demo until Play is pressed, or the
battle sits letterboxed between two black bars.
---
src/lib/cannons.mjs | 6 ++++
src/pages/cannons.astro | 72 ++++++++++++++++++++++++++++-------------
2 files changed, 55 insertions(+), 23 deletions(-)
diff --git a/src/lib/cannons.mjs b/src/lib/cannons.mjs
index d367412..e16d76c 100644
--- a/src/lib/cannons.mjs
+++ b/src/lib/cannons.mjs
@@ -32,3 +32,9 @@ export function loadCannons() {
// $uri/index.html` would then have a file and a directory competing for the
// same URL. Exactly why the notebook exports are at /nb/ and not /notebooks/.
export const cannonsFrameUrl = '/cn/embedded.html';
+
+// Attract mode: the same page, told to play scripted scenes instead of
+// connecting. It costs the client (classes.js) and nothing else -- no
+// WebSocket, and crucially not the WebAssembly server, which is an order of
+// magnitude larger and still waits for the click.
+export const cannonsDemoUrl = '/cn/embedded.html?demo=1';
diff --git a/src/pages/cannons.astro b/src/pages/cannons.astro
index 50e19f6..cecf513 100644
--- a/src/pages/cannons.astro
+++ b/src/pages/cannons.astro
@@ -1,6 +1,6 @@
---
import Base from '../layouts/Base.astro';
-import { loadCannons, cannonsFrameUrl } from '../lib/cannons.mjs';
+import { loadCannons, cannonsFrameUrl, cannonsDemoUrl } from '../lib/cannons.mjs';
const build = loadCannons();
const mb = (n) => (n / 1048576).toFixed(1);
@@ -46,9 +46,27 @@ const overTheWire = build ? mb(build.wireBytes) : null;
computer opponent takes the other seat.
+
+ The battle below the button is a demo, and it has no server at all: the
+ client’s packet handlers are ordinary methods, so a scene is just a script
+ of the packets a real match would have sent, fed straight in. Which is why
+ it can show you eight cannons opening with ICBMs at once — a thing no
+ actual match can produce, because the computer opponents cannot aim a
+ guided weapon.
+
+
{
build ? (
+ {/* Rendered here rather than created in the script, so Astro's scoped
+ styles actually reach it -- see the note on .cn-frame below. It
+ starts on the demo, which needs the client but no server. */}
+
- {/* Rendered here rather than created in the script, so Astro's scoped
- styles actually reach it -- see the note on .cn-frame below. With
- no src it loads nothing, so the payload still waits for the click. */}
-