npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@hexlife/embed

v1.15.0

Published

Typed hex geometry plus embeddable binary, k-state, stochastic, and HCP cellular-automaton engines, rendering, and solid export

Readme

@hexlife/embed

Fast, deterministic hexagonal cellular automata for ordinary web pages, browser workers, and Node.

npm license dependencies types engines WebGL2

The package bundles the Rust/Wasm tick engine and WebGL2 renderer used by HexLife Explorer, then exposes them at the level a host needs: custom elements for the quickest integration, or DOM-free simulation and renderer-only entrypoints for applications that own more of the stack.

For applications that need the hex map but not a cellular automaton, /hex is a zero-dependency, DOM-free and Wasm-free pointy-top axial geometry module:

import {axialNeighbor, axialToPixel, pixelToAxial} from '@hexlife/embed/hex'

const east = axialNeighbor({q: 0, r: 0}, 0)
const center = axialToPixel(east, 24, {x: 320, y: 240})
const picked = pixelToAxial(center, 24, {x: 320, y: 240})

Direction 0 is east; indices continue clockwise in screen coordinates. The contract also includes cube rounding, distance, line traversal, rotation, and negative-coordinate-safe storage chunks. → Full /hex reference

The same (ruleset, seed, density, rows) produces a byte-identical tick sequence in HexLife Explorer, an embed, and HexLife on Reddit. An embed is therefore a reproducible world, not a visual approximation of one.

Install

npm install @hexlife/embed

Quick start

import '@hexlife/embed' // registers <hexlife-world> and <hexlife-grid>
<hexlife-world
  ruleset="D5F5EBB9CD2C79E4B3F1F0E6ED1D67A6"
  rows="64"
  seed="12345"
  speed="20"
></hexlife-world>

The element is display: block with a 1 / 1 aspect ratio by default; give it a width and it sizes itself. Everything lives in a shadow root, so the host page's CSS cannot break it and its CSS cannot touch the host.

For host-owned computation, use the same engine without mounting a DOM element:

import {createDensityState, createSimulation, packCells} from '@hexlife/embed/sim'

const initialCells = createDensityState({rows: 64, columns: 74, seed: 12345, density: 0.5})
const sim = await createSimulation({rulesetHex, rows: 64, columns: 74, initialCells})
sim.tick(10)
const packed = packCells(sim.snapshotCells())
sim.dispose()

Printable solids

@hexlife/embed/solid extrudes a run through time into an object you can print: the hex grid is the cross-section, each tick is a layer, on cells are matter. It simulates nothing — it is a layer sink, so the binary, k-state and stochastic engines all feed the same buffer.

import {createSolidStack, initSolidEngine} from '@hexlife/embed/solid'

await initSolidEngine()
const stack = createSolidStack({rows, cols, ticks, interpolate: 'bridge', subLayers: 1, basePlate: 2})

const layer = stack.layerView()          // build ONCE, outside the loop
for (let tick = 0; tick < ticks; tick++) {
  layer.set(world.state)                 // one memcpy per tick
  stack.pushLayer()
  world.tick()
}

const report = stack.finalize({keepComponents: 'plate-connected'})
report.keptComponents // → 1 means it prints as a single piece
report.floating       // → components that never reach the build surface

const bytes = await stack.export({format: '3mf', cellSize: 2, layerHeight: 0.8})
stack.free()                             // mandatory

Read the report before you export. A slicer will not join separate bodies — it will happily print forty loose fragments and let you find out on the build plate.

interpolate: 'bridge' is what makes the object hold together. Two prisms on consecutive layers whose cells are diagonal neighbours meet along a single vertical edge — a zero-thickness hinge that reports as connected and prints as two pieces. Bridging inserts exactly the set that turns that into face contact, without the fattening 'union' causes. For a vacuum-stable ruleset it guarantees that nothing floats: every voxel is face-connected down to tick 0. Check with isVacuumStable() from /api first — the guarantee is precisely as good as its precondition.

Units are millimetres. cellSize (hexagon circumradius) and layerHeight are independent, so the object's Z aspect ratio is a print decision rather than an accident of tick count. Formats are 'stl' (default, universal), 'ply' (indexed, ~⅓ the bytes) and '3mf' (indexed XML in a zip — what slicers prefer, and the only one that carries real units).

