mrkeyoor.com_
Sat 08 Aug 21:00 UTC
npmUtilsupdated 08 Aug 2026

bmp-js

bmp-js is a dependency-free CommonJS encoder and decoder for Windows BMP image data in Node.js. It decodes 1, 4, 8, 16, 24, and 32-bit files into metadata plus a four-byte-per-pixel ABGR buffer, with code paths for RLE4, RLE8, and bitfields. Encoding is much narrower: it accepts an ABGR buffer and writes an uncompressed 24-bit BMP, dropping the alpha byte.

Verdict

A practical compatibility codec when BMP is unavoidable and you can own the ABGR conversion and input limits. It is not a general image library, and its alpha loss, synchronous API, and dormant release line make it a poor default for new upload pipelines.

API stability4/5The public surface has stayed at two CommonJS functions, encode and decode, throughout the long-lived 0.1.0 release. Source, fixtures, and README agree on the metadata object and ABGR buffer contract. That contract is unlikely to change unexpectedly, but a pre-1.0 version carries no semantic-versioning promise and undocumented quirks such as ignored quality are effectively frozen.
Docs3/5The README clearly lists supported decode bit depths, the 24-bit encode limit, the ABGR byte order, returned header properties, and basic read and write examples. It omits input-safety guidance, RLE and bitfield limitations, alpha-zero behavior for 24-bit input, synchronous performance, TypeScript and ESM use, and the write example misspells Node's writeFileSync method.
Maintenance2/5npm 0.1.0 was published in April 2018, and GitHub reports the last repository push in November 2023 with 20 open issues and pull requests. The project is not archived and uses a clear MIT license, but years without a package release leave Buffer modernization, declarations, malformed-file handling, open reports, and packaging improvements unavailable to registry users.
Ecosystem3/5The package recorded 4,508,645 downloads in the measured week and its dependency-free Node API makes it easy for other file tools to include transitively. BMP is a narrow format, though, and bmp-js offers no plugins, streams, browser build, typed adapters, transforms, or bridges to the RGBA conventions used by Canvas and most JavaScript imaging libraries.

Use it if

  • You need to inspect or transform BMP files in Node without a native image dependency
  • You must decode old paletted, RLE4, RLE8, 16-bit, 24-bit, or 32-bit BMP input
  • You only need to emit basic uncompressed 24-bit BMP files
  • You can adapt the package's ABGR pixel order to the rest of your image pipeline
Skip it if

Setup reality

npm install bmp-js adds no runtime dependencies, native compilation, peer packages, credentials, or config. In Node CommonJS, require('bmp-js') exposes `decode(buffer)` and `encode({ data, width, height })`. Both are synchronous and operate on Node Buffers, so reading a large file and decoding it blocks the event loop; use a worker thread or an offline job when latency matters. The largest integration trap is channel order. Decoded and encoder input pixels use ABGR, four bytes per pixel, not RGBA. For a 24-bit source the decoder writes alpha as zero, not 255, while some 32-bit variants carry an alpha byte. Convert explicitly before handing pixels to Canvas, PNG encoders, or browser APIs. Encoding always produces top-down, uncompressed 24-bit BMP data and ignores the input alpha byte. The documented `quality` argument in source defaults to 100 but does not influence output. The README's sample also contains a capitalization typo: Node's method is `fs.writeFileSync`, not `fs.WriteFileSync`. There is no streaming API and no validation layer for untrusted headers. The decoder reads dimensions from the file and allocates width times height times four bytes, so check the BMP signature, minimum header length, dimensions, pixel count, and upload byte limit before calling it. The release remains 0.1.0 from 2018, with no bundled TypeScript types or ESM entry, so add your own declaration or wrapper and lock representative fixture tests if this sits in a production conversion path.

Patterns

Decode a BMP filedecode-file

const fs = require('node:fs');
const bmp = require('bmp-js');

const input = fs.readFileSync('./input.bmp');
const image = bmp.decode(input);
console.log(image.width, image.height, image.bitPP);

decode is synchronous and returns ABGR pixel bytes in image.data.

Read one decoded pixelread-pixel

function getPixel(image, x, y) {
  if (x < 0 || y < 0 || x >= image.width || y >= image.height) throw new RangeError('pixel');
  const offset = (y * image.width + x) * 4;
  const [a, b, g, r] = image.data.subarray(offset, offset + 4);
  return { r, g, b, a };
}

