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

lifehashjs

v1.1.4

Published

A framework-agnostic JavaScript library for generating LifeHash visualizations from strings

Downloads

50

Readme

LifeHashJS

A framework-agnostic JavaScript library for generating LifeHash visualizations from strings. LifeHash is a beautiful method of hash visualization based on Conway's Game of Life.

About This Library

This library is a JavaScript wrapper around the LifeHash reference implementation by Blockchain Commons. It provides a pre-built WebAssembly (WASM) version of LifeHash, so you don't need to compile the C++ source code yourself. Simply install and use!
Platform note: This package targets web environments (browsers and Node.js/bundlers). React Native is not supported directly; for RN, use a WebView wrapper or backend generation.

Original Implementation: BlockchainCommons/LifeHash (Swift/C++ reference implementation)

What This Library Does:

  • Provides a pre-built WASM binary compiled from the original LifeHash C++ code
  • Exposes a simple, framework-agnostic JavaScript API
  • Handles WASM module loading and initialization automatically
  • Works in browsers and Node.js environments

Credits:

Installation

npm install lifehashjs
# or
pnpm install lifehashjs
# or
yarn add lifehashjs

Quick Start

import { generate, ELifeHashVersion } from "lifehashjs";

// Generate a LifeHash from a string
const result = await generate("Hello, World!", {
  version: ELifeHashVersion.version2,
});

// Use the data URI in an image element
const img = document.createElement("img");
img.src = result.dataUri;
img.width = result.width;
img.height = result.height;
document.body.appendChild(img);

Usage Examples

Vanilla JavaScript / HTML

<!DOCTYPE html>
<html>
  <head>
    <title>LifeHash Example</title>
  </head>
  <body>
    <img id="lifehash" alt="LifeHash" />

    <script type="module">
      import { generate, ELifeHashVersion } from "lifehashjs";

      async function displayLifeHash() {
        const result = await generate("Hello, World!", {
          version: ELifeHashVersion.version2,
        });

        const img = document.getElementById("lifehash");
        img.src = result.dataUri;
        img.width = result.width;
        img.height = result.height;
      }

      displayLifeHash();
    </script>
  </body>
</html>

React Example (web)

Basic Component Usage

import React, { useEffect, useState } from "react";
import { generate, ELifeHashVersion } from "lifehashjs";

function LifeHashImage({ input }) {
  const [dataUri, setDataUri] = useState(null);

  useEffect(() => {
    generate(input, { version: ELifeHashVersion.version2 }).then((result) =>
      setDataUri(result.dataUri)
    );
  }, [input]);

  if (!dataUri) return <div>Loading...</div>;

  return <img src={dataUri} alt="LifeHash" />;
}

Framework-agnostic core utility

You can also use a shared, framework-agnostic generator that manages cancellation and optional preloading. Import it from the package root:

import {
  lifeHashGenerator,
  ELifeHashVersion,
  TCoreLifeHashOptions,
  TCoreLifeHashState,
} from "lifehashjs/lifehash-utils";

const unsubscribe = lifeHashGenerator.subscribe((state: TCoreLifeHashState) => {
  // handle updates (e.g., setState in React/Vue/Svelte)
  console.log(state);
});

lifeHashGenerator.generate("hello", {
  version: ELifeHashVersion.version2,
  preload: true,
} as TCoreLifeHashOptions);

// later, to cancel:
lifeHashGenerator.cancel();
// to stop listening:
unsubscribe();

Custom Hook Usage (manual, AbortController built-in)

For a more robust implementation with error handling and loading states, you can create a custom hook using generateWithAbort, which accepts an AbortSignal and avoids manual cancel flags. Use the exported TLifeHashAsyncState type instead of defining your own:

import {
  ELifeHashVersion,
  generateWithAbort,
  TLifeHashAsyncState,
} from "lifehashjs";
import { useEffect, useState } from "react";

const useLifeHash = (hash: string): TLifeHashAsyncState => {
  const [state, setState] = useState<TLifeHashAsyncState>({
    dataUri: null,
    error: null,
    isLoading: true,
  });

  useEffect(() => {
    setState({ dataUri: null, error: null, isLoading: true });

    async function generateLifeHash() {
      const controller = new AbortController();
      try {
        const result = await generateWithAbort(hash, {
          version: ELifeHashVersion.version2,
          signal: controller.signal,
        });

        if (result?.dataUri) {
          setState({ dataUri: result.dataUri, error: null, isLoading: false });
        } else {
          setState({ dataUri: null, error: null, isLoading: false });
        }
      } catch (err) {
        if ((err as Error & { name?: string })?.name !== "AbortError") {
          setState({ dataUri: null, error: err as Error, isLoading: false });
        }
      }
      return controller;
    }

    const ctrl = generateLifeHash();
    return () => {
      ctrl?.abort?.();
    };
  }, [hash]);

  return state;
};