merge: 'greedy' is the default and welds runs of coplanar faces into single quads: 586,864 triangles down to 16,796 on a 30×36×100 volume, and the 3MF down to 0.125 MiB. merge: 'none' is a supported first-class setting, not a debug flag — merging necessarily leaves T-junctions, slicers do not care, strict manifold validators do. Both meshes bound exactly the same solid.

The boundary of the printed object is open, not toroidal: the simulation wraps, an object cannot, so features are cut at the grid edge and two pieces touching only across the seam are two pieces.

Full /solid reference

The same run, in 3D

@hexlife/embed/spacetime draws those layers instead of meshing them: the grid is the cross-section, time runs up, and the whole history is one ray-marched solid you can turn, zoom and slice. It is the HexLife Explorer's spacetime view, packaged — and it simulates nothing either, so it eats exactly the same per-tick states the extruder does.

import {createSpacetimeView} from '@hexlife/embed/spacetime'

const view = createSpacetimeView(canvas, {rows, columns, depth: ticks})
for (let tick = 0; tick < ticks; tick++) {
  view.pushSimulation(sim, tick)          // native rule×2+state packing; no JS cell loop
  sim.tick()
}
view.draw()                        // drag to turn, wheel to dolly — controls are on by default
view.setCrossSection(30)           // slice it: layer 30 as an opaque plane through the solid

Cost is pixels × march steps and is independent of grid resolution — a 576-row world costs the same per frame as a 96-row one. What a bigger world costs is texture memory: one byte per cell per layer, allocated once. The layer byte is the colour-table index, so changing the palette retints every layer of history and re-uploads nothing.

Full /spacetime reference

A close-packed 3D lattice

@hexlife/embed/hcp is a fourth engine: a general k-state CA on the hexagonal close-packed lattice (12 equidistant neighbours). The v1 rule is a 6-phase tetrahedral block of size k⁴. Slot 3 is always the unique lowest site, so gravity lives in the table, not in the tick. Importing it loads no bytes into a page that only uses /ca or /sim.

import {initHcpEngine, HexHcp, blockRuleFromTet} from '@hexlife/embed/hcp'

await initHcpEngine()
const rule = blockRuleFromTet(6, ([a, b, c, apex]) => [a, b, c, apex])
const world = new HexHcp({states: 6, layers: 24, rows: 48, columns: 56, rule})
world.setBlockAlternates(true)
world.tick(12)
world.paintIf(0, inlets, 0, 1)
world.clearStatesInLayer(world.layers - 1, 0b0110)

HXP1 codes freeze the current world — geometry, boundaries, rule, generation, and cells — and resume the next tick identically. isConservative / isIsotropic report table properties; they never enforce them.

Full /hcp reference

Documentation

The full reference lives in the repository, one page per surface:

| Page | What it covers | | :--- | :--- | | Docs index | Everything below, in one table. | | Getting started | Install, first world, sizing, host-owned simulation, requirements. | | Entry points | Which of the thirteen imports you need and what each requires. | | /hex | Pointy-top axial coordinates, direction protocol, pixels, lines, rounding, and chunks. | | <hexlife-world> | Attributes, JavaScript API, events, GPU-loss recovery, policies, styling. | | <hexlife-grid> | Many worlds in one WebGL context. | | /sim | DOM-free simulation, seeded and sparse states, vacuum stability, block skipping. | | /render | The renderer alone, for hosts that own their simulation. | | /spacetime | A run drawn as a 3D solid: one tick per layer, ray-marched, turnable and sliceable. | | /ca | k-state worlds, conservation, isotropy, <hexlife-ca>, HXK1 codes. | | /stochastic | Probability and time-in-state, the lattice gas, <hexlife-stochastic>, HXS1 codes. | | /solid | Extrude a run into a printable solid: welding, components, meshing, STL/PLY/3MF. | | /hcp | HCP worlds, k⁴ tetrahedral blocks, <hexlife-hcp>, HXP1 codes. | | /api | DOM-free metadata, world codecs, palettes, GPU probing. | | Determinism & versioning | The reproducibility contract and what a major bump means. |

Entry points