console.log(getPixel(image, 10, 4));

The source buffer order is ABGR. For 24-bit BMP input, the decoder sets alpha to 0.

Convert decoded ABGR bytes to RGBAconvert-to-rgba

function toRgba(image) {
  const rgba = Buffer.alloc(image.data.length);
  for (let i = 0; i < image.data.length; i += 4) {
    rgba[i] = image.data[i + 3];
    rgba[i + 1] = image.data[i + 2];
    rgba[i + 2] = image.data[i + 1];
    rgba[i + 3] = image.bitPP === 24 ? 255 : image.data[i];
  }
  return rgba;
}

Promoting 24-bit alpha to 255 is a policy choice needed because the decoder stores zero there.

Encode ABGR pixels as a BMP fileencode-file

const fs = require('node:fs');
const bmp = require('bmp-js');

const encoded = bmp.encode({ data: abgrPixels, width, height });
fs.writeFileSync('./output.bmp', encoded.data);

The encoder writes uncompressed 24-bit BMP and ignores every input alpha byte. Node uses writeFileSync with a lowercase w.

Create a solid-color BMPcreate-solid-image

const bmp = require('bmp-js');

const width = 64;
const height = 64;
const data = Buffer.alloc(width * height * 4);
for (let i = 0; i < data.length; i += 4) {
  data[i] = 255;    // A, ignored while encoding
  data[i + 1] = 40; // B
  data[i + 2] = 90; // G
  data[i + 3] = 220;// R
}
const output = bmp.encode({ data, width, height }).data;

Pixel input is ABGR even though the output file stores only BGR channels.

Edit a pixel in placeedit-pixel

function setPixel(image, x, y, { r, g, b, a = 255 }) {
  const i = (y * image.width + x) * 4;
  image.data[i] = a;
  image.data[i + 1] = b;
  image.data[i + 2] = g;
  image.data[i + 3] = r;
}

setPixel(image, 0, 0, { r: 255, g: 0, b: 0 });

Bounds and channel validation are caller responsibilities; encoding the result discards alpha.

Normalize any supported BMP to 24-bit outputround-trip-bmp

const bmp = require('bmp-js');

const decoded = bmp.decode(sourceBuffer);
const normalized = bmp.encode({
  data: decoded.data,
  width: decoded.width,
  height: decoded.height,
}).data;

This is lossy for palettes, compression, bit depth, header metadata, and alpha because encode always emits uncompressed 24-bit data.

Reject oversized dimensions before decodingguard-image-size

function decodeWithLimit(buffer, maxPixels = 20_000_000) {
  if (buffer.length < 26 || buffer.toString('ascii', 0, 2) !== 'BM') throw new Error('invalid BMP header');
  const width = buffer.readUInt32LE(18);
  const signedHeight = buffer.readInt32LE(22);
  const height = Math.abs(signedHeight);
  if (!width || !height || width > Math.floor(maxPixels / height)) throw new Error('BMP too large');
  return require('bmp-js').decode(buffer);
}

The decoder allocates from header dimensions and provides no built-in memory or pixel cap; also enforce an upload byte limit.

Inspect decoded BMP header fieldsinspect-format

const image = bmp.decode(buffer);
console.log({
  width: image.width,
  height: image.height,
  bitsPerPixel: image.bitPP,
  compression: image.compress,
  paletteEntries: image.palette?.length ?? 0,
});

Header metadata reflects the input. Re-encoding does not preserve its bit depth, compression, or palette.

Create a BMP data URLmake-data-url

const encoded = bmp.encode({ data: abgrPixels, width, height });
const dataUrl = `data:image/bmp;base64,${encoded.data.toString('base64')}`;

Base64 adds payload overhead and the encoder output has no alpha; use object storage or a binary response for large images.

Alternatives

PackageRegistryPick it when
sharpnpmYou need fast production image conversion, resizing, metadata, and many common formats and accept native binaries
jimpnpmYou want pure JavaScript image manipulation with a higher-level bitmap API
pngjsnpmYour actual interchange format is PNG and you want a focused pure-JavaScript codec
utifnpmYou need a pure-JavaScript decoder for TIFF rather than BMP input