BH3D Logo

Zstdify: A Pure TypeScrpt Zstd Re-Implementation, Written in 4 Hours

Ben Houston5 Minutes ReadFebruary 20, 2026

Tags: algorithms, coding, ai

Today, in just four hours, I built zstdify using OpenAI Codex 5.3 in Cursor: a pure TypeScript implementation of Zstandard compression/decompression that works in Node.js and browsers, with no native addons and no WASM.

Why? Because I needed this for a project I am working on and I couldn't find anything that already existed that could do this.

zstdify logo

It is available on npm now as zstdify and zstdify-cli.

If you only want the 30-second version, here it is:

import { compress, decompress } from 'zstdify';

const bytes = new TextEncoder().encode('hello zstd');
const packed = compress(bytes);
const restored = decompress(packed);

And from the command line:

zstdify compress input.txt output.zst
zstdify extract output.zst restored.txt

This post is about three things:

  1. Why I needed this now.
  2. How much of it is actually robust vs demo-ware.
  3. What it says about what AI coding tools can now do in a single day.

Why I Built It

I am working on KTX2 texture tooling for Land of Assets. KTX2 supports Zstandard as a supercompression scheme, so if you want browser-side decode or Node-side processing, you need zstd available in JavaScript.

Historically, this has been awkward:

  • Native bindings are fast but not portable.
  • WASM is portable but adds integration and bundling complexity, and can not be tree shaken.
  • Browser support for built-in zstd is effectively nonexistent.

I wanted a portable, dependency-light implementation I could trust, and I did not want to bury it inside a KTX2-specific codebase. So I split it out as a standalone library.

What Exists Today

For compression and decompression features, zstdify has reached parity for the methods it needs to support in real workflows, and it is extensively validated against the official zstd CLI.

What is implemented:

  • Decoder: raw/RLE/compressed blocks, Huffman/FSE paths, concatenated + skippable frames, checksum validation, dictionary-aware decompression.
  • Encoder: raw/RLE/compressed blocks, level-driven strategy, raw fallback when compression is worse, optional checksum, dictionary-aware headers.
  • Dictionary training: pure TypeScript training from samples with fastcover/cover/legacy style controls.
  • Interop testing: zstdify -> zstd and zstd -> zstdify, including dictionary workflows.
  • Packaging: library + CLI, both published on npm.

What is still left:

  • Formal performance benchmarking vs native/WASM alternatives.
  • Streaming/incremental APIs for large payload workflows.
  • Worker-thread parallelism for higher throughput.

These are optimization and ergonomics gaps, not format-coverage gaps. My personal estimation is that I could add these features in just a few more hours, but I don't need them right now so I haven't added them yet.

Why This Would Have Been Hard Before

I have tried projects at similar complexity with earlier AI models, and they usually failed at the same point: debugging. They could scaffold code, but when the failures became subtle, they got stuck.

I saw that contrast clearly today. In some chats I tried Cursor Composer 1.5 and it could not reliably debug the harder issues, while OpenAI Codex 5.3 was able to identify root causes quickly and move forward. It feels like coding AI took another meaningful step up in just the last few months, and I do not think this project would have been possible for me at this speed before that jump.

My rough workflow:

  1. Reference-first planning: analyze the upstream facebook/zstd project and produce a staged implementation plan.
  2. Template reuse: bootstrap from my existing hdrify monorepo pattern (tests, CLI structure, release flow).
  3. Iterative build/debug loop: generate code, run tests, inspect failures, tighten constraints, repeat.
  4. Hard guardrails on quality: no skipped tests, no "expected failure" escape hatches unless the official CLI behaves the same way.

This was done across 17 separate chats in Cursor over the day. I would start a new chat after a major phase, or when switching into a modular task (for example: fixing one failing test cluster, adding dictionary support, or improving coverage in a targeted area).

I also explicitly asked for tests inspired by the official open-source zstd project's testing approach, with a strong emphasis on interoperability between the official zstd CLI and zstdify. That produced a large corpus-driven test set, fuzz/property-style tests, and an extensive interop suite in both directions. Those tests caught several real edge-case bugs during development.

Dictionary Support Is the Real Opportunity

A side benefit of this project is the simple dictionary creation support this project now enables. Dictionaries can boost compression easily for small repetitive workloads and now you can access it with just a few lines of code. For example, it is now just this simple:

import { compress, decompress, generateDictionary } from 'zstdify';

const encoder = new TextEncoder();
const samples = [
  encoder.encode('header vertex texture normal index'),
  encoder.encode('offset match literal sequence table'),
];
const dict = generateDictionary(samples, { maxDictSize: 2048, algorithm: 'fastcover' });

const payload = encoder.encode('header vertex texture offset match literal');
const compressed = compress(payload, { dictionary: { bytes: dict, id: 42 } });
const restored = decompress(compressed, { dictionary: { bytes: dict, id: 42 } });

Try It

pnpm add zstdify
pnpm add -g zstdify-cli

If you test this on your own corpus (especially with and without dictionaries), I would love feedback and failing cases.

Closing Thought

I was not completely sure I could get this done in less than a day. It worked, and it seems robust.

What impressed me most was OpenAI Codex 5.3's ability to debug complex edge cases instead of only generating happy-path scaffolding. This changes my ambitions with AI. More is now possible.