export default useLifeHash;

Then use it in your components:

import React from "react";
import useLifeHash from "./hooks/useLifeHash";

function LifeHashImage({ hash }: { hash: string }) {
  const { dataUri, error, isLoading } = useLifeHash(hash);

  if (isLoading) return <div>Loading...</div>;
  if (error) return <div>Error: {error.message}</div>;
  if (!dataUri) return null;

  return <img src={dataUri} alt="LifeHash" />;
}

Benefits of the custom hook:

  • ✅ Clean async/await syntax (no promise chaining)
  • ✅ Automatic cleanup on unmount or hash change
  • ✅ Loading and error states included
  • ✅ TypeScript support with proper types
  • ✅ Automatic initialization (no public init() required)

Vue Example (web)

<template>
  <img v-if="dataUri" :src="dataUri" alt="LifeHash" />
  <div v-else>Loading...</div>
</template>

<script setup>
import { ref, watch } from "vue";
import { ELifeHashVersion, generateWithAbort } from "lifehashjs";

const props = defineProps(["input"]);
const dataUri = ref(null);
const error = ref(null);
const isLoading = ref(false);

watch(
  () => props.input,
  async (newInput) => {
    isLoading.value = true;
    error.value = null;
    dataUri.value = null;
    const next = await generateWithAbort(newInput, {
      version: ELifeHashVersion.version2,
      signal: controller.signal,
    });
    dataUri.value = next.dataUri;
    error.value = null;
    isLoading.value = false;
    return controller;
  },
  { immediate: true }
);
</script>
</script>

Node.js Example

const { generate, ELifeHashVersion } = require("lifehashjs");

async function createLifeHash() {
  const result = await generate("Hello, World!", {
    version: ELifeHashVersion.version2,
  });

  // The result contains a data URI that can be used or saved
  console.log("Data URI:", result.dataUri);
  console.log("Dimensions:", result.width, "x", result.height);

  // You can convert the data URI to a buffer if needed
  const base64Data = result.dataUri.split(",")[1];
  const buffer = Buffer.from(base64Data, "base64");
  // Save buffer to file, send in HTTP response, etc.
}

API Reference

generate(input: string, options?: TLifeHashOptions): Promise<TLifeHashResult>

Generate a LifeHash image from a string input. This is the most common method.

Parameters:

  • input (string): The string to generate a LifeHash from
  • options (optional): Configuration object
    • version (ELifeHashVersion): The LifeHash version to use (default: ELifeHashVersion.version2)
    • moduleSize (number): Module size multiplier (default: 1)

Returns: Promise that resolves to a LifeHashResult object:

  • dataUri (string): Data URI of the generated image (e.g., "data:image/png;base64,...")
  • width (number): Image width in pixels
  • height (number): Image height in pixels
  • version (ELifeHashVersion): The version used

Example:

const result = await generate("My input string");
console.log(result.dataUri); // "data:image/png;base64,iVBORw0KGgo..."

generateFromData(data: Uint8Array, options?: TLifeHashOptions): Promise<TLifeHashResult>

Generate a LifeHash image from binary data (Uint8Array).

Example:

const data = new Uint8Array([1, 2, 3, 4, 5]);
const result = await generateFromData(data);

generateFromDigest(digest: Uint8Array, options?: TLifeHashOptions): Promise<TLifeHashResult> (advanced / optional)

Generate a LifeHash image from a SHA-256 digest. Use this only if you already have a canonical 32-byte digest and want to skip hashing inside the module. The digest must be exactly 32 bytes.

Example:

const digest = new Uint8Array(32); // Your 32-byte SHA-256 digest
const result = await generateFromDigest(digest);

sha256(input: string): Promise<Uint8Array> (advanced / optional)

Compute SHA-256 hash of a string via the WASM module. Optional helper; most users can rely on generate/generateWithAbort instead.

Example:

