Skip to content

Sidem/HexLife

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

448 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

HexLife Explorer logo

HexLife Explorer

An interactive, high-performance cellular automaton playground on a hexagonal grid.

Design rulesets, draw life into the grid, and watch complex behavior emerge across nine worlds at once.

β–Ά Try the Live Demo Β· r/hexlife Β· GitHub

Release License Rust WebGL2 Vite


A rotationally symmetric ruleset ('mossy bramble') growing from a single hexagonal seed, with rule-based coloring The same 'mossy bramble' ruleset rendered in plain monochrome cell states

One ruleset, two views β€” mossy bramble growing from a seed. Right: plain cell states. Left: rule-based coloring, where each cell is tinted by rules fired (respecting rotational symmetry), the dynamics become a visible fingerprint.


HexLife runs nine concurrent worlds side-by-side, each a hexagonal cellular automaton with its own ruleset and state. The simulation core is written in Rust β†’ WebAssembly, every world ticks in its own Web Worker, and rendering is a single instanced WebGL2 draw call per world. The result stays smooth enough to evolve, mutate, and compare rulesets in real time.

It sits at the crossroads of artificial life, complex systems, and generative art: a laboratory for watching emergence happen β€” gliders, growth fronts, self-organizing textures β€” and for hunting the edge of chaos where the most interesting behavior lives.

✨ Features

  • πŸ”¬ Nine concurrent worlds β€” compare how different rulesets or seeds evolve, all at once.
  • 🎨 Rule-based coloring β€” cells are tinted by which rule set their state, turning the dynamics into a visible fingerprint.
  • βͺ Scrub-back history β€” pause and rewind the selected world hundreds of ticks to replay what just happened, then branch from any point.
  • βœ‚οΈ Pattern copy/paste β€” marquee-select live cells and stamp them anywhere; placement is hex-phase-aware so shapes never distort (Ctrl+C / Ctrl+V).
  • 🧬 Ruleset toolkit β€” generate (random / neighbor-count / symmetry), hand-edit, mutate, clone, and breed rulesets.
  • πŸŽ₯ Media export β€” full-resolution PNG snapshots and live WebM recordings, straight to disk.
  • πŸ”— Shareable everything β€” rulesets are 32-char hex strings with friendly mnemonic names; whole setups encode into one share link.
  • πŸ“± Responsive β€” a dedicated mobile UI with a bottom tab bar and touch controls.
  • πŸŽ“ Guided onboarding β€” interactive tours and hands-on experiments in the Learning Hub.
  • 🧭 Auto-Explore (experimental / alpha) β€” an evolutionary search that hunts for "interesting" rulesets automatically. More below ↓

πŸš€ Getting Started

Prerequisites: Node.js 18+ and a browser with WebGL2 + hardware acceleration enabled.

git clone https://github.com/Sidem/HexLife.git
cd HexLife
npm install
npm run dev          # β†’ http://localhost:5173

The compiled Wasm engine is checked into the repo, so npm run dev works without a Rust toolchain. You only need Rust if you want to rebuild the engine.

Command What it does
npm run dev Start the Vite dev server
npm run build Rebuild Wasm + production build into dist/
npm run test:run Run the JS test suites once (Vitest)
npm run lint Lint the codebase (ESLint)
npm run typecheck Type-check opt-in files (tsc --noEmit)
Rebuilding the Rust/Wasm engine

Requires Rust and wasm-pack. Then:

npm run build:wasm                                  # rebuild the Wasm binary
cargo test --manifest-path hexlife-wasm/Cargo.toml  # run the Rust tick-engine tests

🧠 Core Concepts

  • Hexagonal grid β€” a flat-top hex grid where each cell has six neighbors and consecutive columns are staggered by half a hex. The grid wraps toroidally (opposite edges connect).
  • Two-state cells β€” every cell is active or inactive. Its next state depends on its own state plus its six neighbors.
  • 128-bit rulesets β€” a center cell + 6 neighbors give 2⁷ = 128 possible local configurations, so a ruleset is exactly 128 bits, written as a 32-character hex string that's trivial to share and edit.

πŸ—οΈ Architecture