| Import | Needs | Use for | | :--- | :--- | :--- | | @hexlife/embed | DOM, WebGL2, Wasm | The browser. Registers <hexlife-world> and <hexlife-grid>. | | @hexlife/embed/api | Nothing | Node and browsers alike: world codes, ruleset metadata, palettes, GPU probing. | | @hexlife/embed/hex | Nothing | Unbounded pointy-top axial coordinates, directions, pixels, lines, and storage chunks. | | @hexlife/embed/sim | Wasm | Node and workers: deterministic host-driven simulation, no DOM or rendering. | | @hexlife/embed/render | DOM, WebGL2 | Draw externally supplied state without ticking a simulation. | | @hexlife/embed/spacetime | DOM, WebGL2 | A run as a 3D solid. One retained tick per layer, ray-marched; turn it, slice it, look inside it. No Wasm artifact at all. | | @hexlife/embed/ca | Wasm | k-state worlds — a second engine, with an optionally mass-conserving backend. | | @hexlife/embed/ca-element | DOM, WebGL2, Wasm | Registers <hexlife-ca>. | | @hexlife/embed/stochastic | Wasm | Probabilistic and time-dependent worlds, plus the conserved lattice gas. A separately loaded artifact. | | @hexlife/embed/stochastic-element | DOM, WebGL2, Wasm | Registers <hexlife-stochastic>. The only element entry that reaches the stochastic artifact. | | @hexlife/embed/solid | Wasm | Printable solids. Extrude a run of any of the engines above through time; export STL, PLY or 3MF. A third separately loaded artifact. | | @hexlife/embed/hcp | Wasm | HCP worlds — a fourth engine, a 12-neighbour close-packed lattice with k⁴ tetrahedral blocks. A separately loaded artifact. | | @hexlife/embed/hcp-element | DOM, WebGL2, Wasm | Registers <hexlife-hcp>. The only element entry that reaches the HCP artifact. |

A server that validates a pasted world code must import only @hexlife/embed/api — the root entry evaluates custom-element, Wasm and WebGL code at import time.

Importing the package root, /sim, /ca, /stochastic or /solid neither downloads nor initializes the HCP artifact.

The browser bundle inlines the Wasm binary as a data URI rather than fetching a side-car asset, because a strict host CSP (a Reddit webview, for instance) is not something an embed can widen.

Live examples

Every page below is a package consumer with the same presentation shell. The k-state pages resolve the exact published npm version through jsDelivr; they do not reach into Explorer internals.

| Demo | What it demonstrates | Package surface | |---|---|---| | Interactive demo library | Nine focused experiments spanning crystal growth, ecology, excitable media, particles, seeded probability, deterministic chaos, sound, and interacting matter. | Every public entrypoint, with each page consuming the published npm package | | 256 worlds, one rule class | All 256 totalistic rules simultaneously, or an equally sized sample of a larger rule class. One shared clock, initial condition, palette, and GPU context make rule-to-rule comparison direct. | <hexlife-grid>, <hexlife-world>, /api | | Coffee extraction lab | Six- and sixteen-state physical models with exact conservation, plus the same extraction on the close-packed 3D lattice. | /ca, /ca-element, /hcp, /hcp-element | | k-state CA builder | Edit exact transition tables, paint and run the Wasm world, inspect invariants, and export a standalone npm-package example. | /ca, /ca-element, <hexlife-ca> |

The atlas is also a performance demonstration: <hexlife-grid> runs hundreds of simulations but draws them through one WebGL2 context, avoiding the browser context limit that makes a wall of independent canvases fail. For sparse or settled worlds, exact uniform-block skipping makes the 383k-cell binary engine about 13× faster at a fixed point and about 2.4× faster at 0.2% occupancy on the project benchmark machine. No approximation or separate "fast" result is involved.

Determinism

For the same (ruleset, seed, density, rows), this package and HexLife Explorer run the same Wasm run_tick over a grid derived the same way and filled by the same seeded RNG, so they agree tick for tick. The pinned reference, if you want to check a build:

const world = document.getElementById('ref')  // <hexlife-world ruleset="D5F5EBB9CD2C79E4B3F1F0E6ED1D67A6"
world.reset(12345)                            //                rows="64" density="0.5" seed="12345" paused>
world.tick(100)
world.checksum // → 231200078

Requirements

WebGL2 and WebAssembly, and Node 20+ for the DOM-free entries. There is no 2D fallback — call detectGraphicsPath() from @hexlife/embed/api to detect that before you mount anything.

Versioning

The custom-element API is additive: attributes, methods and events are only added, never removed or repurposed. A major bump is reserved for the things that would break reproducibility — what a ruleset hex decodes to, what an HXW1.… code decodes to, or the tick sequence itself. A visual change is not breaking; a world that comes back different is.

This package versions independently of the HexLife Explorer application.

Links

License

MIT © Sidem