const hash = await sha256("Hello, World!");
console.log(hash); // Uint8Array(32) [185, 77, 39, ...]

LifeHash Versions

  • ELifeHashVersion.version1 - The original (DEPRECATED)
  • ELifeHashVersion.version2 - Bug fixes and CMYK-friendly (recommended)
  • ELifeHashVersion.detailed - Twice the resolution, CMYK-friendly
  • ELifeHashVersion.fiducial - Optimized for machine vision recognition
  • ELifeHashVersion.grayscaleFiducial - Highest contrast for low-light situations

Framework Support

This library is framework-agnostic and works with any JavaScript environment:

  • React - Use in components with hooks or effects
  • Vue - Use in composition API or options API
  • Angular - Use in services or components
  • Svelte - Use in components or stores
  • Vanilla JavaScript - Use directly in any JS file
  • Node.js - Works in server-side environments
  • Web Workers - Can be used in worker threads
  • Bundlers - Compatible with webpack, Vite, Rollup, etc.
  • ⚠️ React Native - Not supported directly. Options if needed:
    • Run on a Node server/edge function and return data URIs/base64 to the app (recommended for simplicity).
    • Use a WebView to run the web build and post the data URI back.
    • Best native experience: build a native module using the platform LifeHash implementations.

How It Works

This library uses a pre-compiled WebAssembly (WASM) binary of the original LifeHash C++ implementation. The WASM file is included in the package, so you don't need to:

  • ❌ Compile C++ code
  • ❌ Set up Emscripten
  • ❌ Build WASM from source
  • ❌ Configure build tools

Simply install the package and start using it! The library handles:

  • ✅ WASM module loading and initialization
  • ✅ Automatic method attachment after WASM runtime is ready
  • ✅ Memory management
  • ✅ Image generation and conversion to data URIs
  • ✅ Cross-platform path resolution (browser and Node.js)
  • ✅ Security: Hardcoded WASM file paths to prevent path traversal attacks

Technical Details

  • WASM Binary: Pre-compiled from the original bc-lifehash C++ implementation using Emscripten
  • Module Format: ES modules (with CommonJS fallback via index.cjs)
  • TypeScript: Full type definitions included
  • Browser Support: All modern browsers with WebAssembly support
  • Node.js Support: Node.js 18+ (for WASM support)
  • Initialization: The library automatically initializes the WASM module on first use. There is no public init() API; use lifehashjs/lifehash-utils if you want controlled preloading.
  • Emscripten Compatibility: The WASM build exports ccall/cwrap via Emscripten runtime exports, so no global shims are needed.

Optional: Rebuilding the WASM engine (advanced)

This package ships a prebuilt lifehash.wasm + lifehash.js. You only need this section if you want to rebuild those artifacts yourself (e.g., auditability, custom flags, or upgrading the underlying C++ implementation).