A decoupled, performance-first design:

  • Simulation core β€” run_tick in Rust/Wasm; all per-cell buffers live in Wasm linear memory, bit-packed (8 cells/byte).
  • Concurrency β€” one Web Worker per world (9 total) for parallel, non-blocking computation.
  • Rendering β€” renderer.js draws each world into its own FBO with one instanced WebGL2 draw call, composed into the selected view + 3Γ—3 minimap; redraws are gated by dirty flags.
  • Orchestration β€” WorldManager (central controller) drives one WorldProxy per worker; UI ↔ logic communicate through a publish/subscribe EventBus.
  • Persistence β€” settings, rulesets, world configs, and panel layouts are saved to localStorage.
Key modules
Module Role
hexlife-wasm/src/lib.rs Rust tick engine (World::run_tick)
src/core/WorldWorker.js Per-world worker; holds typed-array views into Wasm memory
src/core/WorldProxy.js Main-thread proxy for one worker
src/core/WorldManager.js Central controller; settings, scope resolution, commands
src/rendering/renderer.js WebGL2 render-to-texture + instanced drawing
src/services/EventBus.js Pub/sub event catalog decoupling UI from logic
src/core/AutoExploreService.js Evolutionary search loop + behavior archive
Repository layout β€” two apps, one engine
Path What it is
src/, hexlife-wasm/, index.html HexLife Explorer β€” the app above. Vite + ESLint + Vitest.
src/embed/ The embeddable <hexlife-world> element, and the host boundary other apps import.
devvit/ Live Specimens on Reddit β€” a Devvit app with its own toolchain (TypeScript + esbuild + Biome, Node 22.6+) and its own npm test. See devvit/readme.md.

The Reddit app is bundled from this source tree rather than from a published package, so a post and the Explorer run one engine with one codec and one determinism contract. To keep that from becoming an invisible web of dependencies, devvit/ may import from src/embed/ and nowhere else in src/ β€” src/embed/api.js (DOM-free helpers, typed by api.d.ts) and src/embed/index.js (the browser entry that registers the element). tests/devvitBoundary.test.js enforces it.

Root npm run lint / test / typecheck cover the Explorer only; devvit/ is checked by its own npm test. Both run in CI.

🧭 Auto-Explore (Experimental)

⚠️ Alpha β€” untested and under active development. This automated search is a work in progress; treat its finds as exploratory and expect rough edges.

An optional evolutionary search that hunts for "interesting" rulesets on its own: it screens candidates with short evaluation bursts, confirms the promising ones, and banks survivors into a deduplicated gallery β€” with thumbnails and per-component score bars β€” that you can apply, save, share, or breed further.

How candidates are scored

Each candidate is judged across a suite of initial conditions (Chaos, Sparse, Seed, Clusters), because one ruleset can be lifeless from one seed and teeming from another. Four hard kills zero the score outright β€” extinct, saturated (β‰₯99% active), frozen (≀0.5 cells changing/tick), and short-cycle (period ≀ 4) β€” then survivors are ranked on the weighted, normalized terms below (terms are dropped and renormalized for entries that lack them, so each candidate is judged only on the terms it has).

Term Weight Measures
Structure 0.31 Join-count spatial order β€” domains, fronts, gliders vs. salt-and-pepper noise (most weighted)
Οƒ (criticality) 0.16 Damage-spreading probe; peaks at the edge of chaos (Οƒ β‰ˆ 1)
Temporal 0.13 Variance of block entropy over time β€” Wuensche's complex-rule discriminator
Transport 0.11 Drift of the active-cell centroid β€” coherently moving structure (gliders)
Heterog. 0.11 Order and disorder coexisting in different regions at once
Novelty 0.12 Optional, off by default. CLIP-embedding trajectory novelty (ASAL-style)
Entropy 0.07 Block-entropy mid-band β€” structured but not pure noise
Diversity 0.07 Shannon entropy of rule-usage β€” a rich rule vocabulary
Flux 0.04 Activity arriving in bursts (avalanches) rather than steady churn

