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

web-font-codecs

v0.3.2

Published

Typed browser and Node conversion between SFNT, WOFF1 and WOFF2.

Readme

web-font-codecs

Convert fonts between TTF/OTF, WOFF and WOFF2 in Node or the browser.

  • Every direction: TTF/OTF to WOFF or WOFF2, back again, and WOFF to WOFF2.
  • Uses Mozilla's original WOFF codec and Google's WOFF2 codec, compiled to WebAssembly.
  • Initializes enabled codecs concurrently (both by default); conversions are synchronous.
  • Keeps outlines as they are. The result tells you whether to save it as .ttf or .otf.
  • Runs locally. No uploads, no CDN.
npm install web-font-codecs

Node

import { readFile, writeFile } from 'node:fs/promises';
import { createFontConverter } from 'web-font-codecs';

using converter = await createFontConverter();
const result = converter.convert(await readFile('font.ttf'), { to: 'woff2' });
await writeFile(`font.${result.extension}`, result.data);

Browser

The same code works in the browser. Vite and Astro bundle the .wasm files automatically. With other bundlers, serve woff1-codec/codec.wasm and woff2-codec/codec.wasm yourself and pass their URLs:

import { createFontConverter } from 'web-font-codecs';

using converter = await createFontConverter({
  woff1: { wasm: '/assets/woff1.wasm' },
  woff2: { wasm: '/assets/woff2.wasm' },
});
const response = await fetch('/font.woff2');
const result = converter.convert(new Uint8Array(await response.arrayBuffer()), { to: 'sfnt' });

wasm also accepts a URL, Response, bytes, compiled WebAssembly.Module, ReadableStream<Uint8Array>, or a promise of any of these.

Conversion blocks the thread it runs on and cannot be interrupted. For large fonts or Zopfli, run it in a Worker and call worker.terminate() to cancel.

Options

Use await createFontConverter({ codecs: ['woff2'] }) to load only WOFF2. Conversions requiring a disabled codec throw UNSUPPORTED_FORMAT; same-format copies need no codec.

converter.convert(input, { to: 'sfnt' });
converter.convert(input, { to: 'woff1', encode: { compression: 'zopfli', iterations: 15 } });
converter.convert(input, { to: 'woff2', encode: { quality: 11, allowTransforms: true } });

WOFF (to: 'woff1'):

| Option | Default | Values | | --- | --- | --- | | compression | 'zlib' | 'zlib' or 'zopfli' (slower, smaller) | | iterations | 15 | 1 to 100. Only with 'zopfli'. | | majorVersion, minorVersion | 1, 0 | 0 to 65535 | | metadata | none | XML as UTF-8 Uint8Array. Not validated. | | privateData | none | Any Uint8Array |

WOFF2 (to: 'woff2'):

| Option | Default | Values | | --- | --- | --- | | quality | 11 | Brotli quality, 0 to 11 | | allowTransforms | true | Google's glyph table transforms | | metadata | none | XML as UTF-8 Uint8Array. Not validated. |

Converting to the same format without encode returns a copy of the input. Pass encode (even {}) to re-encode it.

Result

| Field | Value | | --- | --- | | data | The converted font, as a Uint8Array you own | | extension | 'ttf', 'otf', 'woff' or 'woff2'. CFF fonts decode to 'otf'. | | from, to | 'sfnt', 'woff1' or 'woff2' | | warnings | Repair names, e.g. ['checksumMismatch']; [] when none were reported. | | discardedAuxiliaryData | true if the input's WOFF metadata or private data was dropped |

WOFF metadata and private data do not carry over when a font is decoded or re-encoded. To keep WOFF blocks, read them with readAuxiliaryData from woff1-codec and pass them back through encode. WOFF2 offers no way to read them.

detectFormat(bytes) returns 'sfnt', 'woff1' or 'woff2' from the file signature.

Limits

  • Single fonts only. TTC/OTC and WOFF2 collections are rejected.
  • Input and output: 512 MiB each by default and at most. Lower them per codec with maxInputBytes and maxOutputBytes under woff1 and woff2. Set both, since a conversion may use either codec.
  • Each codec has up to 2 GiB of WASM memory, which a large font can exhaust before reaching the output limit.
  • Round trips are not byte-identical. Mozilla may repair checksums and alignment; Google normalizes glyph storage and may drop DSIG.
  • These are codecs, not font validators or sanitizers.

Errors and cleanup

Failures throw FontCodecError with a code: INVALID_INPUT, UNSUPPORTED_FORMAT, LIMIT_EXCEEDED, INVALID_OPTION, CODEC_FAILURE, WASM_INIT or DISPOSED. Native failures also set nativeStatus.

import { FontCodecError } from 'web-font-codecs';

try {
  converter.convert(input, { to: 'woff2' });
} catch (error) {
  if (error instanceof FontCodecError && error.code === 'UNSUPPORTED_FORMAT') console.error(error.message);
  else throw error;
}

using (via Symbol.dispose) or dispose() releases both codecs; later calls throw DISPOSED. Results stay valid after disposal. If the WASM fails to load, createFontConverter() rejects; retry initialization. If the WASM itself crashes (CODEC_FAILURE), create a new converter.

License

MIT. The WOFF codec it depends on is MPL 1.1 and ships its own source. See LICENSING.md and THIRD-PARTY-NOTICES.md.