Inputs and outputs

  • Input: The bc-lifehash C++ sources (src/*.cpp) and a small post-js hook (wasm/lifehash.post.js) that attaches the friendly JS API (e.g. makeFromUTF8).
  • Outputs (engine artifacts):
    • lifehash.wasm: the compiled WebAssembly binary
    • lifehash.js: the Emscripten JS glue (ES module)

These two files are what the runtime uses. The rest of the package (index.js, build/index.cjs, build/modern/index.js, etc.) is just wrapper + packaging.

Build configuration (what matters)

The important Emscripten settings used for this package:

  • ES module output: EXPORT_ES6=1
    • Emits an ES module glue file (works with modern bundlers and browsers).
  • Factory / no globals: MODULARIZE=1
    • The glue exports a factory you await, instead of writing a global Module.
  • Environment targets: ENVIRONMENT='web,node'
    • Same engine artifacts work in both browser and Node.
  • Optimization: -Oz
    • Optimize for size (good for shipping in npm packages). This should not change correctness; runtime errors are almost always “wrong wasm loaded” rather than “optimized too much”.
  • C exports: EXPORTED_FUNCTIONS includes at least:
    • _malloc, _free
    • _lifehash_make_from_utf8, _lifehash_make_from_data, _lifehash_make_from_digest
    • _lifehash_image_free, _lifehash_sha256, _lifehash_data_to_hex, _lifehash_hex_to_data
    • These must be exported because the post-js API uses them via ccall/cwrap.
  • Runtime exports: EXPORTED_RUNTIME_METHODS includes at least:
    • ccall, cwrap, UTF8ToString
    • This avoids having to add any “global shim” in userland JS.
  • Post-js hook: --post-js wasm/lifehash.post.js
    • Attaches the high-level methods (makeFromUTF8, etc.) and provides a Node-safe return shape when DOM APIs aren’t available.

Packaging into lifehashjs

This repo publishes only what’s under build/ (see package.json"files": ["build", ...]).

Why are there “duplicate” artifacts at the repo root?

You’ll see lifehash.wasm and lifehash.js at the repo root and under build/.

  • Repo root (lifehash.wasm, lifehash.js): treated as build inputs during development (e.g. after regenerating the engine).
  • Published copies (build/lifehash.wasm, build/lifehash.js): the actual runtime artifacts that consumers load from npm.

This duplication is intentional to keep the build pipeline simple (copy inputs → publishable build/), but it can be confusing when inspecting the repo. When debugging “wrong WASM loaded” issues, always verify you’re serving/reading the build/ artifacts in installed packages.

After you rebuild the engine artifacts (lifehash.js + lifehash.wasm), you must:

  • Place them at the repo root:
    • lifehash.js
    • lifehash.wasm
  • Run the package build so they get copied into build/:
    • pnpm run build

That produces the publishable artifacts:

  • build/lifehash.js, build/lifehash.wasm (engine)
  • build/index.cjs, build/modern/index.js (wrappers)
  • build/index.d.ts (types)
  • build/lifehash-utils.cjs, build/modern/lifehash-utils.js, build/lifehash-utils.d.ts (controlled generator)

CommonJS build (for older runtimes / require-only consumers)

Even though the WASM engine glue is emitted as an ES module (EXPORT_ES6=1), the package also ships a CommonJS entry so consumers that still use require() (or older build setups that don’t ingest ESM cleanly) can keep working:

  • CJS entry: build/index.cjs
    • Generated from the ESM wrapper source by scripts/build-cjs.js.
    • Exports only the supported public API (generate*, sha256, ELifeHashVersion).
  • How Node resolves it:
    • package.json exports maps "require"build/index.cjs
    • "node"build/index.cjs (so Node ESM import does not accidentally consume the bundler-focused modern entry)
    • and "browser"/"import"build/modern/index.js (bundler-focused ESM entry)

Important: the engine artifacts are still the same (build/lifehash.js + build/lifehash.wasm), and CJS is only for the wrapper entrypoint. This keeps compatibility broad without introducing global shims.

Initialization Process

  1. First Call: When you call any function (e.g., generate()), the library automatically initializes the WASM module if it hasn't been initialized yet.
  2. WASM Loading: The library loads the lifehash.wasm binary file (automatically resolved in browser/bundler or Node.js environments).
  3. Runtime Initialization: After the WASM module loads, the Emscripten runtime initializes and attaches methods like makeFromUTF8, makeFromData, etc. to the module object.
  4. Ready to Use: Once initialization completes, all methods are available and ready to use.

Note: The initialization is asynchronous, but you don't need to worry about it - all public functions (generate, generateFromData, etc.) automatically await initialization before executing.

Troubleshooting

WASM File Not Found

If you encounter errors about the WASM file not being found:

  1. Bundler Configuration: Ensure your bundler (Vite, Webpack, etc.) is configured to copy WASM files. The library uses import.meta.url to locate the WASM file in modern bundlers.

  2. Node.js: In Node.js environments, the library automatically searches for lifehash.wasm in the package directory.

  3. Manual Path: If needed, you can verify the WASM file exists in node_modules/lifehashjs/build/lifehash.wasm (published under build/).

    • You can also resolve it via the explicit subpath export: lifehashjs/lifehash.wasm.

Initialization Errors

If you see errors like "LifeHash module methods not available":

  • Ensure you're awaiting generate()/generateWithAbort() (or other exported functions)
  • Check that WebAssembly is supported in your environment
  • Verify the WASM file is accessible (not blocked by CORS in browsers)

Browser Compatibility

This library requires:

  • WebAssembly support (available in all modern browsers)
  • ES6 modules support (or use the CommonJS version)
  • Canvas API (for image generation in browsers)

Related Projects

License

BSD-2-Clause Plus Patent License

SPDX-License-Identifier: BSD-2-Clause-Patent

Copyright © 2020 Blockchain Commons, LLC

See LICENSE file for full license text.