Skip to content
 
 

Repository files navigation

zstd-wasm-codec

Tiny & performant Zstandard codec for WebAssembly. Decoder + level-1 compressor in a single module. Based on zstd-codec-lib by Tadpole Labs.

This library follows the following criteria:

  • Simple API: ZstdCodec class, with synchronous compress and synchronous decompress methods only
  • Compression: Level 1 only, no dict support. We benchmarked this as the right compression level for our workload (JSON data)
  • Decompression: Up to level 9 (to be future-proof)
  • 38kb / 48kb (zipped) for the size/perf-optimized codec
  • Based on the zstd reference implementation
  • Thoroughly tested
  • Zero dependencies

Implementation notes:

  • Given the limitations of wasm memory management and to achieve appropriate code size & performance, memory is allocated to a fixed-size ring buffer, avoiding heap growth entirely. A single WebAssembly instance serves both compression and decompression, time-sharing one 12 MB buffer that is sufficiently sized to handle the maximum memory required by a level-9 frame (4 MB decoder window).
  • For use in browsers, the module is asynchronously compiled & cached at page load.
  • Only the fast (lvl 1) strategy is pulled from upstream — heavier strategies (dfast/greedy/lazy/btopt/btultra*) are excluded via the upstream ZSTD_EXCLUDE_*_BLOCK_COMPRESSOR macros, so --gc-sections + LTO drop them entirely. Higher compression levels are not supported (any level other than 1 throws).
  • NodeJS supports zstd natively, so it's usually preferable to just use that.

Usage

The public API is a single ZstdCodec class — one instance serves both directions, time-sharing one 12 MB buffer. Obtain one via createCodec() (which loads and caches the wasm module), then call it synchronously.

import { createCodec } from 'zstd-wasm-codec'; // Default (Node/browser - automatically inferred)

import { createCodec } // For strict CSP policies (no unsafe-eval for WASM)
from 'zstd-wasm-codec/external'; // .wasm fetched from same-origin

import { createCodec } // If you need the extra perf. (+30%) for +10kb in the browser
from 'zstd-wasm-codec/perf' // or perf/external
                            // non-browser env uses perf. by default

Note: In development mode, the inlined version is served for /external to avoid bundler issues (e.g., in Vite).

// One codec instance round-trips both ways, reusable across calls.
const codec = await createCodec();

// Compression — level 1 only (see caveats below).
const compressed: Uint8Array = codec.compressSync(input);

// Decompression (buffer already in memory).
const decoded: Uint8Array = codec.decompressSync(compressed);

Compression caveats

  • Level 1 only — only the fast strategy is included to keep the binary small. Any level other than 1 throws. If you need maximum ratio you'll need a different library.
  • The codec uses a fixed 12 MB linear memory. Allocation happens up front; the wasm doesn't grow at runtime.
  • The compressor emits level-1 frames (512 KB window); the decoder accepts foreign frames up to level 9 (4 MB window). Frames declaring a larger window — e.g. level-19 output from the zstd CLI — are refused with frameParameter_windowTooLarge.
  • Output frames are spec-compliant — round-tripping through the upstream zstd CLI is verified by CI.

Important Considerations

  • The default export is pre-minified and mangled. All builds tested against the full suite.
  • Legacy ZSTD format is not supported, and the presence of magic bytes is expected.
  • Dictionaries are not supported (in either direction). Frames that reference a dictionary ID fail with ZSTD_error_dictionary_wrong.
  • Consult the reference to interpret error codes, should any occur.
  • Do not use the wasm module standalone (without js).

Setup

The toolchain is defined via Nix in shell.nix. It is recommended to use direnv to load the correct tools during development, but nix-shell can also be used.


### Development Workflow

```bash
# Full build (WASM + TypeScript)
yarn build

# Clean build
yarn clean
yarn build

# Run tests
yarn test                    # All runtimes (Node + browsers + Bun)
yarn test:node               # Node.js only — includes the codec suite
yarn test:codec              # Codec round-trip + cross-decode tests only
yarn test:browsers           # Browser tests only
yarn test:bun                # Bun only

# Run benchmarks
yarn bench:full

License

This package is dual-licensed under Apache-2.0 OR MIT

The underlying zstd implementation is licensed under BSD-3-Clause.

About

Tiny & performant decoder-only implementation of Zstandard

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages