Back to Conway's Game of Life
SOURCE / PINNED RELEASE

Made of little things.

Conway's Game of Life

Release
230955df1900…
Author-recorded commit
c08196c2b050…
License
LICENSE
Author’s source reference
nostr://npub16nzc4ql748r2kv9202rgzrt0fmtk4n3tzsjl86yf6ty9agqe9u0qjcc7hc/wss%3A%2F%2Fgit.napplet.soy%2F/n-644c45e22c7

Archive hash verified: 2627ce678935a213…. The source-to-build association is the author’s claim; it has not been independently rebuilt.

index.html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <!-- Listing description: byte-identical to napplet.json `description`
         (pinned by tests/guidance.test.mjs; frozen with the player at Phase 9). -->
    <meta name="description" content="Conway's Game of Life on a wrapping 120 by 90 board: paint cells, drop classic patterns like the Gosper glider gun, and watch it evolve at your chosen speed." />
    <title>Conway's Game of Life</title>
  </head>
  <body>
    <!--
      A napplet is an applet framed by a runtime: the runtime already shows this
      napplet's name, so there is no title header here. The board is the frame
      from the first pixel; the HUD is one quiet status line (spec §8 copy).
    -->
    <main id="app" class="app">
      <div class="hud" aria-label="Simulation status">
        <span class="hud-item">gen <span id="generation" class="num">0</span></span>
        <span class="hud-item">pop <span id="population" class="num">0</span></span>
        <output id="status" class="hud-status" aria-live="polite" hidden>Stable</output>
        <output id="save-status" class="hud-status hud-save" aria-live="polite" hidden
          >Saving unavailable &mdash; play stays in this session.</output
        >
        <!-- Armed-placement hint (spec §6 rework ruling 2026-10-30): text set
             by main.ts while a pattern is armed; hidden = not armed. -->
        <output id="place-status" class="hud-status hud-place" aria-live="polite" hidden></output>
      </div>
      <!-- Static control chrome (spec §5): outside any state machine; painting
           stays legal while any control state is active. -->
      <div class="controls" aria-label="Simulation controls">
        <!-- Boots PAUSED behind the welcome gate (spec §5 welcome rule), so
             the initial glyph is Play; syncRunButton() keeps it in step. -->
        <button id="play-pause" class="ctl" type="button" aria-label="Play">⏵</button>
        <button id="step" class="ctl" type="button" title="Step one generation" aria-label="Step one generation">⏭</button>
        <button id="clear" class="ctl ctl-word" type="button">Clear</button>
        <button id="random" class="ctl ctl-word" type="button">Random</button>
        <button id="patterns" class="ctl ctl-word" type="button" aria-haspopup="true" aria-expanded="false" aria-controls="pattern-menu" title="Pattern library">Patterns</button>
        <label class="ctl-speed">
          <input id="speed" type="range" min="1" max="30" step="1" value="10" aria-label="Speed in generations per second" />
          <output id="speed-value" class="num" for="speed">10/s</output>
        </label>
      </div>
      <div id="board" class="board">
        <canvas id="cells" aria-label="Game of Life board"></canvas>
        <!-- Pattern library overlay (Phase 5, spec §6): static chrome riding
             outside any state machine — open/close never touches running,
             generation, or paint legality. Items built from PATTERNS. -->
        <div id="pattern-menu" class="menu" hidden>
          <div class="menu-title">Patterns</div>
          <div id="pattern-list" class="menu-list"></div>
          <button id="menu-close" class="ctl ctl-word" type="button">Back</button>
        </div>
        <!-- Welcome gate (spec §5 welcome rule, player ruling 2026-10-30):
             full-frame primer overlay at boot; Start dismisses it AND enters
             the sim through the same setRunning path as the play/pause
             button. Fixed positioning rides the FRAME, not the board box:
             at the 200x160 floor the board row collapses under the control
             bar, and a board-anchored card would squash to nothing. Markup
             lives inside .board; the scrim blocks all controls until Start.
             Copy pinned by the browser suite (spec §8). -->
        <div id="welcome" class="welcome" aria-label="How to play">
          <div class="welcome-card">
            <div class="welcome-title">How to play</div>
            <ul class="welcome-list">
              <li>Left click/drag to paint cells</li>
              <li>Right click/drag to erase</li>
              <li>Space play / pause, &rarr; step, C clear</li>
              <li>Patterns: pick one, then tap the board</li>
              <li>Slider sets speed, 1&ndash;30 per second</li>
            </ul>
            <button id="welcome-start" class="ctl ctl-word" type="button">Start</button>
          </div>
        </div>
      </div>
    </main>
    <script type="module" src="/src/main.ts"></script>
  </body>
</html>