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

bmp-encode

v1.0.0

Published

Encode RGBA pixels to a BMP file, with alpha that survives. Zero dependencies, browser and Node.

Readme

bmp-encode

Encode RGBA pixels to a BMP file — with an alpha channel that actually survives. No dependencies. Works in the browser and in Node.

npm install bmp-encode

Why this exists

Browsers can read BMP but they cannot write it: canvas.toBlob() has no "image/bmp". So you write the bytes yourself, which looks like forty lines of DataView calls.

The catch is the header. The obvious choice, a BITMAPINFOHEADER at 32 bits per pixel, does store your alpha byte — and decoders throw it away. The BMP specification calls that byte reserved, so Chrome, Firefox and most image libraries ignore it. Your transparent pixels come back opaque and nothing tells you why.

What makes alpha survive is a BITMAPV4HEADER with BI_BITFIELDS compression and an explicit alpha mask. That is the whole difference between this library and the snippet you were about to write.

There is a reproducible check in test/browser.html: it encodes four pixels with alpha 255, 128, 0 and 200, hands the bytes to the browser as an image, and reads the decoded pixels back. All four alpha values come back unchanged.

Usage

From a canvas

import { encodeBmpBlob } from "bmp-encode";

const blob = encodeBmpBlob(canvas);

const a = document.createElement("a");
a.href = URL.createObjectURL(blob);
a.download = "image.bmp";
a.click();

From ImageData

import { encodeBmp } from "bmp-encode";

const bytes = encodeBmp(ctx.getImageData(0, 0, canvas.width, canvas.height));
// -> Uint8Array containing a complete .bmp file

In Node

The core takes anything shaped like ImageData, so it does not need a DOM:

import { writeFileSync } from "node:fs";
import { encodeBmp } from "bmp-encode";

writeFileSync(
  "out.bmp",
  encodeBmp({ width, height, data /* Uint8ClampedArray | Uint8Array, RGBA */ })
);

API

encodeBmp(source, options?) => Uint8Array

source is an ImageData, any object with { width, height, data }, or a canvas (HTMLCanvasElement or OffscreenCanvas) to read pixels from.

Returns the bytes of a complete BMP file.

encodeBmpBlob(source, options?) => Blob

The same bytes wrapped in a Blob with type image/bmp. Browser only.

Options

| Option | Default | Meaning | | --- | --- | --- | | pixelsPerMetre | 2835 (~72 dpi) | Resolution written into the header. Affects printing software only; the pixels are unchanged. Use 11811 for 300 dpi. |

Invalid input throws rather than producing a corrupt file: non-positive dimensions, or a data length that does not match width * height * 4.

What it writes

  • BITMAPV4HEADER (108 bytes), 32 bits per pixel
  • BI_BITFIELDS with masks 0x00FF0000 / 0x0000FF00 / 0x000000FF / 0xFF000000
  • Top-down rows, via a negative height — no row padding is needed at 32bpp
  • Pixels as B, G, R, A

Output is roughly width * height * 4 + 122 bytes. BMP does not compress, so a 1920×1080 image is about 8 MB whatever it contains. If the file only needs to be lossless, PNG is smaller by a wide margin; reach for BMP when something on the other end insists on it — label printers, till systems, embroidery machines, industrial controllers and older Windows software all tend to.

Prior art

canvas-to-bmp covers the same ground. It is an untyped UMD bundle, has not been updated in years, and writes a plain BITMAPINFOHEADER, so alpha does not survive. This package exists because that was not good enough for a tool that had to keep transparency.

Tests

npm test          # unit tests, node:test
npm run build
open test/browser.html   # the alpha round-trip, in a real decoder

Licence

MIT. Extracted from the BMP encoder behind edit-tool.com, a set of free image tools that run entirely in your browser.