Skip to content

fits-js

Horsehead Nebula (Barnard 33), rendered in your browser from a 1.6 MB FITS file using the same code shown below. The file comes fromastropy's FITS-images tutorial data (BSD-licensed).

fits-js parses FITS files in TypeScript. It runs in Node, browsers, Bun, and Deno from a single browser-safe core. Opening a file reads only the header blocks, and a small cutout from a 4 GB cube reads only the bytes that cutout covers.

Terminal window
npm install @fits-js/core

Zero runtime dependencies. Node 22 or later. In the browser, any environment with fetch, Blob, and TypedArray works.

import { BytesReader, openFits, readImage } from "@fits-js/core";
const bytes = new Uint8Array(await (await fetch("/horsehead.fits")).arrayBuffer());
const reader = new BytesReader(bytes);
const { hdus } = await openFits(reader);
const primary = hdus[0];
const { data } = await readImage(primary, reader);
const w = primary.header.getNumber("NAXIS1")!;
const h = primary.header.getNumber("NAXIS2")!;
const finite = new Float64Array(data.length);
let n = 0;
for (let i = 0; i < data.length; i++) {
const v = Number(data[i]);
if (Number.isFinite(v)) finite[n++] = v;
}
const sorted = finite.subarray(0, n).sort();
const lo = sorted[Math.floor(n * 0.005)];
const hi = sorted[Math.floor(n * 0.995)];
const scale = 255 / (hi - lo);
const canvas = document.getElementById("horsehead") as HTMLCanvasElement;
canvas.width = h;
canvas.height = w;
const ctx = canvas.getContext("2d")!;
const img = ctx.createImageData(h, w);
for (let cy = 0; cy < w; cy++) {
for (let cx = 0; cx < h; cx++) {
const i1 = w - 1 - cy;
const i2 = h - 1 - cx;
const v = Number(data[i2 * w + i1]);
const norm = Number.isFinite(v) ? Math.max(0, Math.min(255, (v - lo) * scale)) : 0;
const k = (cy * h + cx) * 4;
img.data[k] = img.data[k + 1] = img.data[k + 2] = norm;
img.data[k + 3] = 255;
}
}
ctx.putImageData(img, 0, 0);

The percentile clip drops outlier pixels. The pixel mapping flips Y (FITS stores row 0 at the bottom, the canvas paints top-down) and rotates 90 degrees to match the orientation the Horsehead is usually shown in.

The Quickstart covers NodeFileReader, HttpRangeReader, and a few cutout patterns.