Per-IC scores are combined with a soft-max (favoring each world's best IC) plus a small plain-mean robustness bonus. The same components can be measured for any world on demand from the Analysis β†’ Interestingness Metrics panel.

🀝 Sharing & Contributing Rulesets

Community β€” r/hexlife

Share Live Specimens (playable hexagonal worlds in the Reddit feed) on r/hexlife:

  1. In HexLife Explorer, open Share (or a personal library entry β†’ Share on Reddit).
  2. Use Copy post kit & open r/hexlife β€” the kit includes title, description, tags, and a world code.
  3. On the subreddit: community menu β‹― β†’ New HexLife post β†’ paste only the HXW1.… line into the form (and the suggested title into the title field).

A Live Specimen is a Devvit custom post; Reddit has no public URL that opens that form from outside the site, so this two-step handoff is intentional. The full lab is always sidem.github.io/HexLife.

Portable packs

Your discoveries don't have to stay on one device. Both the Ruleset Library and the Auto-Explore gallery can export a portable pack file (hexlife-pack, versioned JSON) and import one back:

  • Ruleset Library toolbar β€” the ⬇ button downloads all your saved rulesets as hexlife-rulesets-<date>.json; the ⬆ button imports a pack (duplicates, matched by rule, are skipped).
  • Auto-Explore gallery header β€” the same ⬇ / ⬆ pair exports/imports gallery finds. Imported finds are re-scored into the archive (better scores win their behavior cell). Import is disabled while a search is running.

Packs are treated as untrusted input: every entry is sanitized on import (bad rule codes are dropped, over-long text is clamped, and thumbnails over 64 KB or that aren't image data-URLs are dropped). Perceptual (CLIP embedding) cell keys are stripped on import β€” they're specific to the exporter's model β€” so a pack made with embeddings on imports cleanly on a device with them off, and scores stay honest.

Getting a ruleset into the public library (PR path)

The bundled public library lives in src/core/library/rulesets.json. To contribute a rule:

  1. In the Ruleset Library, open a saved rule's β‹― menu β†’ "Copy as public-library JSON". This copies a single entry in the committed shape:

    {
      "name": "Spiral Weaver",
      "description": "A tidy little glider gun.",
      "tags": ["gliders", "spiral"],
      "hex": "12482080480080006880800180010117",
      "initialState": { "mode": "clusters", "params": { "count": 5, "density": 0.7 } },
      "seed": 4242
    }
  2. Open a PR adding that entry to the rulesets.json array. Do not include a thumb β€” thumbnails are baked client-side on demand and cached locally, so the committed JSON stays small. initialState/seed are optional (include them when a rule only shines from a specific starting condition).

Additions to the public library are curated/manual by design β€” there is no auto-merge.

⌨️ Keyboard Shortcuts

Full shortcut reference
Keys Action
P Play / pause
Escape Close active popout or panel
1–9 Select world (numpad layout)
Shift+1–9 Toggle world's enabled state
N / E / S / A Toggle Ruleset Actions / Editor / World Setup / Analysis panel
G Generate new ruleset
M / Shift+M Clone & mutate others / mutate selected
O / I Clone selected ruleset to all / invert it
R / Shift+R Reset all worlds / selected world
C / Shift+C Clear all worlds / selected world
D / Shift+D Reset densities & reset all / apply selected density to all
Ctrl+C / Ctrl+V Copy / paste a cell-region pattern
← / β†’ Step back / forward one tick (while paused)
Ctrl+Z / Ctrl+Shift+Z Undo / redo ruleset change

🏷️ Versioning & releases

Versions are semantic and single-sourced in package.json. The running build identifies itself as v1.0.0 Β· <sha> Β· <date> in Settings and in the boot log β€” the SHA is there because GitHub Pages deploys on every push to main, so most live builds sit between tags and the version alone would be misleading.

What counts as breaking here is about worlds, not widgets: ruleset codes, world codes (HXW1.…), share links, <hexlife-world> attributes, and determinism. A total UI redesign is not a major bump; silently changing what a 32-char hex string means is. See CHANGELOG.md.

Cutting a release:

  1. Write the new ## [X.Y.Z] section in CHANGELOG.md.
  2. Update version and date-released in CITATION.cff.
  3. npm version <patch|minor|major> β€” bumps package.json, commits, and tags vX.Y.Z.
  4. git push --follow-tags.

The tag triggers release.yml, which publishes a GitHub Release from that CHANGELOG section β€” and refuses if the tag and package.json disagree, or if the CHANGELOG has no matching entry. (A GitHub Release is also the event a Zenodo webhook needs to mint a citable DOI.)

The Reddit app in devvit/ ships on Reddit's own review cadence and is not covered by these tags.

πŸ“„ License

Released under the MIT License β€” Β© 2025 Sidem. This applies to the whole repository, including devvit/.

About

Interactive hexagonal cellular automata lab in the browser - Rust/WebAssembly tick engine, instanced WebGL2 rendering, 9 parallel worlds, shareable 128-bit rulesets with mnemonic names, and an evolutionary Auto-Explore search for emergent behavior at the edge of chaos.

Topics

Resources

License

Stars

4 stars

Watchers

2 watching

Forks

Contributors